diff options
Diffstat (limited to 'drivers/ata')
109 files changed, 12047 insertions, 2690 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 6bdedd7cca2..2664da32d9d 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -11,13 +11,13 @@ config HAVE_PATA_PLATFORM to update the PATA_PLATFORM entry. menuconfig ATA - tristate "Serial ATA and Parallel ATA drivers" + tristate "Serial ATA and Parallel ATA drivers (libata)" depends on HAS_IOMEM depends on BLOCK - depends on !(M32R || M68K) || BROKEN + depends on !(M32R || M68K || S390) || BROKEN select SCSI ---help--- - If you want to use a ATA hard disk, ATA tape drive, ATA CD-ROM or + If you want to use an ATA hard disk, ATA tape drive, ATA CD-ROM or any other ATA device under Linux, say Y and make sure that you know the name of your ATA host adapter (the card inside your computer that "speaks" the ATA protocol, also called ATA controller), @@ -58,6 +58,20 @@ config ATA_ACPI You can disable this at kernel boot time by using the option libata.noacpi=1 +config SATA_ZPODD + bool "SATA Zero Power Optical Disc Drive (ZPODD) support" + depends on ATA_ACPI && PM_RUNTIME + default n + help + This option adds support for SATA Zero Power Optical Disc + Drive (ZPODD). It requires both the ODD and the platform + support, and if enabled, will automatically power on/off the + ODD when certain condition is satisfied. This does not impact + end user's experience of the ODD, only power is saved when + the ODD is not in use (i.e. no disc inside). + + If unsure, say N. + config SATA_PMP bool "SATA Port Multiplier support" default y @@ -83,6 +97,56 @@ config SATA_AHCI_PLATFORM If unsure, say N. +config AHCI_DA850 + tristate "DaVinci DA850 AHCI SATA support" + depends on ARCH_DAVINCI_DA850 + help + This option enables support for the DaVinci DA850 SoC's + onboard AHCI SATA. + + If unsure, say N. + +config AHCI_ST + tristate "ST AHCI SATA support" + depends on ARCH_STI + help + This option enables support for ST AHCI SATA controller. + + If unsure, say N. + +config AHCI_IMX + tristate "Freescale i.MX AHCI SATA support" + depends on MFD_SYSCON && (ARCH_MXC || COMPILE_TEST) + help + This option enables support for the Freescale i.MX SoC's + onboard AHCI SATA. + + If unsure, say N. + +config AHCI_MVEBU + tristate "Marvell EBU AHCI SATA support" + depends on ARCH_MVEBU + help + This option enables support for the Marvebu EBU SoC's + onboard AHCI SATA. + + If unsure, say N. + +config AHCI_SUNXI + tristate "Allwinner sunxi AHCI SATA support" + depends on ARCH_SUNXI + help + This option enables support for the Allwinner sunxi SoC's + onboard AHCI SATA. + + If unsure, say N. + +config AHCI_XGENE + tristate "APM X-Gene 6.0Gbps AHCI SATA host controller support" + depends on PHY_XGENE + help + This option enables support for APM X-Gene SoC SATA host controller. + config SATA_FSL tristate "Freescale 3.0Gbps SATA support" depends on FSL_SOC @@ -93,7 +157,7 @@ config SATA_FSL If unsure, say N. config SATA_INIC162X - tristate "Initio 162x SATA support" + tristate "Initio 162x SATA support (Very Experimental)" depends on PCI help This option enables support for Initio 162x Serial ATA. @@ -115,7 +179,7 @@ config SATA_SIL24 If unsure, say N. config ATA_SFF - bool "ATA SFF support" + bool "ATA SFF support (for legacy IDE and PATA)" default y help This option adds support for ATA controllers with SFF @@ -146,7 +210,7 @@ config PDC_ADMA config PATA_OCTEON_CF tristate "OCTEON Boot Bus Compact Flash support" - depends on CPU_CAVIUM_OCTEON + depends on CAVIUM_OCTEON_SOC help This option enables a polled compact flash driver for use with compact flash cards attached to the OCTEON boot bus. @@ -163,7 +227,7 @@ config SATA_QSTOR config SATA_SX4 tristate "Promise SATA SX4 support (Experimental)" - depends on PCI && EXPERIMENTAL + depends on PCI help This option enables support for Promise Serial ATA SX4. @@ -214,8 +278,29 @@ config SATA_DWC_VDEBUG help This option enables the taskfile dumping and NCQ debugging. +config SATA_DWC_PMP + tristate "DesignWare Cores SATA with PMP support" + depends on 460EX + help + This option enables support for the on-chip SATA controller of the + AppliedMicro processor 460EX with PMP support. + + If unsure, say N. + +config SATA_HIGHBANK + tristate "Calxeda Highbank SATA support" + depends on ARCH_HIGHBANK || COMPILE_TEST + help + This option enables support for the Calxeda Highbank SoC's + onboard SATA. + + If unsure, say N. + config SATA_MV tristate "Marvell SATA support" + depends on PCI || ARCH_DOVE || ARCH_KIRKWOOD || ARCH_MV78XX0 || \ + ARCH_MVEBU || ARCH_ORION5X || COMPILE_TEST + select GENERIC_PHY help This option enables support for the Marvell Serial ATA family. Currently supports 88SX[56]0[48][01] PCI(-X) chips, @@ -239,6 +324,14 @@ config SATA_PROMISE If unsure, say N. +config SATA_RCAR + tristate "Renesas R-Car SATA support" + depends on ARCH_SHMOBILE || COMPILE_TEST + help + This option enables support for Renesas R-Car Serial ATA. + + If unsure, say N. + config SATA_SIL tristate "Silicon Image SATA support" depends on PCI @@ -313,6 +406,7 @@ config PATA_AMD config PATA_ARASAN_CF tristate "ARASAN CompactFlash PATA Controller Support" + depends on ARCH_SPEAR13XX || COMPILE_TEST depends on DMADEVICES select DMA_ENGINE help @@ -364,7 +458,7 @@ config PATA_CMD64X config PATA_CS5520 tristate "CS5510/5520 PATA support" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) help This option enables support for the Cyrix 5510/5520 companion chip used with the MediaGX/Geode processor family. @@ -373,7 +467,7 @@ config PATA_CS5520 config PATA_CS5530 tristate "CS5530 PATA support" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) help This option enables support for the Cyrix/NatSemi/AMD CS5530 companion chip used with the MediaGX/Geode processor family. @@ -382,7 +476,7 @@ config PATA_CS5530 config PATA_CS5535 tristate "CS5535 PATA support (Experimental)" - depends on PCI && X86 && !X86_64 && EXPERIMENTAL + depends on PCI && X86_32 help This option enables support for the NatSemi/AMD CS5535 companion chip used with the Geode processor family. @@ -391,7 +485,7 @@ config PATA_CS5535 config PATA_CS5536 tristate "CS5536 PATA support" - depends on PCI + depends on PCI && (X86_32 || MIPS || COMPILE_TEST) help This option enables support for the AMD CS5536 companion chip used with the Geode LX processor family. @@ -400,7 +494,7 @@ config PATA_CS5536 config PATA_CYPRESS tristate "Cypress CY82C693 PATA support (Very Experimental)" - depends on PCI && EXPERIMENTAL + depends on PCI help This option enables support for the Cypress/Contaq CY82C693 chipset found in some Alpha systems @@ -416,6 +510,15 @@ config PATA_EFAR If unsure, say N. +config PATA_EP93XX + tristate "Cirrus Logic EP93xx PATA support" + depends on ARCH_EP93XX + help + This option enables support for the PATA controller in + the Cirrus Logic EP9312 and EP9315 ARM CPU. + + If unsure, say N. + config PATA_HPT366 tristate "HPT 366/368 PATA support" depends on PCI @@ -479,7 +582,7 @@ config PATA_IMX config PATA_IT8213 tristate "IT8213 PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + depends on PCI help This option enables support for the ITE 821 PATA controllers via the new ATA layer. @@ -572,7 +675,7 @@ config PATA_OLDPIIX config PATA_OPTIDMA tristate "OPTI FireStar PATA support (Very Experimental)" - depends on PCI && EXPERIMENTAL + depends on PCI help This option enables DMA/PIO support for the later OPTi controllers found on some old motherboards and in some @@ -599,7 +702,7 @@ config PATA_PDC_OLD config PATA_RADISYS tristate "RADISYS 82600 PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + depends on PCI help This option enables support for the RADISYS 82600 PATA controllers via the new ATA layer @@ -618,7 +721,7 @@ config PATA_RDC config PATA_SC1200 tristate "SC1200 PATA support" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) help This option enables support for the NatSemi/AMD SC1200 SoC companion chip used with the Geode processor family. @@ -670,7 +773,7 @@ config PATA_SIS config PATA_TOSHIBA tristate "Toshiba Piccolo support (Experimental)" - depends on PCI && EXPERIMENTAL + depends on PCI help Support for the Toshiba Piccolo controllers. Currently only the primary channel is supported by this driver. @@ -721,7 +824,7 @@ comment "PIO-only SFF controllers" config PATA_AT32 tristate "Atmel AVR32 PATA support (Experimental)" - depends on AVR32 && PLATFORM_AT32AP && EXPERIMENTAL + depends on AVR32 && PLATFORM_AT32AP help This option enables support for the IDE devices on the Atmel AT32AP platform. @@ -730,7 +833,7 @@ config PATA_AT32 config PATA_AT91 tristate "PATA support for AT91SAM9260" - depends on ARM && ARCH_AT91 + depends on ARM && SOC_AT91SAM9 help This option enables support for IDE devices on the Atmel AT91SAM9260 SoC. @@ -738,7 +841,7 @@ config PATA_AT91 config PATA_CMD640_PCI tristate "CMD640 PCI PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + depends on PCI help This option enables support for the CMD640 PCI IDE interface chip. Only the primary channel is currently @@ -784,7 +887,7 @@ config PATA_NS87410 config PATA_OPTI tristate "OPTI621/6215 PATA support (Very Experimental)" - depends on PCI && EXPERIMENTAL + depends on PCI help This option enables full PIO support for the early Opti ATA controllers found on some old motherboards. @@ -864,7 +967,7 @@ config PATA_SAMSUNG_CF config PATA_WINBOND_VLB tristate "Winbond W83759A VLB PATA support (Experimental)" - depends on ISA && EXPERIMENTAL + depends on ISA select PATA_LEGACY help Support for the Winbond W83759A controller on Vesa Local Bus @@ -892,7 +995,7 @@ config ATA_GENERIC config PATA_LEGACY tristate "Legacy ISA PATA support (Experimental)" - depends on (ISA || PCI) && EXPERIMENTAL + depends on (ISA || PCI) help This option enables support for ISA/VLB/PCI bus legacy PATA ports and allows them to be accessed via the new ATA layer. diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 6ece5b7231a..7e7a77de757 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -4,11 +4,19 @@ obj-$(CONFIG_ATA) += libata.o # non-SFF interface obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o -obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o +obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o obj-$(CONFIG_SATA_FSL) += sata_fsl.o obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o +obj-$(CONFIG_SATA_DWC_PMP) += sata_dwc_pmp.o +obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +obj-$(CONFIG_AHCI_DA850) += ahci_da850.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_ST) += ahci_st.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o libahci.o libahci_platform.o # SFF w/ custom DMA obj-$(CONFIG_PDC_ADMA) += pdc_adma.o @@ -22,6 +30,7 @@ obj-$(CONFIG_ATA_PIIX) += ata_piix.o obj-$(CONFIG_SATA_MV) += sata_mv.o obj-$(CONFIG_SATA_NV) += sata_nv.o obj-$(CONFIG_SATA_PROMISE) += sata_promise.o +obj-$(CONFIG_SATA_RCAR) += sata_rcar.o obj-$(CONFIG_SATA_SIL) += sata_sil.o obj-$(CONFIG_SATA_SIS) += sata_sis.o obj-$(CONFIG_SATA_SVW) += sata_svw.o @@ -43,6 +52,7 @@ obj-$(CONFIG_PATA_CS5535) += pata_cs5535.o obj-$(CONFIG_PATA_CS5536) += pata_cs5536.o obj-$(CONFIG_PATA_CYPRESS) += pata_cypress.o obj-$(CONFIG_PATA_EFAR) += pata_efar.o +obj-$(CONFIG_PATA_EP93XX) += pata_ep93xx.o obj-$(CONFIG_PATA_HPT366) += pata_hpt366.o obj-$(CONFIG_PATA_HPT37X) += pata_hpt37x.o obj-$(CONFIG_PATA_HPT3X2N) += pata_hpt3x2n.o @@ -105,3 +115,4 @@ libata-y := libata-core.o libata-scsi.o libata-eh.o libata-transport.o libata-$(CONFIG_ATA_SFF) += libata-sff.o libata-$(CONFIG_SATA_PMP) += libata-pmp.o libata-$(CONFIG_ATA_ACPI) += libata-acpi.o +libata-$(CONFIG_SATA_ZPODD) += libata-zpodd.o diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c index 3bc8c79bf2c..0cd7c7a39e5 100644 --- a/drivers/ata/acard-ahci.c +++ b/drivers/ata/acard-ahci.c @@ -2,7 +2,7 @@ /* * acard-ahci.c - ACard AHCI SATA support * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -36,7 +36,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -78,7 +77,7 @@ static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc); static int acard_ahci_port_start(struct ata_port *ap); static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int acard_ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int acard_ahci_pci_device_resume(struct pci_dev *pdev); #endif @@ -119,16 +118,16 @@ static struct pci_driver acard_ahci_pci_driver = { .id_table = acard_ahci_pci_tbl, .probe = acard_ahci_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = acard_ahci_pci_device_suspend, .resume = acard_ahci_pci_device_resume, #endif }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int acard_ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); struct ahci_host_priv *hpriv = host->private_data; void __iomem *mmio = hpriv->mmio; u32 ctl; @@ -156,7 +155,7 @@ static int acard_ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg static int acard_ahci_pci_device_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -503,21 +502,10 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id &acard_ahci_sht); } -static int __init acard_ahci_init(void) -{ - return pci_register_driver(&acard_ahci_pci_driver); -} - -static void __exit acard_ahci_exit(void) -{ - pci_unregister_driver(&acard_ahci_pci_driver); -} +module_pci_driver(acard_ahci_pci_driver); MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("ACard AHCI SATA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, acard_ahci_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(acard_ahci_init); -module_exit(acard_ahci_exit); diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index d07bf0366d9..4cd52a4541a 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1,7 +1,7 @@ /* * ahci.c - AHCI SATA support * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -35,7 +35,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -53,6 +52,7 @@ enum { AHCI_PCI_BAR_STA2X11 = 0, + AHCI_PCI_BAR_ENMOTUS = 2, AHCI_PCI_BAR_STANDARD = 5, }; @@ -60,6 +60,7 @@ enum board_ids { /* board IDs by feature in alphabetical order */ board_ahci, board_ahci_ign_iferr, + board_ahci_noncq, board_ahci_nosntf, board_ahci_yes_fbs, @@ -82,6 +83,8 @@ enum board_ids { static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); +static void ahci_mcp89_apple_enable(struct pci_dev *pdev); +static bool is_mcp89_apple(struct pci_dev *pdev); static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); #ifdef CONFIG_PM @@ -103,35 +106,36 @@ static struct ata_port_operations ahci_p5wdh_ops = { .hardreset = ahci_p5wdh_hardreset, }; -#define AHCI_HFLAGS(flags) .private_data = (void *)(flags) - static const struct ata_port_info ahci_port_info[] = { /* by features */ - [board_ahci] = - { + [board_ahci] = { .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_ign_iferr] = - { + [board_ahci_ign_iferr] = { AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_nosntf] = - { + [board_ahci_noncq] = { + AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ), + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }, + [board_ahci_nosntf] = { AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_yes_fbs] = - { + [board_ahci_yes_fbs] = { AHCI_HFLAGS (AHCI_HFLAG_YES_FBS), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, @@ -139,8 +143,7 @@ static const struct ata_port_info ahci_port_info[] = { .port_ops = &ahci_ops, }, /* by chipsets */ - [board_ahci_mcp65] = - { + [board_ahci_mcp65] = { AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM, @@ -148,24 +151,21 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_mcp77] = - { + [board_ahci_mcp77] = { AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_mcp89] = - { + [board_ahci_mcp89] = { AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_mv] = - { + [board_ahci_mv] = { AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP), .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, @@ -173,8 +173,7 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_sb600] = - { + [board_ahci_sb600] = { AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255 | AHCI_HFLAG_32BIT_ONLY), @@ -183,16 +182,14 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_pmp_retry_srst_ops, }, - [board_ahci_sb700] = /* for SB700 and SB800 */ - { + [board_ahci_sb700] = { /* for SB700 and SB800 */ AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_pmp_retry_srst_ops, }, - [board_ahci_vt8251] = - { + [board_ahci_vt8251] = { AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, @@ -261,10 +258,60 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */ { PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */ { PCI_VDEVICE(INTEL, 0x1e0e), board_ahci }, /* Panther Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c02), board_ahci }, /* Lynx Point AHCI */ + { PCI_VDEVICE(INTEL, 0x8c03), board_ahci }, /* Lynx Point AHCI */ + { PCI_VDEVICE(INTEL, 0x8c04), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c05), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c06), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c07), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c0e), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x8c0f), board_ahci }, /* Lynx Point RAID */ + { PCI_VDEVICE(INTEL, 0x9c02), board_ahci }, /* Lynx Point-LP AHCI */ + { PCI_VDEVICE(INTEL, 0x9c03), board_ahci }, /* Lynx Point-LP AHCI */ + { PCI_VDEVICE(INTEL, 0x9c04), board_ahci }, /* Lynx Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9c05), board_ahci }, /* Lynx Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9c06), board_ahci }, /* Lynx Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9c07), board_ahci }, /* Lynx Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9c0e), board_ahci }, /* Lynx Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9c0f), board_ahci }, /* Lynx Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x1f22), board_ahci }, /* Avoton AHCI */ + { PCI_VDEVICE(INTEL, 0x1f23), board_ahci }, /* Avoton AHCI */ + { PCI_VDEVICE(INTEL, 0x1f24), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f25), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f26), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */ + { PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */ + { PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ + { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */ + { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */ + { PCI_VDEVICE(INTEL, 0x8d04), board_ahci }, /* Wellsburg RAID */ + { PCI_VDEVICE(INTEL, 0x8d06), board_ahci }, /* Wellsburg RAID */ + { PCI_VDEVICE(INTEL, 0x8d0e), board_ahci }, /* Wellsburg RAID */ + { PCI_VDEVICE(INTEL, 0x8d62), board_ahci }, /* Wellsburg AHCI */ + { PCI_VDEVICE(INTEL, 0x8d64), board_ahci }, /* Wellsburg RAID */ + { PCI_VDEVICE(INTEL, 0x8d66), board_ahci }, /* Wellsburg RAID */ + { PCI_VDEVICE(INTEL, 0x8d6e), board_ahci }, /* Wellsburg RAID */ + { PCI_VDEVICE(INTEL, 0x23a3), board_ahci }, /* Coleto Creek AHCI */ + { PCI_VDEVICE(INTEL, 0x9c83), board_ahci }, /* Wildcat Point-LP AHCI */ + { PCI_VDEVICE(INTEL, 0x9c85), board_ahci }, /* Wildcat Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9c87), board_ahci }, /* Wildcat Point-LP RAID */ + { PCI_VDEVICE(INTEL, 0x9c8f), board_ahci }, /* Wildcat Point-LP RAID */ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr }, + /* JMicron 362B and 362C have an AHCI function with IDE class code */ + { PCI_VDEVICE(JMICRON, 0x2362), board_ahci_ign_iferr }, + { PCI_VDEVICE(JMICRON, 0x236f), board_ahci_ign_iferr }, /* ATI */ { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ @@ -277,6 +324,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* AMD */ { PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */ + { PCI_VDEVICE(AMD, 0x7900), board_ahci }, /* AMD CZ */ /* AMD is using RAID class only for ahci controllers */ { PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci }, @@ -382,20 +430,48 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* Marvell */ { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */ { PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv }, /* 6121 */ - { PCI_DEVICE(0x1b4b, 0x9123), + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9123), .class = PCI_CLASS_STORAGE_SATA_AHCI, .class_mask = 0xffffff, .driver_data = board_ahci_yes_fbs }, /* 88se9128 */ - { PCI_DEVICE(0x1b4b, 0x9125), + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9125), .driver_data = board_ahci_yes_fbs }, /* 88se9125 */ - { PCI_DEVICE(0x1b4b, 0x91a3), + { PCI_DEVICE_SUB(PCI_VENDOR_ID_MARVELL_EXT, 0x9178, + PCI_VENDOR_ID_MARVELL_EXT, 0x9170), + .driver_data = board_ahci_yes_fbs }, /* 88se9170 */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x917a), + .driver_data = board_ahci_yes_fbs }, /* 88se9172 */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9172), + .driver_data = board_ahci_yes_fbs }, /* 88se9172 */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9192), + .driver_data = board_ahci_yes_fbs }, /* 88se9172 on some Gigabyte */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0), + .driver_data = board_ahci_yes_fbs }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3), + .driver_data = board_ahci_yes_fbs }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230), + .driver_data = board_ahci_yes_fbs }, + { PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642), .driver_data = board_ahci_yes_fbs }, /* Promise */ { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */ + { PCI_VDEVICE(PROMISE, 0x3781), board_ahci }, /* FastTrak TX8660 ahci-mode */ /* Asmedia */ - { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1061 */ + { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci }, /* ASM1060 */ + { PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci }, /* ASM1060 */ + { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */ + { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */ + + /* + * Samsung SSDs found on some macbooks. NCQ times out. + * https://bugzilla.kernel.org/show_bug.cgi?id=60731 + */ + { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_noncq }, + + /* Enmotus */ + { PCI_DEVICE(0x1c44, 0x8000), board_ahci }, /* Generic, PCI class code for AHCI */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, @@ -506,6 +582,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; bool online; int rc; @@ -516,7 +593,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), deadline, &online, NULL); - ahci_start_engine(ap); + hpriv->start_engine(ap); DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); @@ -531,6 +608,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, { struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; bool online; @@ -540,13 +618,13 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(link->device, &tf); - tf.command = 0x80; + tf.command = ATA_BUSY; ata_tf_to_fis(&tf, 0, 0, d2h_fis); rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), deadline, &online, NULL); - ahci_start_engine(ap); + hpriv->start_engine(ap); /* The pseudo configuration device on SIMG4726 attached to * ASUS P5W-DH Deluxe doesn't send signature FIS after @@ -573,7 +651,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, #ifdef CONFIG_PM static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); struct ahci_host_priv *hpriv = host->private_data; void __iomem *mmio = hpriv->mmio; u32 ctl; @@ -601,13 +679,17 @@ static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) static int ahci_pci_device_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); if (rc) return rc; + /* Apple BIOS helpfully mangles the registers on resume */ + if (is_mcp89_apple(pdev)) + ahci_mcp89_apple_enable(pdev); + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { rc = ahci_pci_reset_controller(host); if (rc) @@ -724,6 +806,48 @@ static void ahci_p5wdh_workaround(struct ata_host *host) } } +/* + * Macbook7,1 firmware forcibly disables MCP89 AHCI and changes PCI ID when + * booting in BIOS compatibility mode. We restore the registers but not ID. + */ +static void ahci_mcp89_apple_enable(struct pci_dev *pdev) +{ + u32 val; + + printk(KERN_INFO "ahci: enabling MCP89 AHCI mode\n"); + + pci_read_config_dword(pdev, 0xf8, &val); + val |= 1 << 0x1b; + /* the following changes the device ID, but appears not to affect function */ + /* val = (val & ~0xf0000000) | 0x80000000; */ + pci_write_config_dword(pdev, 0xf8, val); + + pci_read_config_dword(pdev, 0x54c, &val); + val |= 1 << 0xc; + pci_write_config_dword(pdev, 0x54c, val); + + pci_read_config_dword(pdev, 0x4a4, &val); + val &= 0xff; + val |= 0x01060100; + pci_write_config_dword(pdev, 0x4a4, val); + + pci_read_config_dword(pdev, 0x54c, &val); + val &= ~(1 << 0xc); + pci_write_config_dword(pdev, 0x54c, val); + + pci_read_config_dword(pdev, 0xf8, &val); + val &= ~(1 << 0x1b); + pci_write_config_dword(pdev, 0xf8, val); +} + +static bool is_mcp89_apple(struct pci_dev *pdev) +{ + return pdev->vendor == PCI_VENDOR_ID_NVIDIA && + pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA && + pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE && + pdev->subsystem_device == 0xcb89; +} + /* only some SB600 ahci controllers can do 64bit DMA */ static bool ahci_sb600_enable_64bit(struct pci_dev *pdev) { @@ -769,6 +893,22 @@ static bool ahci_sb600_enable_64bit(struct pci_dev *pdev) }, }, /* + * All BIOS versions for the MSI K9AGM2 (MS-7327) support + * 64bit DMA. + * + * This board also had the typo mentioned above in the + * Manufacturer DMI field (fixed in BIOS version 1.5), so + * match on DMI_BOARD_VENDOR of "MICRO-STAR INTER" again. + */ + { + .ident = "MSI K9AGM2", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, + "MICRO-STAR INTER"), + DMI_MATCH(DMI_BOARD_NAME, "MS-7327"), + }, + }, + /* * All BIOS versions for the Asus M3A support 64bit DMA. * (all release versions from 0301 to 1206 were tested) */ @@ -980,6 +1120,17 @@ static bool ahci_broken_online(struct pci_dev *pdev) return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff); } +static bool ahci_broken_devslp(struct pci_dev *pdev) +{ + /* device with broken DEVSLP but still showing SDS capability */ + static const struct pci_device_id ids[] = { + { PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */ + {} + }; + + return pci_match_id(ids, pdev); +} + #ifdef CONFIG_ATA_ACPI static void ahci_gtf_filter_workaround(struct ata_host *host) { @@ -1028,6 +1179,116 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) {} #endif +static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, + struct ahci_host_priv *hpriv) +{ + int rc, nvec; + + if (hpriv->flags & AHCI_HFLAG_NO_MSI) + goto intx; + + nvec = pci_msi_vec_count(pdev); + if (nvec < 0) + goto intx; + + /* + * If number of MSIs is less than number of ports then Sharing Last + * Message mode could be enforced. In this case assume that advantage + * of multipe MSIs is negated and use single MSI mode instead. + */ + if (nvec < n_ports) + goto single_msi; + + rc = pci_enable_msi_exact(pdev, nvec); + if (rc == -ENOSPC) + goto single_msi; + else if (rc < 0) + goto intx; + + /* fallback to single MSI mode if the controller enforced MRSM mode */ + if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) { + pci_disable_msi(pdev); + printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n"); + goto single_msi; + } + + return nvec; + +single_msi: + if (pci_enable_msi(pdev)) + goto intx; + return 1; + +intx: + pci_intx(pdev, 1); + return 0; +} + +/** + * ahci_host_activate - start AHCI host, request IRQs and register it + * @host: target ATA host + * @irq: base IRQ number to request + * @n_msis: number of MSIs allocated for this host + * @irq_handler: irq_handler used when requesting IRQs + * @irq_flags: irq_flags used when requesting IRQs + * + * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1 + * when multiple MSIs were allocated. That is one MSI per port, starting + * from @irq. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis) +{ + int i, rc; + + /* Sharing Last Message among several ports is not supported */ + if (n_msis < host->n_ports) + return -EINVAL; + + rc = ata_host_start(host); + if (rc) + return rc; + + for (i = 0; i < host->n_ports; i++) { + struct ahci_port_priv *pp = host->ports[i]->private_data; + + /* Do not receive interrupts sent by dummy ports */ + if (!pp) { + disable_irq(irq + i); + continue; + } + + rc = devm_request_threaded_irq(host->dev, irq + i, + ahci_hw_interrupt, + ahci_thread_fn, IRQF_SHARED, + pp->irq_desc, host->ports[i]); + if (rc) + goto out_free_irqs; + } + + for (i = 0; i < host->n_ports; i++) + ata_port_desc(host->ports[i], "irq %d", irq + i); + + rc = ata_host_register(host, &ahci_sht); + if (rc) + goto out_free_all_irqs; + + return 0; + +out_free_all_irqs: + i = host->n_ports; +out_free_irqs: + for (i--; i >= 0; i--) + devm_free_irq(host->dev, irq + i, host->ports[i]); + + return rc; +} + static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { unsigned int board_id = ent->driver_data; @@ -1036,7 +1297,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; struct ata_host *host; - int n_ports, i, rc; + int n_ports, n_msis, i, rc; int ahci_pci_bar = AHCI_PCI_BAR_STANDARD; VPRINTK("ENTER\n"); @@ -1051,15 +1312,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (pdev->vendor == PCI_VENDOR_ID_MARVELL && !marvell_enable) return -ENODEV; - /* - * For some reason, MCP89 on MacBook 7,1 doesn't work with - * ahci, use ata_generic instead. - */ - if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && - pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA && - pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE && - pdev->subsystem_device == 0xcb89) - return -ENODEV; + /* Apple BIOS on MCP89 prevents us using AHCI */ + if (is_mcp89_apple(pdev)) + ahci_mcp89_apple_enable(pdev); /* Promise's PDC42819 is a SAS/SATA controller that has an AHCI mode. * At the moment, we can only use the AHCI mode. Let the users know @@ -1069,24 +1324,17 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "PDC42819 can only drive SATA devices with this driver\n"); - /* The Connext uses non-standard BAR */ + /* Both Connext and Enmotus devices use non-standard BARs */ if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06) ahci_pci_bar = AHCI_PCI_BAR_STA2X11; + else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000) + ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS; /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; - /* AHCI controllers often implement SFF compatible interface. - * Grab all PCI BARs just in case. - */ - rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME); - if (rc == -EBUSY) - pcim_pin_device(pdev); - if (rc) - return rc; - if (pdev->vendor == PCI_VENDOR_ID_INTEL && (pdev->device == 0x2652 || pdev->device == 0x2653)) { u8 map; @@ -1103,6 +1351,15 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } + /* AHCI controllers often implement SFF compatible interface. + * Grab all PCI BARs just in case. + */ + rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME); + if (rc == -EBUSY) + pcim_pin_device(pdev); + if (rc) + return rc; + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; @@ -1121,11 +1378,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (ahci_sb600_enable_64bit(pdev)) hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY; - if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) - pci_intx(pdev, 1); - hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; + /* must set flag prior to save config in order to take effect */ + if (ahci_broken_devslp(pdev)) + hpriv->flags |= AHCI_HFLAG_NO_DEVSLP; + /* save initial config */ ahci_pci_save_initial_config(pdev, hpriv); @@ -1140,6 +1398,14 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) */ if (!(hpriv->flags & AHCI_HFLAG_NO_FPDMA_AA)) pi.flags |= ATA_FLAG_FPDMA_AA; + + /* + * All AHCI controllers should be forward-compatible + * with the new auxiliary field. This code should be + * conditionalized if any buggy AHCI controllers are + * encountered. + */ + pi.flags |= ATA_FLAG_FPDMA_AUX; } if (hpriv->cap & HOST_CAP_PMP) @@ -1172,6 +1438,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) */ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + n_msis = ahci_init_interrupts(pdev, n_ports, hpriv); + if (n_msis > 1) + hpriv->flags |= AHCI_HFLAG_MULTI_MSI; + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); if (!host) return -ENOMEM; @@ -1180,7 +1450,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) host->flags |= ATA_HOST_PARALLEL_SCAN; else - printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n"); + dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n"); if (pi.flags & ATA_FLAG_EM) ahci_reset_em(host); @@ -1221,26 +1491,18 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ahci_pci_print_info(host); pci_set_master(pdev); - return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, - &ahci_sht); -} -static int __init ahci_init(void) -{ - return pci_register_driver(&ahci_pci_driver); -} + if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) + return ahci_host_activate(host, pdev->irq, n_msis); -static void __exit ahci_exit(void) -{ - pci_unregister_driver(&ahci_pci_driver); + return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, + &ahci_sht); } +module_pci_driver(ahci_pci_driver); MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("AHCI SATA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, ahci_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(ahci_init); -module_exit(ahci_exit); diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index b1750007c8d..5513296e5e2 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -1,7 +1,7 @@ /* * ahci.h - Common AHCI SATA definitions and declarations * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -35,7 +35,10 @@ #ifndef _AHCI_H #define _AHCI_H +#include <linux/clk.h> #include <linux/libata.h> +#include <linux/phy/phy.h> +#include <linux/regulator/consumer.h> /* Enclosure Management Control */ #define EM_CTRL_MSG_TYPE 0x000f0000 @@ -50,6 +53,7 @@ enum { AHCI_MAX_PORTS = 32, + AHCI_MAX_CLKS = 3, AHCI_MAX_SG = 168, /* hardware max is 64K */ AHCI_DMA_BOUNDARY = 0xffffffff, AHCI_MAX_CMDS = 32, @@ -90,6 +94,7 @@ enum { /* HOST_CTL bits */ HOST_RESET = (1 << 0), /* reset controller; self-clear */ HOST_IRQ_EN = (1 << 1), /* global IRQ enable */ + HOST_MRSM = (1 << 2), /* MSI Revert to Single Message */ HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ /* HOST_CAP bits */ @@ -115,6 +120,9 @@ enum { HOST_CAP2_BOH = (1 << 0), /* BIOS/OS handoff supported */ HOST_CAP2_NVMHCI = (1 << 1), /* NVMHCI supported */ HOST_CAP2_APST = (1 << 2), /* Automatic partial to slumber */ + HOST_CAP2_SDS = (1 << 3), /* Support device sleep */ + HOST_CAP2_SADM = (1 << 4), /* Support aggressive DevSlp */ + HOST_CAP2_DESO = (1 << 5), /* DevSlp from slumber only */ /* registers for each SATA port */ PORT_LST_ADDR = 0x00, /* command list DMA addr */ @@ -133,6 +141,7 @@ enum { PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ PORT_SCR_NTF = 0x3c, /* SATA phy register: SNotification */ PORT_FBS = 0x40, /* FIS-based Switching */ + PORT_DEVSLP = 0x44, /* device sleep */ /* PORT_IRQ_{STAT,MASK} bits */ PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ @@ -186,6 +195,7 @@ enum { PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ + /* PORT_FBS bits */ PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */ PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */ PORT_FBS_DEV_OFFSET = 8, /* FBS device to issue offset */ @@ -194,7 +204,19 @@ enum { PORT_FBS_DEC = (1 << 1), /* FBS device error clear */ PORT_FBS_EN = (1 << 0), /* Enable FBS */ + /* PORT_DEVSLP bits */ + PORT_DEVSLP_DM_OFFSET = 25, /* DITO multiplier offset */ + PORT_DEVSLP_DM_MASK = (0xf << 25), /* DITO multiplier mask */ + PORT_DEVSLP_DITO_OFFSET = 15, /* DITO offset */ + PORT_DEVSLP_MDAT_OFFSET = 10, /* Minimum assertion time */ + PORT_DEVSLP_DETO_OFFSET = 2, /* DevSlp exit timeout */ + PORT_DEVSLP_DSP = (1 << 1), /* DevSlp present */ + PORT_DEVSLP_ADSE = (1 << 0), /* Aggressive DevSlp enable */ + /* hpriv->flags bits */ + +#define AHCI_HFLAGS(flags) .private_data = (void *)(flags) + AHCI_HFLAG_NO_NCQ = (1 << 0), AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */ AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */ @@ -210,6 +232,12 @@ enum { AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */ AHCI_HFLAG_NO_FPDMA_AA = (1 << 13), /* no FPDMA AA */ AHCI_HFLAG_YES_FBS = (1 << 14), /* force FBS cap on */ + AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on + port start (wait until + error-handling stage) */ + AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */ + AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */ + AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */ /* ap->flags bits */ @@ -276,12 +304,15 @@ struct ahci_port_priv { unsigned int ncq_saw_d2h:1; unsigned int ncq_saw_dmas:1; unsigned int ncq_saw_sdb:1; + u32 intr_status; /* interrupts to handle */ + spinlock_t lock; /* protects parent ata_port */ u32 intr_mask; /* interrupts to enable */ bool fbs_supported; /* set iff FBS is supported */ bool fbs_enabled; /* set iff FBS is enabled */ int fbs_last_dev; /* save FBS.DEV of last FIS */ /* enclosure management info per PM slot */ struct ahci_em_priv em_priv[EM_MAX_SLOTS]; + char *irq_desc; /* desc in /proc/interrupts */ }; struct ahci_host_priv { @@ -296,6 +327,17 @@ struct ahci_host_priv { u32 em_loc; /* enclosure management location */ u32 em_buf_sz; /* EM buffer size in byte */ u32 em_msg_type; /* EM message type */ + bool got_runtime_pm; /* Did we do pm_runtime_get? */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ + struct phy *phy; /* If platform uses phy */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the + * default ahci_start_engine during ahci_save_initial_config, this can + * be overridden anytime before the host is activated. + */ + void (*start_engine)(struct ata_port *ap); }; extern int ahci_ignore_sss; @@ -312,8 +354,10 @@ extern struct device_attribute *ahci_sdev_attrs[]; .sdev_attrs = ahci_sdev_attrs extern struct ata_port_operations ahci_ops; +extern struct ata_port_operations ahci_platform_ops; extern struct ata_port_operations ahci_pmp_retry_srst_ops; +unsigned int ahci_dev_classify(struct ata_port *ap); void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, u32 opts); void ahci_save_initial_config(struct device *dev, @@ -327,7 +371,9 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, int pmp, unsigned long deadline, int (*check_ready)(struct ata_link *link)); +unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); int ahci_stop_engine(struct ata_port *ap); +void ahci_start_fis_rx(struct ata_port *ap); void ahci_start_engine(struct ata_port *ap); int ahci_check_ready(struct ata_link *link); int ahci_kick_engine(struct ata_port *ap); @@ -336,7 +382,11 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv, struct ata_port_info *pi); int ahci_reset_em(struct ata_host *host); irqreturn_t ahci_interrupt(int irq, void *dev_instance); +irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance); +irqreturn_t ahci_thread_fn(int irq, void *dev_instance); void ahci_print_info(struct ata_host *host, const char *scc_s); +int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis); +void ahci_error_handler(struct ata_port *ap); static inline void __iomem *__ahci_port_base(struct ata_host *host, unsigned int port_no) diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c new file mode 100644 index 00000000000..2b77d53bccf --- /dev/null +++ b/drivers/ata/ahci_da850.c @@ -0,0 +1,115 @@ +/* + * DaVinci DA850 AHCI SATA platform driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/ahci_platform.h> +#include "ahci.h" + +/* SATA PHY Control Register offset from AHCI base */ +#define SATA_P0PHYCR_REG 0x178 + +#define SATA_PHY_MPY(x) ((x) << 0) +#define SATA_PHY_LOS(x) ((x) << 6) +#define SATA_PHY_RXCDR(x) ((x) << 10) +#define SATA_PHY_RXEQ(x) ((x) << 13) +#define SATA_PHY_TXSWING(x) ((x) << 19) +#define SATA_PHY_ENPLL(x) ((x) << 31) + +/* + * The multiplier needed for 1.5GHz PLL output. + * + * NOTE: This is currently hardcoded to be suitable for 100MHz crystal + * frequency (which is used by DA850 EVM board) and may need to be changed + * if you would like to use this driver on some other board. + */ +#define DA850_SATA_CLK_MULTIPLIER 7 + +static void da850_sata_init(struct device *dev, void __iomem *pwrdn_reg, + void __iomem *ahci_base) +{ + unsigned int val; + + /* Enable SATA clock receiver */ + val = readl(pwrdn_reg); + val &= ~BIT(0); + writel(val, pwrdn_reg); + + val = SATA_PHY_MPY(DA850_SATA_CLK_MULTIPLIER + 1) | SATA_PHY_LOS(1) | + SATA_PHY_RXCDR(4) | SATA_PHY_RXEQ(1) | SATA_PHY_TXSWING(3) | + SATA_PHY_ENPLL(1); + + writel(val, ahci_base + SATA_P0PHYCR_REG); +} + +static const struct ata_port_info ahci_da850_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, +}; + +static int ahci_da850_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct resource *res; + void __iomem *pwrdn_reg; + int rc; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + goto disable_resources; + + pwrdn_reg = devm_ioremap(dev, res->start, resource_size(res)); + if (!pwrdn_reg) + goto disable_resources; + + da850_sata_init(dev, pwrdn_reg, hpriv->mmio); + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, + 0, 0, 0); + if (rc) + goto disable_resources; + + return 0; +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +static SIMPLE_DEV_PM_OPS(ahci_da850_pm_ops, ahci_platform_suspend, + ahci_platform_resume); + +static struct platform_driver ahci_da850_driver = { + .probe = ahci_da850_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = "ahci_da850", + .owner = THIS_MODULE, + .pm = &ahci_da850_pm_ops, + }, +}; +module_platform_driver(ahci_da850_driver); + +MODULE_DESCRIPTION("DaVinci DA850 AHCI SATA platform driver"); +MODULE_AUTHOR("Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c new file mode 100644 index 00000000000..cac4360f272 --- /dev/null +++ b/drivers/ata/ahci_imx.c @@ -0,0 +1,527 @@ +/* + * copyright (c) 2013 Freescale Semiconductor, Inc. + * Freescale IMX AHCI SATA platform driver + * + * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/ahci_platform.h> +#include <linux/of_device.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/libata.h> +#include "ahci.h" + +enum { + /* Timer 1-ms Register */ + IMX_TIMER1MS = 0x00e0, + /* Port0 PHY Control Register */ + IMX_P0PHYCR = 0x0178, + IMX_P0PHYCR_TEST_PDDQ = 1 << 20, + IMX_P0PHYCR_CR_READ = 1 << 19, + IMX_P0PHYCR_CR_WRITE = 1 << 18, + IMX_P0PHYCR_CR_CAP_DATA = 1 << 17, + IMX_P0PHYCR_CR_CAP_ADDR = 1 << 16, + /* Port0 PHY Status Register */ + IMX_P0PHYSR = 0x017c, + IMX_P0PHYSR_CR_ACK = 1 << 18, + IMX_P0PHYSR_CR_DATA_OUT = 0xffff << 0, + /* Lane0 Output Status Register */ + IMX_LANE0_OUT_STAT = 0x2003, + IMX_LANE0_OUT_STAT_RX_PLL_STATE = 1 << 1, + /* Clock Reset Register */ + IMX_CLOCK_RESET = 0x7f3f, + IMX_CLOCK_RESET_RESET = 1 << 0, +}; + +enum ahci_imx_type { + AHCI_IMX53, + AHCI_IMX6Q, +}; + +struct imx_ahci_priv { + struct platform_device *ahci_pdev; + enum ahci_imx_type type; + struct clk *sata_clk; + struct clk *sata_ref_clk; + struct clk *ahb_clk; + struct regmap *gpr; + bool no_device; + bool first_time; +}; + +static int ahci_imx_hotplug; +module_param_named(hotplug, ahci_imx_hotplug, int, 0644); +MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)"); + +static void ahci_imx_host_stop(struct ata_host *host); + +static int imx_phy_crbit_assert(void __iomem *mmio, u32 bit, bool assert) +{ + int timeout = 10; + u32 crval; + u32 srval; + + /* Assert or deassert the bit */ + crval = readl(mmio + IMX_P0PHYCR); + if (assert) + crval |= bit; + else + crval &= ~bit; + writel(crval, mmio + IMX_P0PHYCR); + + /* Wait for the cr_ack signal */ + do { + srval = readl(mmio + IMX_P0PHYSR); + if ((assert ? srval : ~srval) & IMX_P0PHYSR_CR_ACK) + break; + usleep_range(100, 200); + } while (--timeout); + + return timeout ? 0 : -ETIMEDOUT; +} + +static int imx_phy_reg_addressing(u16 addr, void __iomem *mmio) +{ + u32 crval = addr; + int ret; + + /* Supply the address on cr_data_in */ + writel(crval, mmio + IMX_P0PHYCR); + + /* Assert the cr_cap_addr signal */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, true); + if (ret) + return ret; + + /* Deassert cr_cap_addr */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, false); + if (ret) + return ret; + + return 0; +} + +static int imx_phy_reg_write(u16 val, void __iomem *mmio) +{ + u32 crval = val; + int ret; + + /* Supply the data on cr_data_in */ + writel(crval, mmio + IMX_P0PHYCR); + + /* Assert the cr_cap_data signal */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, true); + if (ret) + return ret; + + /* Deassert cr_cap_data */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, false); + if (ret) + return ret; + + if (val & IMX_CLOCK_RESET_RESET) { + /* + * In case we're resetting the phy, it's unable to acknowledge, + * so we return immediately here. + */ + crval |= IMX_P0PHYCR_CR_WRITE; + writel(crval, mmio + IMX_P0PHYCR); + goto out; + } + + /* Assert the cr_write signal */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, true); + if (ret) + return ret; + + /* Deassert cr_write */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, false); + if (ret) + return ret; + +out: + return 0; +} + +static int imx_phy_reg_read(u16 *val, void __iomem *mmio) +{ + int ret; + + /* Assert the cr_read signal */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, true); + if (ret) + return ret; + + /* Capture the data from cr_data_out[] */ + *val = readl(mmio + IMX_P0PHYSR) & IMX_P0PHYSR_CR_DATA_OUT; + + /* Deassert cr_read */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, false); + if (ret) + return ret; + + return 0; +} + +static int imx_sata_phy_reset(struct ahci_host_priv *hpriv) +{ + void __iomem *mmio = hpriv->mmio; + int timeout = 10; + u16 val; + int ret; + + /* Reset SATA PHY by setting RESET bit of PHY register CLOCK_RESET */ + ret = imx_phy_reg_addressing(IMX_CLOCK_RESET, mmio); + if (ret) + return ret; + ret = imx_phy_reg_write(IMX_CLOCK_RESET_RESET, mmio); + if (ret) + return ret; + + /* Wait for PHY RX_PLL to be stable */ + do { + usleep_range(100, 200); + ret = imx_phy_reg_addressing(IMX_LANE0_OUT_STAT, mmio); + if (ret) + return ret; + ret = imx_phy_reg_read(&val, mmio); + if (ret) + return ret; + if (val & IMX_LANE0_OUT_STAT_RX_PLL_STATE) + break; + } while (--timeout); + + return timeout ? 0 : -ETIMEDOUT; +} + +static int imx_sata_enable(struct ahci_host_priv *hpriv) +{ + struct imx_ahci_priv *imxpriv = hpriv->plat_data; + struct device *dev = &imxpriv->ahci_pdev->dev; + int ret; + + if (imxpriv->no_device) + return 0; + + if (hpriv->target_pwr) { + ret = regulator_enable(hpriv->target_pwr); + if (ret) + return ret; + } + + ret = clk_prepare_enable(imxpriv->sata_ref_clk); + if (ret < 0) + goto disable_regulator; + + if (imxpriv->type == AHCI_IMX6Q) { + /* + * set PHY Paremeters, two steps to configure the GPR13, + * one write for rest of parameters, mask of first write + * is 0x07ffffff, and the other one write for setting + * the mpll_clk_en. + */ + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK | + IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK | + IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK | + IMX6Q_GPR13_SATA_SPD_MODE_MASK | + IMX6Q_GPR13_SATA_MPLL_SS_EN | + IMX6Q_GPR13_SATA_TX_ATTEN_MASK | + IMX6Q_GPR13_SATA_TX_BOOST_MASK | + IMX6Q_GPR13_SATA_TX_LVL_MASK | + IMX6Q_GPR13_SATA_MPLL_CLK_EN | + IMX6Q_GPR13_SATA_TX_EDGE_RATE, + IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB | + IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M | + IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F | + IMX6Q_GPR13_SATA_SPD_MODE_3P0G | + IMX6Q_GPR13_SATA_MPLL_SS_EN | + IMX6Q_GPR13_SATA_TX_ATTEN_9_16 | + IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB | + IMX6Q_GPR13_SATA_TX_LVL_1_025_V); + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + IMX6Q_GPR13_SATA_MPLL_CLK_EN); + + usleep_range(100, 200); + + ret = imx_sata_phy_reset(hpriv); + if (ret) { + dev_err(dev, "failed to reset phy: %d\n", ret); + goto disable_regulator; + } + } + + usleep_range(1000, 2000); + + return 0; + +disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); + + return ret; +} + +static void imx_sata_disable(struct ahci_host_priv *hpriv) +{ + struct imx_ahci_priv *imxpriv = hpriv->plat_data; + + if (imxpriv->no_device) + return; + + if (imxpriv->type == AHCI_IMX6Q) { + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + !IMX6Q_GPR13_SATA_MPLL_CLK_EN); + } + + clk_disable_unprepare(imxpriv->sata_ref_clk); + + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); +} + +static void ahci_imx_error_handler(struct ata_port *ap) +{ + u32 reg_val; + struct ata_device *dev; + struct ata_host *host = dev_get_drvdata(ap->dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + struct imx_ahci_priv *imxpriv = hpriv->plat_data; + + ahci_error_handler(ap); + + if (!(imxpriv->first_time) || ahci_imx_hotplug) + return; + + imxpriv->first_time = false; + + ata_for_each_dev(dev, &ap->link, ENABLED) + return; + /* + * Disable link to save power. An imx ahci port can't be recovered + * without full reset once the pddq mode is enabled making it + * impossible to use as part of libata LPM. + */ + reg_val = readl(mmio + IMX_P0PHYCR); + writel(reg_val | IMX_P0PHYCR_TEST_PDDQ, mmio + IMX_P0PHYCR); + imx_sata_disable(hpriv); + imxpriv->no_device = true; + + dev_info(ap->dev, "no device found, disabling link.\n"); + dev_info(ap->dev, "pass " MODULE_PARAM_PREFIX ".hotplug=1 to enable hotplug\n"); +} + +static int ahci_imx_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct ata_host *host = dev_get_drvdata(ap->dev); + struct ahci_host_priv *hpriv = host->private_data; + struct imx_ahci_priv *imxpriv = hpriv->plat_data; + int ret = -EIO; + + if (imxpriv->type == AHCI_IMX53) + ret = ahci_pmp_retry_srst_ops.softreset(link, class, deadline); + else if (imxpriv->type == AHCI_IMX6Q) + ret = ahci_ops.softreset(link, class, deadline); + + return ret; +} + +static struct ata_port_operations ahci_imx_ops = { + .inherits = &ahci_ops, + .host_stop = ahci_imx_host_stop, + .error_handler = ahci_imx_error_handler, + .softreset = ahci_imx_softreset, +}; + +static const struct ata_port_info ahci_imx_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_imx_ops, +}; + +static const struct of_device_id imx_ahci_of_match[] = { + { .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 }, + { .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx_ahci_of_match); + +static int imx_ahci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; + struct ahci_host_priv *hpriv; + struct imx_ahci_priv *imxpriv; + unsigned int reg_val; + int ret; + + of_id = of_match_device(imx_ahci_of_match, dev); + if (!of_id) + return -EINVAL; + + imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL); + if (!imxpriv) + return -ENOMEM; + + imxpriv->ahci_pdev = pdev; + imxpriv->no_device = false; + imxpriv->first_time = true; + imxpriv->type = (enum ahci_imx_type)of_id->data; + + imxpriv->sata_clk = devm_clk_get(dev, "sata"); + if (IS_ERR(imxpriv->sata_clk)) { + dev_err(dev, "can't get sata clock.\n"); + return PTR_ERR(imxpriv->sata_clk); + } + + imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref"); + if (IS_ERR(imxpriv->sata_ref_clk)) { + dev_err(dev, "can't get sata_ref clock.\n"); + return PTR_ERR(imxpriv->sata_ref_clk); + } + + imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(imxpriv->ahb_clk)) { + dev_err(dev, "can't get ahb clock.\n"); + return PTR_ERR(imxpriv->ahb_clk); + } + + if (imxpriv->type == AHCI_IMX6Q) { + imxpriv->gpr = syscon_regmap_lookup_by_compatible( + "fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imxpriv->gpr)) { + dev_err(dev, + "failed to find fsl,imx6q-iomux-gpr regmap\n"); + return PTR_ERR(imxpriv->gpr); + } + } + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + hpriv->plat_data = imxpriv; + + ret = clk_prepare_enable(imxpriv->sata_clk); + if (ret) + return ret; + + ret = imx_sata_enable(hpriv); + if (ret) + goto disable_clk; + + /* + * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, + * and IP vendor specific register IMX_TIMER1MS. + * Configure CAP_SSS (support stagered spin up). + * Implement the port0. + * Get the ahb clock rate, and configure the TIMER1MS register. + */ + reg_val = readl(hpriv->mmio + HOST_CAP); + if (!(reg_val & HOST_CAP_SSS)) { + reg_val |= HOST_CAP_SSS; + writel(reg_val, hpriv->mmio + HOST_CAP); + } + reg_val = readl(hpriv->mmio + HOST_PORTS_IMPL); + if (!(reg_val & 0x1)) { + reg_val |= 0x1; + writel(reg_val, hpriv->mmio + HOST_PORTS_IMPL); + } + + reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; + writel(reg_val, hpriv->mmio + IMX_TIMER1MS); + + ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, + 0, 0, 0); + if (ret) + goto disable_sata; + + return 0; + +disable_sata: + imx_sata_disable(hpriv); +disable_clk: + clk_disable_unprepare(imxpriv->sata_clk); + return ret; +} + +static void ahci_imx_host_stop(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + struct imx_ahci_priv *imxpriv = hpriv->plat_data; + + imx_sata_disable(hpriv); + clk_disable_unprepare(imxpriv->sata_clk); +} + +#ifdef CONFIG_PM_SLEEP +static int imx_ahci_suspend(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int ret; + + ret = ahci_platform_suspend_host(dev); + if (ret) + return ret; + + imx_sata_disable(hpriv); + + return 0; +} + +static int imx_ahci_resume(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int ret; + + ret = imx_sata_enable(hpriv); + if (ret) + return ret; + + return ahci_platform_resume_host(dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(ahci_imx_pm_ops, imx_ahci_suspend, imx_ahci_resume); + +static struct platform_driver imx_ahci_driver = { + .probe = imx_ahci_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = "ahci-imx", + .owner = THIS_MODULE, + .of_match_table = imx_ahci_of_match, + .pm = &ahci_imx_pm_ops, + }, +}; +module_platform_driver(imx_ahci_driver); + +MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver"); +MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ahci:imx"); diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c new file mode 100644 index 00000000000..fd3dfd733b8 --- /dev/null +++ b/drivers/ata/ahci_mvebu.c @@ -0,0 +1,128 @@ +/* + * AHCI glue platform driver for Marvell EBU SOCs + * + * Copyright (C) 2014 Marvell + * + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> + * Marcin Wojtas <mw@semihalf.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/ahci_platform.h> +#include <linux/kernel.h> +#include <linux/mbus.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include "ahci.h" + +#define AHCI_VENDOR_SPECIFIC_0_ADDR 0xa0 +#define AHCI_VENDOR_SPECIFIC_0_DATA 0xa4 + +#define AHCI_WINDOW_CTRL(win) (0x60 + ((win) << 4)) +#define AHCI_WINDOW_BASE(win) (0x64 + ((win) << 4)) +#define AHCI_WINDOW_SIZE(win) (0x68 + ((win) << 4)) + +static void ahci_mvebu_mbus_config(struct ahci_host_priv *hpriv, + const struct mbus_dram_target_info *dram) +{ + int i; + + for (i = 0; i < 4; i++) { + writel(0, hpriv->mmio + AHCI_WINDOW_CTRL(i)); + writel(0, hpriv->mmio + AHCI_WINDOW_BASE(i)); + writel(0, hpriv->mmio + AHCI_WINDOW_SIZE(i)); + } + + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + + writel((cs->mbus_attr << 8) | + (dram->mbus_dram_target_id << 4) | 1, + hpriv->mmio + AHCI_WINDOW_CTRL(i)); + writel(cs->base, hpriv->mmio + AHCI_WINDOW_BASE(i)); + writel(((cs->size - 1) & 0xffff0000), + hpriv->mmio + AHCI_WINDOW_SIZE(i)); + } +} + +static void ahci_mvebu_regret_option(struct ahci_host_priv *hpriv) +{ + /* + * Enable the regret bit to allow the SATA unit to regret a + * request that didn't receive an acknowlegde and avoid a + * deadlock + */ + writel(0x4, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_ADDR); + writel(0x80, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA); +} + +static const struct ata_port_info ahci_mvebu_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, +}; + +static int ahci_mvebu_probe(struct platform_device *pdev) +{ + struct ahci_host_priv *hpriv; + const struct mbus_dram_target_info *dram; + int rc; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + dram = mv_mbus_dram_info(); + if (!dram) + return -ENODEV; + + ahci_mvebu_mbus_config(hpriv, dram); + ahci_mvebu_regret_option(hpriv); + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info, + 0, 0, 0); + if (rc) + goto disable_resources; + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +static const struct of_device_id ahci_mvebu_of_match[] = { + { .compatible = "marvell,armada-380-ahci", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ahci_mvebu_of_match); + +/* + * We currently don't provide power management related operations, + * since there is no suspend/resume support at the platform level for + * Armada 38x for the moment. + */ +static struct platform_driver ahci_mvebu_driver = { + .probe = ahci_mvebu_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = "ahci-mvebu", + .owner = THIS_MODULE, + .of_match_table = ahci_mvebu_of_match, + }, +}; +module_platform_driver(ahci_mvebu_driver); + +MODULE_DESCRIPTION("Marvell EBU AHCI SATA driver"); +MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>, Marcin Wojtas <mw@semihalf.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ahci_mvebu"); diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 48be4e18916..b10d81ddb52 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -13,98 +13,37 @@ */ #include <linux/kernel.h> -#include <linux/gfp.h> #include <linux/module.h> -#include <linux/init.h> -#include <linux/interrupt.h> +#include <linux/pm.h> #include <linux/device.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/libata.h> #include <linux/ahci_platform.h> #include "ahci.h" -enum ahci_type { - AHCI, /* standard platform ahci */ - IMX53_AHCI, /* ahci on i.mx53 */ +static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, }; -static struct platform_device_id ahci_devtype[] = { - { - .name = "ahci", - .driver_data = AHCI, - }, { - .name = "imx53-ahci", - .driver_data = IMX53_AHCI, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, ahci_devtype); - - -static const struct ata_port_info ahci_port_info[] = { - /* by features */ - [AHCI] = { - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_ops, - }, - [IMX53_AHCI] = { - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_pmp_retry_srst_ops, - }, -}; - -static struct scsi_host_template ahci_platform_sht = { - AHCI_SHT("ahci_platform"), -}; - -static int __init ahci_probe(struct platform_device *pdev) +static int ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_platform_data *pdata = dev_get_platdata(dev); - const struct platform_device_id *id = platform_get_device_id(pdev); - struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; - const struct ata_port_info *ppi[] = { &pi, NULL }; struct ahci_host_priv *hpriv; - struct ata_host *host; - struct resource *mem; - int irq; - int n_ports; - int i; + unsigned long hflags = 0; int rc; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(dev, "no mmio space\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "no irq\n"); - return -EINVAL; - } - - if (pdata && pdata->ata_port_info) - pi = *pdata->ata_port_info; - - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) { - dev_err(dev, "can't alloc ahci_host_priv\n"); - return -ENOMEM; - } + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); - hpriv->flags |= (unsigned long)pi.private_data; - - hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); - if (!hpriv->mmio) { - dev_err(dev, "can't map %pR\n", mem); - return -ENOMEM; - } + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; /* * Some platforms might need to prepare for mmio region access, @@ -115,188 +54,50 @@ static int __init ahci_probe(struct platform_device *pdev) if (pdata && pdata->init) { rc = pdata->init(dev, hpriv->mmio); if (rc) - return rc; - } - - ahci_save_initial_config(dev, hpriv, - pdata ? pdata->force_port_map : 0, - pdata ? pdata->mask_port_map : 0); - - /* prepare host */ - if (hpriv->cap & HOST_CAP_NCQ) - pi.flags |= ATA_FLAG_NCQ; - - if (hpriv->cap & HOST_CAP_PMP) - pi.flags |= ATA_FLAG_PMP; - - ahci_set_em_messages(hpriv, &pi); - - /* CAP.NP sometimes indicate the index of the last enabled - * port, at other times, that of the last possible port, so - * determining the maximum port number requires looking at - * both CAP.NP and port_map. - */ - n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); - - host = ata_host_alloc_pinfo(dev, ppi, n_ports); - if (!host) { - rc = -ENOMEM; - goto err0; - } - - host->private_data = hpriv; - - if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) - host->flags |= ATA_HOST_PARALLEL_SCAN; - else - printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n"); - - if (pi.flags & ATA_FLAG_EM) - ahci_reset_em(host); - - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - - ata_port_desc(ap, "mmio %pR", mem); - ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); - - /* set enclosure management message type */ - if (ap->flags & ATA_FLAG_EM) - ap->em_message_type = hpriv->em_msg_type; - - /* disabled/not-implemented port */ - if (!(hpriv->port_map & (1 << i))) - ap->ops = &ata_dummy_port_ops; + goto disable_resources; } - rc = ahci_reset_controller(host); - if (rc) - goto err0; - - ahci_init_controller(host); - ahci_print_info(host, "platform"); + if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) + hflags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; - rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, - &ahci_platform_sht); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, + hflags, 0, 0); if (rc) - goto err0; + goto pdata_exit; return 0; -err0: +pdata_exit: if (pdata && pdata->exit) pdata->exit(dev); +disable_resources: + ahci_platform_disable_resources(hpriv); return rc; } -static int __devexit ahci_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct ahci_platform_data *pdata = dev_get_platdata(dev); - struct ata_host *host = dev_get_drvdata(dev); - - ata_host_detach(host); - - if (pdata && pdata->exit) - pdata->exit(dev); - - return 0; -} - -#ifdef CONFIG_PM -static int ahci_suspend(struct device *dev) -{ - struct ahci_platform_data *pdata = dev_get_platdata(dev); - struct ata_host *host = dev_get_drvdata(dev); - struct ahci_host_priv *hpriv = host->private_data; - void __iomem *mmio = hpriv->mmio; - u32 ctl; - int rc; - - if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { - dev_err(dev, "firmware update required for suspend/resume\n"); - return -EIO; - } - - /* - * AHCI spec rev1.1 section 8.3.3: - * Software must disable interrupts prior to requesting a - * transition of the HBA to D3 state. - */ - ctl = readl(mmio + HOST_CTL); - ctl &= ~HOST_IRQ_EN; - writel(ctl, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - - rc = ata_host_suspend(host, PMSG_SUSPEND); - if (rc) - return rc; - - if (pdata && pdata->suspend) - return pdata->suspend(dev); - return 0; -} - -static int ahci_resume(struct device *dev) -{ - struct ahci_platform_data *pdata = dev_get_platdata(dev); - struct ata_host *host = dev_get_drvdata(dev); - int rc; - - if (pdata && pdata->resume) { - rc = pdata->resume(dev); - if (rc) - return rc; - } - - if (dev->power.power_state.event == PM_EVENT_SUSPEND) { - rc = ahci_reset_controller(host); - if (rc) - return rc; - - ahci_init_controller(host); - } - - ata_host_resume(host); - - return 0; -} - -static struct dev_pm_ops ahci_pm_ops = { - .suspend = &ahci_suspend, - .resume = &ahci_resume, -}; -#endif +static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, + ahci_platform_resume); static const struct of_device_id ahci_of_match[] = { - { .compatible = "calxeda,hb-ahci", }, + { .compatible = "snps,spear-ahci", }, + { .compatible = "snps,exynos5440-ahci", }, + { .compatible = "ibm,476gtr-ahci", }, + { .compatible = "snps,dwc-ahci", }, + { .compatible = "hisilicon,hisi-ahci", }, {}, }; MODULE_DEVICE_TABLE(of, ahci_of_match); static struct platform_driver ahci_driver = { - .remove = __devexit_p(ahci_remove), + .probe = ahci_probe, + .remove = ata_platform_remove_one, .driver = { .name = "ahci", .owner = THIS_MODULE, .of_match_table = ahci_of_match, -#ifdef CONFIG_PM .pm = &ahci_pm_ops, -#endif }, - .id_table = ahci_devtype, }; - -static int __init ahci_init(void) -{ - return platform_driver_probe(&ahci_driver, ahci_probe); -} -module_init(ahci_init); - -static void __exit ahci_exit(void) -{ - platform_driver_unregister(&ahci_driver); -} -module_exit(ahci_exit); +module_platform_driver(ahci_driver); MODULE_DESCRIPTION("AHCI SATA platform driver"); MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c new file mode 100644 index 00000000000..2595598df9c --- /dev/null +++ b/drivers/ata/ahci_st.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2012 STMicroelectronics Limited + * + * Authors: Francesco Virlinzi <francesco.virlinzi@st.com> + * Alexandre Torgue <alexandre.torgue@st.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/ahci_platform.h> +#include <linux/libata.h> +#include <linux/reset.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> + +#include "ahci.h" + +#define ST_AHCI_OOBR 0xbc +#define ST_AHCI_OOBR_WE BIT(31) +#define ST_AHCI_OOBR_CWMIN_SHIFT 24 +#define ST_AHCI_OOBR_CWMAX_SHIFT 16 +#define ST_AHCI_OOBR_CIMIN_SHIFT 8 +#define ST_AHCI_OOBR_CIMAX_SHIFT 0 + +struct st_ahci_drv_data { + struct platform_device *ahci; + struct reset_control *pwr; + struct reset_control *sw_rst; + struct reset_control *pwr_rst; + struct ahci_host_priv *hpriv; +}; + +static void st_ahci_configure_oob(void __iomem *mmio) +{ + unsigned long old_val, new_val; + + new_val = (0x02 << ST_AHCI_OOBR_CWMIN_SHIFT) | + (0x04 << ST_AHCI_OOBR_CWMAX_SHIFT) | + (0x08 << ST_AHCI_OOBR_CIMIN_SHIFT) | + (0x0C << ST_AHCI_OOBR_CIMAX_SHIFT); + + old_val = readl(mmio + ST_AHCI_OOBR); + writel(old_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR); + writel(new_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR); + writel(new_val, mmio + ST_AHCI_OOBR); +} + +static int st_ahci_deassert_resets(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + int err; + + if (drv_data->pwr) { + err = reset_control_deassert(drv_data->pwr); + if (err) { + dev_err(dev, "unable to bring out of pwrdwn\n"); + return err; + } + } + + st_ahci_configure_oob(drv_data->hpriv->mmio); + + if (drv_data->sw_rst) { + err = reset_control_deassert(drv_data->sw_rst); + if (err) { + dev_err(dev, "unable to bring out of sw-rst\n"); + return err; + } + } + + if (drv_data->pwr_rst) { + err = reset_control_deassert(drv_data->pwr_rst); + if (err) { + dev_err(dev, "unable to bring out of pwr-rst\n"); + return err; + } + } + + return 0; +} + +static void st_ahci_host_stop(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + struct device *dev = host->dev; + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + int err; + + if (drv_data->pwr) { + err = reset_control_assert(drv_data->pwr); + if (err) + dev_err(dev, "unable to pwrdwn\n"); + } + + ahci_platform_disable_resources(hpriv); +} + +static int st_ahci_probe_resets(struct platform_device *pdev) +{ + struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev); + + drv_data->pwr = devm_reset_control_get(&pdev->dev, "pwr-dwn"); + if (IS_ERR(drv_data->pwr)) { + dev_info(&pdev->dev, "power reset control not defined\n"); + drv_data->pwr = NULL; + } + + drv_data->sw_rst = devm_reset_control_get(&pdev->dev, "sw-rst"); + if (IS_ERR(drv_data->sw_rst)) { + dev_info(&pdev->dev, "soft reset control not defined\n"); + drv_data->sw_rst = NULL; + } + + drv_data->pwr_rst = devm_reset_control_get(&pdev->dev, "pwr-rst"); + if (IS_ERR(drv_data->pwr_rst)) { + dev_dbg(&pdev->dev, "power soft reset control not defined\n"); + drv_data->pwr_rst = NULL; + } + + return st_ahci_deassert_resets(&pdev->dev); +} + +static struct ata_port_operations st_ahci_port_ops = { + .inherits = &ahci_platform_ops, + .host_stop = st_ahci_host_stop, +}; + +static const struct ata_port_info st_ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &st_ahci_port_ops, +}; + +static int st_ahci_probe(struct platform_device *pdev) +{ + struct st_ahci_drv_data *drv_data; + struct ahci_host_priv *hpriv; + int err; + + drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + platform_set_drvdata(pdev, drv_data); + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + drv_data->hpriv = hpriv; + + err = st_ahci_probe_resets(pdev); + if (err) + return err; + + err = ahci_platform_enable_resources(hpriv); + if (err) + return err; + + err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, 0, 0, 0); + if (err) { + ahci_platform_disable_resources(hpriv); + return err; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st_ahci_suspend(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = drv_data->hpriv; + int err; + + err = ahci_platform_suspend_host(dev); + if (err) + return err; + + if (drv_data->pwr) { + err = reset_control_assert(drv_data->pwr); + if (err) { + dev_err(dev, "unable to pwrdwn"); + return err; + } + } + + ahci_platform_disable_resources(hpriv); + + return 0; +} + +static int st_ahci_resume(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = drv_data->hpriv; + int err; + + err = ahci_platform_enable_resources(hpriv); + if (err) + return err; + + err = st_ahci_deassert_resets(dev); + if (err) { + ahci_platform_disable_resources(hpriv); + return err; + } + + return ahci_platform_resume_host(dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume); + +static struct of_device_id st_ahci_match[] = { + { .compatible = "st,ahci", }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_ahci_match); + +static struct platform_driver st_ahci_driver = { + .driver = { + .name = "st_ahci", + .owner = THIS_MODULE, + .pm = &st_ahci_pm_ops, + .of_match_table = of_match_ptr(st_ahci_match), + }, + .probe = st_ahci_probe, + .remove = ata_platform_remove_one, +}; +module_platform_driver(st_ahci_driver); + +MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>"); +MODULE_AUTHOR("Francesco Virlinzi <francesco.virlinzi@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics SATA AHCI Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c new file mode 100644 index 00000000000..02002f125bd --- /dev/null +++ b/drivers/ata/ahci_sunxi.c @@ -0,0 +1,252 @@ +/* + * Allwinner sunxi AHCI SATA platform driver + * Copyright 2013 Olliver Schinagl <oliver@schinagl.nl> + * Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * + * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov + * Based on code from Allwinner Technology Co., Ltd. <www.allwinnertech.com>, + * Daniel Wang <danielwang@allwinnertech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/ahci_platform.h> +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include "ahci.h" + +#define AHCI_BISTAFR 0x00a0 +#define AHCI_BISTCR 0x00a4 +#define AHCI_BISTFCTR 0x00a8 +#define AHCI_BISTSR 0x00ac +#define AHCI_BISTDECR 0x00b0 +#define AHCI_DIAGNR0 0x00b4 +#define AHCI_DIAGNR1 0x00b8 +#define AHCI_OOBR 0x00bc +#define AHCI_PHYCS0R 0x00c0 +#define AHCI_PHYCS1R 0x00c4 +#define AHCI_PHYCS2R 0x00c8 +#define AHCI_TIMER1MS 0x00e0 +#define AHCI_GPARAM1R 0x00e8 +#define AHCI_GPARAM2R 0x00ec +#define AHCI_PPARAMR 0x00f0 +#define AHCI_TESTR 0x00f4 +#define AHCI_VERSIONR 0x00f8 +#define AHCI_IDR 0x00fc +#define AHCI_RWCR 0x00fc +#define AHCI_P0DMACR 0x0170 +#define AHCI_P0PHYCR 0x0178 +#define AHCI_P0PHYSR 0x017c + +static void sunxi_clrbits(void __iomem *reg, u32 clr_val) +{ + u32 reg_val; + + reg_val = readl(reg); + reg_val &= ~(clr_val); + writel(reg_val, reg); +} + +static void sunxi_setbits(void __iomem *reg, u32 set_val) +{ + u32 reg_val; + + reg_val = readl(reg); + reg_val |= set_val; + writel(reg_val, reg); +} + +static void sunxi_clrsetbits(void __iomem *reg, u32 clr_val, u32 set_val) +{ + u32 reg_val; + + reg_val = readl(reg); + reg_val &= ~(clr_val); + reg_val |= set_val; + writel(reg_val, reg); +} + +static u32 sunxi_getbits(void __iomem *reg, u8 mask, u8 shift) +{ + return (readl(reg) >> shift) & mask; +} + +static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base) +{ + u32 reg_val; + int timeout; + + /* This magic is from the original code */ + writel(0, reg_base + AHCI_RWCR); + msleep(5); + + sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, + (0x7 << 24), + (0x5 << 24) | BIT(23) | BIT(18)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS1R, + (0x3 << 16) | (0x1f << 8) | (0x3 << 6), + (0x2 << 16) | (0x6 << 8) | (0x2 << 6)); + sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(28) | BIT(15)); + sunxi_clrbits(reg_base + AHCI_PHYCS1R, BIT(19)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, + (0x7 << 20), (0x3 << 20)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS2R, + (0x1f << 5), (0x19 << 5)); + msleep(5); + + sunxi_setbits(reg_base + AHCI_PHYCS0R, (0x1 << 19)); + + timeout = 250; /* Power up takes aprox 50 us */ + do { + reg_val = sunxi_getbits(reg_base + AHCI_PHYCS0R, 0x7, 28); + if (reg_val == 0x02) + break; + + if (--timeout == 0) { + dev_err(dev, "PHY power up failed.\n"); + return -EIO; + } + udelay(1); + } while (1); + + sunxi_setbits(reg_base + AHCI_PHYCS2R, (0x1 << 24)); + + timeout = 100; /* Calibration takes aprox 10 us */ + do { + reg_val = sunxi_getbits(reg_base + AHCI_PHYCS2R, 0x1, 24); + if (reg_val == 0x00) + break; + + if (--timeout == 0) { + dev_err(dev, "PHY calibration failed.\n"); + return -EIO; + } + udelay(1); + } while (1); + + msleep(15); + + writel(0x7, reg_base + AHCI_RWCR); + + return 0; +} + +static void ahci_sunxi_start_engine(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_host_priv *hpriv = ap->host->private_data; + + /* Setup DMA before DMA start */ + sunxi_clrsetbits(hpriv->mmio + AHCI_P0DMACR, 0x0000ff00, 0x00004400); + + /* Start DMA */ + sunxi_setbits(port_mmio + PORT_CMD, PORT_CMD_START); +} + +static const struct ata_port_info ahci_sunxi_port_info = { + .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, +}; + +static int ahci_sunxi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + unsigned long hflags; + int rc; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + hpriv->start_engine = ahci_sunxi_start_engine; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + rc = ahci_sunxi_phy_init(dev, hpriv->mmio); + if (rc) + goto disable_resources; + + hflags = AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | + AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, + hflags, 0, 0); + if (rc) + goto disable_resources; + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +#ifdef CONFIG_PM_SLEEP +static int ahci_sunxi_resume(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + rc = ahci_sunxi_phy_init(dev, hpriv->mmio); + if (rc) + goto disable_resources; + + rc = ahci_platform_resume_host(dev); + if (rc) + goto disable_resources; + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} +#endif + +static SIMPLE_DEV_PM_OPS(ahci_sunxi_pm_ops, ahci_platform_suspend, + ahci_sunxi_resume); + +static const struct of_device_id ahci_sunxi_of_match[] = { + { .compatible = "allwinner,sun4i-a10-ahci", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ahci_sunxi_of_match); + +static struct platform_driver ahci_sunxi_driver = { + .probe = ahci_sunxi_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = "ahci-sunxi", + .owner = THIS_MODULE, + .of_match_table = ahci_sunxi_of_match, + .pm = &ahci_sunxi_pm_ops, + }, +}; +module_platform_driver(ahci_sunxi_driver); + +MODULE_DESCRIPTION("Allwinner sunxi AHCI SATA driver"); +MODULE_AUTHOR("Olliver Schinagl <oliver@schinagl.nl>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c new file mode 100644 index 00000000000..ee3a3659bd9 --- /dev/null +++ b/drivers/ata/ahci_xgene.c @@ -0,0 +1,523 @@ +/* + * AppliedMicro X-Gene SoC SATA Host Controller Driver + * + * Copyright (c) 2014, Applied Micro Circuits Corporation + * Author: Loc Ho <lho@apm.com> + * Tuan Phan <tphan@apm.com> + * Suman Tripathi <stripathi@apm.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * NOTE: PM support is not currently available. + * + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/ahci_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/phy/phy.h> +#include "ahci.h" + +/* Max # of disk per a controller */ +#define MAX_AHCI_CHN_PERCTR 2 + +/* MUX CSR */ +#define SATA_ENET_CONFIG_REG 0x00000000 +#define CFG_SATA_ENET_SELECT_MASK 0x00000001 + +/* SATA core host controller CSR */ +#define SLVRDERRATTRIBUTES 0x00000000 +#define SLVWRERRATTRIBUTES 0x00000004 +#define MSTRDERRATTRIBUTES 0x00000008 +#define MSTWRERRATTRIBUTES 0x0000000c +#define BUSCTLREG 0x00000014 +#define IOFMSTRWAUX 0x00000018 +#define INTSTATUSMASK 0x0000002c +#define ERRINTSTATUS 0x00000030 +#define ERRINTSTATUSMASK 0x00000034 + +/* SATA host AHCI CSR */ +#define PORTCFG 0x000000a4 +#define PORTADDR_SET(dst, src) \ + (((dst) & ~0x0000003f) | (((u32)(src)) & 0x0000003f)) +#define PORTPHY1CFG 0x000000a8 +#define PORTPHY1CFG_FRCPHYRDY_SET(dst, src) \ + (((dst) & ~0x00100000) | (((u32)(src) << 0x14) & 0x00100000)) +#define PORTPHY2CFG 0x000000ac +#define PORTPHY3CFG 0x000000b0 +#define PORTPHY4CFG 0x000000b4 +#define PORTPHY5CFG 0x000000b8 +#define SCTL0 0x0000012C +#define PORTPHY5CFG_RTCHG_SET(dst, src) \ + (((dst) & ~0xfff00000) | (((u32)(src) << 0x14) & 0xfff00000)) +#define PORTAXICFG_EN_CONTEXT_SET(dst, src) \ + (((dst) & ~0x01000000) | (((u32)(src) << 0x18) & 0x01000000)) +#define PORTAXICFG 0x000000bc +#define PORTAXICFG_OUTTRANS_SET(dst, src) \ + (((dst) & ~0x00f00000) | (((u32)(src) << 0x14) & 0x00f00000)) + +/* SATA host controller AXI CSR */ +#define INT_SLV_TMOMASK 0x00000010 + +/* SATA diagnostic CSR */ +#define CFG_MEM_RAM_SHUTDOWN 0x00000070 +#define BLOCK_MEM_RDY 0x00000074 + +struct xgene_ahci_context { + struct ahci_host_priv *hpriv; + struct device *dev; + u8 last_cmd[MAX_AHCI_CHN_PERCTR]; /* tracking the last command issued*/ + void __iomem *csr_core; /* Core CSR address of IP */ + void __iomem *csr_diag; /* Diag CSR address of IP */ + void __iomem *csr_axi; /* AXI CSR address of IP */ + void __iomem *csr_mux; /* MUX CSR address of IP */ +}; + +static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx) +{ + dev_dbg(ctx->dev, "Release memory from shutdown\n"); + writel(0x0, ctx->csr_diag + CFG_MEM_RAM_SHUTDOWN); + readl(ctx->csr_diag + CFG_MEM_RAM_SHUTDOWN); /* Force a barrier */ + msleep(1); /* reset may take up to 1ms */ + if (readl(ctx->csr_diag + BLOCK_MEM_RDY) != 0xFFFFFFFF) { + dev_err(ctx->dev, "failed to release memory from shutdown\n"); + return -ENODEV; + } + return 0; +} + +/** + * xgene_ahci_restart_engine - Restart the dma engine. + * @ap : ATA port of interest + * + * Restarts the dma engine inside the controller. + */ +static int xgene_ahci_restart_engine(struct ata_port *ap) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + + ahci_stop_engine(ap); + ahci_start_fis_rx(ap); + hpriv->start_engine(ap); + + return 0; +} + +/** + * xgene_ahci_qc_issue - Issue commands to the device + * @qc: Command to issue + * + * Due to Hardware errata for IDENTIFY DEVICE command, the controller cannot + * clear the BSY bit after receiving the PIO setup FIS. This results in the dma + * state machine goes into the CMFatalErrorUpdate state and locks up. By + * restarting the dma engine, it removes the controller out of lock up state. + */ +static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + struct xgene_ahci_context *ctx = hpriv->plat_data; + int rc = 0; + + if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA)) + xgene_ahci_restart_engine(ap); + + rc = ahci_qc_issue(qc); + + /* Save the last command issued */ + ctx->last_cmd[ap->port_no] = qc->tf.command; + + return rc; +} + +/** + * xgene_ahci_read_id - Read ID data from the specified device + * @dev: device + * @tf: proposed taskfile + * @id: data buffer + * + * This custom read ID function is required due to the fact that the HW + * does not support DEVSLP. + */ +static unsigned int xgene_ahci_read_id(struct ata_device *dev, + struct ata_taskfile *tf, u16 *id) +{ + u32 err_mask; + + err_mask = ata_do_dev_read_id(dev, tf, id); + if (err_mask) + return err_mask; + + /* + * Mask reserved area. Word78 spec of Link Power Management + * bit15-8: reserved + * bit7: NCQ autosence + * bit6: Software settings preservation supported + * bit5: reserved + * bit4: In-order sata delivery supported + * bit3: DIPM requests supported + * bit2: DMA Setup FIS Auto-Activate optimization supported + * bit1: DMA Setup FIX non-Zero buffer offsets supported + * bit0: Reserved + * + * Clear reserved bit 8 (DEVSLP bit) as we don't support DEVSLP + */ + id[ATA_ID_FEATURE_SUPP] &= ~(1 << 8); + + return 0; +} + +static void xgene_ahci_set_phy_cfg(struct xgene_ahci_context *ctx, int channel) +{ + void __iomem *mmio = ctx->hpriv->mmio; + u32 val; + + dev_dbg(ctx->dev, "port configure mmio 0x%p channel %d\n", + mmio, channel); + val = readl(mmio + PORTCFG); + val = PORTADDR_SET(val, channel == 0 ? 2 : 3); + writel(val, mmio + PORTCFG); + readl(mmio + PORTCFG); /* Force a barrier */ + /* Disable fix rate */ + writel(0x0001fffe, mmio + PORTPHY1CFG); + readl(mmio + PORTPHY1CFG); /* Force a barrier */ + writel(0x5018461c, mmio + PORTPHY2CFG); + readl(mmio + PORTPHY2CFG); /* Force a barrier */ + writel(0x1c081907, mmio + PORTPHY3CFG); + readl(mmio + PORTPHY3CFG); /* Force a barrier */ + writel(0x1c080815, mmio + PORTPHY4CFG); + readl(mmio + PORTPHY4CFG); /* Force a barrier */ + /* Set window negotiation */ + val = readl(mmio + PORTPHY5CFG); + val = PORTPHY5CFG_RTCHG_SET(val, 0x300); + writel(val, mmio + PORTPHY5CFG); + readl(mmio + PORTPHY5CFG); /* Force a barrier */ + val = readl(mmio + PORTAXICFG); + val = PORTAXICFG_EN_CONTEXT_SET(val, 0x1); /* Enable context mgmt */ + val = PORTAXICFG_OUTTRANS_SET(val, 0xe); /* Set outstanding */ + writel(val, mmio + PORTAXICFG); + readl(mmio + PORTAXICFG); /* Force a barrier */ +} + +/** + * xgene_ahci_do_hardreset - Issue the actual COMRESET + * @link: link to reset + * @deadline: deadline jiffies for the operation + * @online: Return value to indicate if device online + * + * Due to the limitation of the hardware PHY, a difference set of setting is + * required for each supported disk speed - Gen3 (6.0Gbps), Gen2 (3.0Gbps), + * and Gen1 (1.5Gbps). Otherwise during long IO stress test, the PHY will + * report disparity error and etc. In addition, during COMRESET, there can + * be error reported in the register PORT_SCR_ERR. For SERR_DISPARITY and + * SERR_10B_8B_ERR, the PHY receiver line must be reseted. The following + * algorithm is followed to proper configure the hardware PHY during COMRESET: + * + * Alg Part 1: + * 1. Start the PHY at Gen3 speed (default setting) + * 2. Issue the COMRESET + * 3. If no link, go to Alg Part 3 + * 4. If link up, determine if the negotiated speed matches the PHY + * configured speed + * 5. If they matched, go to Alg Part 2 + * 6. If they do not matched and first time, configure the PHY for the linked + * up disk speed and repeat step 2 + * 7. Go to Alg Part 2 + * + * Alg Part 2: + * 1. On link up, if there are any SERR_DISPARITY and SERR_10B_8B_ERR error + * reported in the register PORT_SCR_ERR, then reset the PHY receiver line + * 2. Go to Alg Part 3 + * + * Alg Part 3: + * 1. Clear any pending from register PORT_SCR_ERR. + * + * NOTE: For the initial version, we will NOT support Gen1/Gen2. In addition + * and until the underlying PHY supports an method to reset the receiver + * line, on detection of SERR_DISPARITY or SERR_10B_8B_ERR errors, + * an warning message will be printed. + */ +static int xgene_ahci_do_hardreset(struct ata_link *link, + unsigned long deadline, bool *online) +{ + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + struct xgene_ahci_context *ctx = hpriv->plat_data; + struct ahci_port_priv *pp = ap->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_taskfile tf; + int rc; + u32 val; + + /* clear D2H reception area to properly wait for D2H FIS */ + ata_tf_init(link->device, &tf); + tf.command = ATA_BUSY; + ata_tf_to_fis(&tf, 0, 0, d2h_fis); + rc = sata_link_hardreset(link, timing, deadline, online, + ahci_check_ready); + + val = readl(port_mmio + PORT_SCR_ERR); + if (val & (SERR_DISPARITY | SERR_10B_8B_ERR)) + dev_warn(ctx->dev, "link has error\n"); + + /* clear all errors if any pending */ + val = readl(port_mmio + PORT_SCR_ERR); + writel(val, port_mmio + PORT_SCR_ERR); + + return rc; +} + +static int xgene_ahci_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + bool online; + int rc; + u32 portcmd_saved; + u32 portclb_saved; + u32 portclbhi_saved; + u32 portrxfis_saved; + u32 portrxfishi_saved; + + /* As hardreset resets these CSR, save it to restore later */ + portcmd_saved = readl(port_mmio + PORT_CMD); + portclb_saved = readl(port_mmio + PORT_LST_ADDR); + portclbhi_saved = readl(port_mmio + PORT_LST_ADDR_HI); + portrxfis_saved = readl(port_mmio + PORT_FIS_ADDR); + portrxfishi_saved = readl(port_mmio + PORT_FIS_ADDR_HI); + + ahci_stop_engine(ap); + + rc = xgene_ahci_do_hardreset(link, deadline, &online); + + /* As controller hardreset clears them, restore them */ + writel(portcmd_saved, port_mmio + PORT_CMD); + writel(portclb_saved, port_mmio + PORT_LST_ADDR); + writel(portclbhi_saved, port_mmio + PORT_LST_ADDR_HI); + writel(portrxfis_saved, port_mmio + PORT_FIS_ADDR); + writel(portrxfishi_saved, port_mmio + PORT_FIS_ADDR_HI); + + hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); + + return rc; +} + +static void xgene_ahci_host_stop(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + + ahci_platform_disable_resources(hpriv); +} + +static struct ata_port_operations xgene_ahci_ops = { + .inherits = &ahci_ops, + .host_stop = xgene_ahci_host_stop, + .hardreset = xgene_ahci_hardreset, + .read_id = xgene_ahci_read_id, + .qc_issue = xgene_ahci_qc_issue, +}; + +static const struct ata_port_info xgene_ahci_port_info = { + .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &xgene_ahci_ops, +}; + +static int xgene_ahci_hw_init(struct ahci_host_priv *hpriv) +{ + struct xgene_ahci_context *ctx = hpriv->plat_data; + int i; + int rc; + u32 val; + + /* Remove IP RAM out of shutdown */ + rc = xgene_ahci_init_memram(ctx); + if (rc) + return rc; + + for (i = 0; i < MAX_AHCI_CHN_PERCTR; i++) + xgene_ahci_set_phy_cfg(ctx, i); + + /* AXI disable Mask */ + writel(0xffffffff, hpriv->mmio + HOST_IRQ_STAT); + readl(hpriv->mmio + HOST_IRQ_STAT); /* Force a barrier */ + writel(0, ctx->csr_core + INTSTATUSMASK); + val = readl(ctx->csr_core + INTSTATUSMASK); /* Force a barrier */ + dev_dbg(ctx->dev, "top level interrupt mask 0x%X value 0x%08X\n", + INTSTATUSMASK, val); + + writel(0x0, ctx->csr_core + ERRINTSTATUSMASK); + readl(ctx->csr_core + ERRINTSTATUSMASK); /* Force a barrier */ + writel(0x0, ctx->csr_axi + INT_SLV_TMOMASK); + readl(ctx->csr_axi + INT_SLV_TMOMASK); + + /* Enable AXI Interrupt */ + writel(0xffffffff, ctx->csr_core + SLVRDERRATTRIBUTES); + writel(0xffffffff, ctx->csr_core + SLVWRERRATTRIBUTES); + writel(0xffffffff, ctx->csr_core + MSTRDERRATTRIBUTES); + writel(0xffffffff, ctx->csr_core + MSTWRERRATTRIBUTES); + + /* Enable coherency */ + val = readl(ctx->csr_core + BUSCTLREG); + val &= ~0x00000002; /* Enable write coherency */ + val &= ~0x00000001; /* Enable read coherency */ + writel(val, ctx->csr_core + BUSCTLREG); + + val = readl(ctx->csr_core + IOFMSTRWAUX); + val |= (1 << 3); /* Enable read coherency */ + val |= (1 << 9); /* Enable write coherency */ + writel(val, ctx->csr_core + IOFMSTRWAUX); + val = readl(ctx->csr_core + IOFMSTRWAUX); + dev_dbg(ctx->dev, "coherency 0x%X value 0x%08X\n", + IOFMSTRWAUX, val); + + return rc; +} + +static int xgene_ahci_mux_select(struct xgene_ahci_context *ctx) +{ + u32 val; + + /* Check for optional MUX resource */ + if (IS_ERR(ctx->csr_mux)) + return 0; + + val = readl(ctx->csr_mux + SATA_ENET_CONFIG_REG); + val &= ~CFG_SATA_ENET_SELECT_MASK; + writel(val, ctx->csr_mux + SATA_ENET_CONFIG_REG); + val = readl(ctx->csr_mux + SATA_ENET_CONFIG_REG); + return val & CFG_SATA_ENET_SELECT_MASK ? -1 : 0; +} + +static int xgene_ahci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct xgene_ahci_context *ctx; + struct resource *res; + unsigned long hflags; + int rc; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + hpriv->plat_data = ctx; + ctx->hpriv = hpriv; + ctx->dev = dev; + + /* Retrieve the IP core resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + ctx->csr_core = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->csr_core)) + return PTR_ERR(ctx->csr_core); + + /* Retrieve the IP diagnostic resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + ctx->csr_diag = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->csr_diag)) + return PTR_ERR(ctx->csr_diag); + + /* Retrieve the IP AXI resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + ctx->csr_axi = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->csr_axi)) + return PTR_ERR(ctx->csr_axi); + + /* Retrieve the optional IP mux resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + ctx->csr_mux = devm_ioremap_resource(dev, res); + + dev_dbg(dev, "VAddr 0x%p Mmio VAddr 0x%p\n", ctx->csr_core, + hpriv->mmio); + + /* Select ATA */ + if ((rc = xgene_ahci_mux_select(ctx))) { + dev_err(dev, "SATA mux selection failed error %d\n", rc); + return -ENODEV; + } + + /* Due to errata, HW requires full toggle transition */ + rc = ahci_platform_enable_clks(hpriv); + if (rc) + goto disable_resources; + ahci_platform_disable_clks(hpriv); + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + goto disable_resources; + + /* Configure the host controller */ + xgene_ahci_hw_init(hpriv); + + /* + * Setup DMA mask. This is preliminary until the DMA range is sorted + * out. + */ + rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (rc) { + dev_err(dev, "Unable to set dma mask\n"); + goto disable_resources; + } + + hflags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; + + rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info, + hflags, 0, 0); + if (rc) + goto disable_resources; + + dev_dbg(dev, "X-Gene SATA host controller initialized\n"); + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +static const struct of_device_id xgene_ahci_of_match[] = { + {.compatible = "apm,xgene-ahci"}, + {}, +}; +MODULE_DEVICE_TABLE(of, xgene_ahci_of_match); + +static struct platform_driver xgene_ahci_driver = { + .probe = xgene_ahci_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = "xgene-ahci", + .owner = THIS_MODULE, + .of_match_table = xgene_ahci_of_match, + }, +}; + +module_platform_driver(xgene_ahci_driver); + +MODULE_DESCRIPTION("APM X-Gene AHCI SATA driver"); +MODULE_AUTHOR("Loc Ho <lho@apm.com>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.4"); diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c index 7df56ec3181..9ff545ce8da 100644 --- a/drivers/ata/ata_generic.c +++ b/drivers/ata/ata_generic.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -177,7 +176,7 @@ static int ata_generic_init_one(struct pci_dev *dev, const struct pci_device_id if ((id->driver_data & ATA_GEN_CLASS_MATCH) && all_generic_ide == 0) return -ENODEV; - if (id->driver_data & ATA_GEN_INTEL_IDER) + if ((id->driver_data & ATA_GEN_INTEL_IDER) && !all_generic_ide) if (!is_intel_ider(dev)) return -ENODEV; @@ -221,13 +220,6 @@ static struct pci_device_id ata_generic[] = { { PCI_DEVICE(PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558), }, { PCI_DEVICE(PCI_VENDOR_ID_CENATEK,PCI_DEVICE_ID_CENATEK_IDE), .driver_data = ATA_GEN_FORCE_DMA }, - /* - * For some reason, MCP89 on MacBook 7,1 doesn't work with - * ahci, use ata_generic instead. - */ - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA, - PCI_VENDOR_ID_APPLE, 0xcb89, - .driver_data = ATA_GEN_FORCE_DMA }, #if !defined(CONFIG_PATA_TOSHIBA) && !defined(CONFIG_PATA_TOSHIBA_MODULE) { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), }, { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), }, @@ -249,23 +241,13 @@ static struct pci_driver ata_generic_pci_driver = { .id_table = ata_generic, .probe = ata_generic_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init ata_generic_init(void) -{ - return pci_register_driver(&ata_generic_pci_driver); -} - - -static void __exit ata_generic_exit(void) -{ - pci_unregister_driver(&ata_generic_pci_driver); -} - +module_pci_driver(ata_generic_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for generic ATA"); @@ -273,7 +255,4 @@ MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, ata_generic); MODULE_VERSION(DRV_VERSION); -module_init(ata_generic_init); -module_exit(ata_generic_exit); - module_param(all_generic_ide, int, 0); diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index fdf27b9fce4..893e30e9a9e 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -1,7 +1,7 @@ /* * ata_piix.c - Intel PATA/SATA controllers * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -100,7 +100,7 @@ enum { PIIX_IOCFG = 0x54, /* IDE I/O configuration register */ - ICH5_PMR = 0x90, /* port mapping register */ + ICH5_PMR = 0x90, /* address map register */ ICH5_PCS = 0x92, /* port control and status */ PIIX_SIDPR_BAR = 5, PIIX_SIDPR_LEN = 16, @@ -150,6 +150,8 @@ enum piix_controller_ids { tolapai_sata, piix_pata_vmw, /* PIIX4 for VMware, spurious DMA_ERR */ ich8_sata_snb, + ich8_2port_sata_snb, + ich8_2port_sata_byt, }; struct piix_map_db { @@ -164,28 +166,6 @@ struct piix_host_priv { void __iomem *sidpr; }; -static int piix_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent); -static void piix_remove_one(struct pci_dev *pdev); -static int piix_pata_prereset(struct ata_link *link, unsigned long deadline); -static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev); -static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev); -static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev); -static int ich_pata_cable_detect(struct ata_port *ap); -static u8 piix_vmw_bmdma_status(struct ata_port *ap); -static int piix_sidpr_scr_read(struct ata_link *link, - unsigned int reg, u32 *val); -static int piix_sidpr_scr_write(struct ata_link *link, - unsigned int reg, u32 val); -static int piix_sidpr_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, - unsigned hints); -static bool piix_irq_check(struct ata_port *ap); -static int piix_port_start(struct ata_port *ap); -#ifdef CONFIG_PM -static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); -static int piix_pci_device_resume(struct pci_dev *pdev); -#endif - static unsigned int in_module_init = 1; static const struct pci_device_id piix_pci_tbl[] = { @@ -253,7 +233,7 @@ static const struct pci_device_id piix_pci_tbl[] = { PCI_CLASS_STORAGE_IDE << 8, 0xffff00, ich6m_sata }, /* 82801GB/GR/GH (ICH7, identical to ICH6) */ { 0x8086, 0x27c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata }, - /* 2801GBM/GHM (ICH7M, identical to ICH6M) */ + /* 82801GBM/GHM (ICH7M, identical to ICH6M) */ { 0x8086, 0x27c4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6m_sata }, /* Enterprise Southbridge 2 (631xESB/632xESB) */ { 0x8086, 0x2680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata }, @@ -321,65 +301,47 @@ static const struct pci_device_id piix_pci_tbl[] = { { 0x8086, 0x1e08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, /* SATA Controller IDE (Panther Point) */ { 0x8086, 0x1e09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, - { } /* terminate list */ -}; - -static struct pci_driver piix_pci_driver = { - .name = DRV_NAME, - .id_table = piix_pci_tbl, - .probe = piix_init_one, - .remove = piix_remove_one, -#ifdef CONFIG_PM - .suspend = piix_pci_device_suspend, - .resume = piix_pci_device_resume, -#endif -}; - -static struct scsi_host_template piix_sht = { - ATA_BMDMA_SHT(DRV_NAME), -}; - -static struct ata_port_operations piix_sata_ops = { - .inherits = &ata_bmdma32_port_ops, - .sff_irq_check = piix_irq_check, - .port_start = piix_port_start, -}; + /* SATA Controller IDE (Lynx Point) */ + { 0x8086, 0x8c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Lynx Point) */ + { 0x8086, 0x8c01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Lynx Point) */ + { 0x8086, 0x8c08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata_snb }, + /* SATA Controller IDE (Lynx Point) */ + { 0x8086, 0x8c09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (Lynx Point-LP) */ + { 0x8086, 0x9c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Lynx Point-LP) */ + { 0x8086, 0x9c01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Lynx Point-LP) */ + { 0x8086, 0x9c08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (Lynx Point-LP) */ + { 0x8086, 0x9c09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (DH89xxCC) */ + { 0x8086, 0x2326, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (Avoton) */ + { 0x8086, 0x1f20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Avoton) */ + { 0x8086, 0x1f21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Avoton) */ + { 0x8086, 0x1f30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (Avoton) */ + { 0x8086, 0x1f31, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (Wellsburg) */ + { 0x8086, 0x8d00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Wellsburg) */ + { 0x8086, 0x8d08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata_snb }, + /* SATA Controller IDE (Wellsburg) */ + { 0x8086, 0x8d60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb }, + /* SATA Controller IDE (Wellsburg) */ + { 0x8086, 0x8d68, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, + /* SATA Controller IDE (BayTrail) */ + { 0x8086, 0x0F20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata_byt }, + { 0x8086, 0x0F21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata_byt }, + /* SATA Controller IDE (Coleto Creek) */ + { 0x8086, 0x23a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata }, -static struct ata_port_operations piix_pata_ops = { - .inherits = &piix_sata_ops, - .cable_detect = ata_cable_40wire, - .set_piomode = piix_set_piomode, - .set_dmamode = piix_set_dmamode, - .prereset = piix_pata_prereset, -}; - -static struct ata_port_operations piix_vmw_ops = { - .inherits = &piix_pata_ops, - .bmdma_status = piix_vmw_bmdma_status, -}; - -static struct ata_port_operations ich_pata_ops = { - .inherits = &piix_pata_ops, - .cable_detect = ich_pata_cable_detect, - .set_dmamode = ich_set_dmamode, -}; - -static struct device_attribute *piix_sidpr_shost_attrs[] = { - &dev_attr_link_power_management_policy, - NULL -}; - -static struct scsi_host_template piix_sidpr_sht = { - ATA_BMDMA_SHT(DRV_NAME), - .shost_attrs = piix_sidpr_shost_attrs, -}; - -static struct ata_port_operations piix_sidpr_sata_ops = { - .inherits = &piix_sata_ops, - .hardreset = sata_std_hardreset, - .scr_read = piix_sidpr_scr_read, - .scr_write = piix_sidpr_scr_write, - .set_lpm = piix_sidpr_set_lpm, + { } /* terminate list */ }; static const struct piix_map_db ich5_map_db = { @@ -484,147 +446,8 @@ static const struct piix_map_db *piix_map_db_table[] = { [ich8m_apple_sata] = &ich8m_apple_map_db, [tolapai_sata] = &tolapai_map_db, [ich8_sata_snb] = &ich8_map_db, -}; - -static struct ata_port_info piix_port_info[] = { - [piix_pata_mwdma] = /* PIIX3 MWDMA only */ - { - .flags = PIIX_PATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */ - .port_ops = &piix_pata_ops, - }, - - [piix_pata_33] = /* PIIX4 at 33MHz */ - { - .flags = PIIX_PATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */ - .udma_mask = ATA_UDMA2, - .port_ops = &piix_pata_ops, - }, - - [ich_pata_33] = /* ICH0 - ICH at 33Mhz*/ - { - .flags = PIIX_PATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA12_ONLY, /* Check: maybe MWDMA0 is ok */ - .udma_mask = ATA_UDMA2, - .port_ops = &ich_pata_ops, - }, - - [ich_pata_66] = /* ICH controllers up to 66MHz */ - { - .flags = PIIX_PATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA12_ONLY, /* MWDMA0 is broken on chip */ - .udma_mask = ATA_UDMA4, - .port_ops = &ich_pata_ops, - }, - - [ich_pata_100] = - { - .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA12_ONLY, - .udma_mask = ATA_UDMA5, - .port_ops = &ich_pata_ops, - }, - - [ich_pata_100_nomwdma1] = - { - .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2_ONLY, - .udma_mask = ATA_UDMA5, - .port_ops = &ich_pata_ops, - }, - - [ich5_sata] = - { - .flags = PIIX_SATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &piix_sata_ops, - }, - - [ich6_sata] = - { - .flags = PIIX_SATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &piix_sata_ops, - }, - - [ich6m_sata] = - { - .flags = PIIX_SATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &piix_sata_ops, - }, - - [ich8_sata] = - { - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &piix_sata_ops, - }, - - [ich8_2port_sata] = - { - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &piix_sata_ops, - }, - - [tolapai_sata] = - { - .flags = PIIX_SATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &piix_sata_ops, - }, - - [ich8m_apple_sata] = - { - .flags = PIIX_SATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &piix_sata_ops, - }, - - [piix_pata_vmw] = - { - .flags = PIIX_PATA_FLAGS, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */ - .udma_mask = ATA_UDMA2, - .port_ops = &piix_vmw_ops, - }, - - /* - * some Sandybridge chipsets have broken 32 mode up to now, - * see https://bugzilla.kernel.org/show_bug.cgi?id=40592 - */ - [ich8_sata_snb] = - { - .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR | PIIX_FLAG_PIO16, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &piix_sata_ops, - }, - + [ich8_2port_sata_snb] = &ich8_2port_map_db, + [ich8_2port_sata_byt] = &ich8_2port_map_db, }; static struct pci_bits piix_enable_bits[] = { @@ -694,7 +517,7 @@ static int ich_pata_cable_detect(struct ata_port *ap) const struct ich_laptop *lap = &ich_laptop[0]; u8 mask; - /* Check for specials - Acer Aspire 5602WLMi */ + /* Check for specials */ while (lap->device) { if (lap->device == pdev->device && lap->subvendor == pdev->subsystem_vendor && @@ -1007,7 +830,7 @@ static bool piix_irq_check(struct ata_port *ap) return ap->ops->bmdma_status(ap) & ATA_DMA_INTR; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int piix_broken_suspend(void) { static const struct dmi_system_id sysids[] = { @@ -1172,7 +995,7 @@ static int piix_broken_suspend(void) static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); unsigned long flags; int rc = 0; @@ -1207,7 +1030,7 @@ static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) static int piix_pci_device_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); unsigned long flags; int rc; @@ -1243,6 +1066,213 @@ static u8 piix_vmw_bmdma_status(struct ata_port *ap) return ata_bmdma_status(ap) & ~ATA_DMA_ERR; } +static struct scsi_host_template piix_sht = { + ATA_BMDMA_SHT(DRV_NAME), +}; + +static struct ata_port_operations piix_sata_ops = { + .inherits = &ata_bmdma32_port_ops, + .sff_irq_check = piix_irq_check, + .port_start = piix_port_start, +}; + +static struct ata_port_operations piix_pata_ops = { + .inherits = &piix_sata_ops, + .cable_detect = ata_cable_40wire, + .set_piomode = piix_set_piomode, + .set_dmamode = piix_set_dmamode, + .prereset = piix_pata_prereset, +}; + +static struct ata_port_operations piix_vmw_ops = { + .inherits = &piix_pata_ops, + .bmdma_status = piix_vmw_bmdma_status, +}; + +static struct ata_port_operations ich_pata_ops = { + .inherits = &piix_pata_ops, + .cable_detect = ich_pata_cable_detect, + .set_dmamode = ich_set_dmamode, +}; + +static struct device_attribute *piix_sidpr_shost_attrs[] = { + &dev_attr_link_power_management_policy, + NULL +}; + +static struct scsi_host_template piix_sidpr_sht = { + ATA_BMDMA_SHT(DRV_NAME), + .shost_attrs = piix_sidpr_shost_attrs, +}; + +static struct ata_port_operations piix_sidpr_sata_ops = { + .inherits = &piix_sata_ops, + .hardreset = sata_std_hardreset, + .scr_read = piix_sidpr_scr_read, + .scr_write = piix_sidpr_scr_write, + .set_lpm = piix_sidpr_set_lpm, +}; + +static struct ata_port_info piix_port_info[] = { + [piix_pata_mwdma] = /* PIIX3 MWDMA only */ + { + .flags = PIIX_PATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */ + .port_ops = &piix_pata_ops, + }, + + [piix_pata_33] = /* PIIX4 at 33MHz */ + { + .flags = PIIX_PATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */ + .udma_mask = ATA_UDMA2, + .port_ops = &piix_pata_ops, + }, + + [ich_pata_33] = /* ICH0 - ICH at 33Mhz*/ + { + .flags = PIIX_PATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA12_ONLY, /* Check: maybe MWDMA0 is ok */ + .udma_mask = ATA_UDMA2, + .port_ops = &ich_pata_ops, + }, + + [ich_pata_66] = /* ICH controllers up to 66MHz */ + { + .flags = PIIX_PATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA12_ONLY, /* MWDMA0 is broken on chip */ + .udma_mask = ATA_UDMA4, + .port_ops = &ich_pata_ops, + }, + + [ich_pata_100] = + { + .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA12_ONLY, + .udma_mask = ATA_UDMA5, + .port_ops = &ich_pata_ops, + }, + + [ich_pata_100_nomwdma1] = + { + .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2_ONLY, + .udma_mask = ATA_UDMA5, + .port_ops = &ich_pata_ops, + }, + + [ich5_sata] = + { + .flags = PIIX_SATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [ich6_sata] = + { + .flags = PIIX_SATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [ich6m_sata] = + { + .flags = PIIX_SATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [ich8_sata] = + { + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [ich8_2port_sata] = + { + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [tolapai_sata] = + { + .flags = PIIX_SATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [ich8m_apple_sata] = + { + .flags = PIIX_SATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [piix_pata_vmw] = + { + .flags = PIIX_PATA_FLAGS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */ + .udma_mask = ATA_UDMA2, + .port_ops = &piix_vmw_ops, + }, + + /* + * some Sandybridge chipsets have broken 32 mode up to now, + * see https://bugzilla.kernel.org/show_bug.cgi?id=40592 + */ + [ich8_sata_snb] = + { + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR | PIIX_FLAG_PIO16, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [ich8_2port_sata_snb] = + { + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR + | PIIX_FLAG_PIO16, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + + [ich8_2port_sata_byt] = + { + .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR | PIIX_FLAG_PIO16, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + .port_ops = &piix_sata_ops, + }, + +}; + #define AHCI_PCI_BAR 5 #define AHCI_GLOBAL_CTL 0x04 #define AHCI_ENABLE (1 << 31) @@ -1286,7 +1316,7 @@ static int piix_disable_ahci(struct pci_dev *pdev) * they are found return an error code so we can turn off DMA */ -static int __devinit piix_check_450nx_errata(struct pci_dev *ata_dev) +static int piix_check_450nx_errata(struct pci_dev *ata_dev) { struct pci_dev *pdev = NULL; u16 cfg; @@ -1312,8 +1342,8 @@ static int __devinit piix_check_450nx_errata(struct pci_dev *ata_dev) return no_piix_dma; } -static void __devinit piix_init_pcs(struct ata_host *host, - const struct piix_map_db *map_db) +static void piix_init_pcs(struct ata_host *host, + const struct piix_map_db *map_db) { struct pci_dev *pdev = to_pci_dev(host->dev); u16 pcs, new_pcs; @@ -1329,45 +1359,46 @@ static void __devinit piix_init_pcs(struct ata_host *host, } } -static const int *__devinit piix_init_sata_map(struct pci_dev *pdev, - struct ata_port_info *pinfo, - const struct piix_map_db *map_db) +static const int *piix_init_sata_map(struct pci_dev *pdev, + struct ata_port_info *pinfo, + const struct piix_map_db *map_db) { const int *map; int i, invalid_map = 0; u8 map_value; + char buf[32]; + char *p = buf, *end = buf + sizeof(buf); pci_read_config_byte(pdev, ICH5_PMR, &map_value); map = map_db->map[map_value & map_db->mask]; - dev_info(&pdev->dev, "MAP ["); for (i = 0; i < 4; i++) { switch (map[i]) { case RV: invalid_map = 1; - pr_cont(" XX"); + p += scnprintf(p, end - p, " XX"); break; case NA: - pr_cont(" --"); + p += scnprintf(p, end - p, " --"); break; case IDE: WARN_ON((i & 1) || map[i + 1] != IDE); pinfo[i / 2] = piix_port_info[ich_pata_100]; i++; - pr_cont(" IDE IDE"); + p += scnprintf(p, end - p, " IDE IDE"); break; default: - pr_cont(" P%d", map[i]); + p += scnprintf(p, end - p, " P%d", map[i]); if (i & 1) pinfo[i / 2].flags |= ATA_FLAG_SLAVE_POSS; break; } } - pr_cont(" ]\n"); + dev_info(&pdev->dev, "MAP [%s ]\n", buf); if (invalid_map) dev_err(&pdev->dev, "invalid MAP value %u\n", map_value); @@ -1408,7 +1439,7 @@ static bool piix_no_sidpr(struct ata_host *host) return false; } -static int __devinit piix_init_sidpr(struct ata_host *host) +static int piix_init_sidpr(struct ata_host *host) { struct pci_dev *pdev = to_pci_dev(host->dev); struct piix_host_priv *hpriv = host->private_data; @@ -1544,6 +1575,62 @@ static bool piix_broken_system_poweroff(struct pci_dev *pdev) return false; } +static int prefer_ms_hyperv = 1; +module_param(prefer_ms_hyperv, int, 0); +MODULE_PARM_DESC(prefer_ms_hyperv, + "Prefer Hyper-V paravirtualization drivers instead of ATA, " + "0 - Use ATA drivers, " + "1 (Default) - Use the paravirtualization drivers."); + +static void piix_ignore_devices_quirk(struct ata_host *host) +{ +#if IS_ENABLED(CONFIG_HYPERV_STORAGE) + static const struct dmi_system_id ignore_hyperv[] = { + { + /* On Hyper-V hypervisors the disks are exposed on + * both the emulated SATA controller and on the + * paravirtualised drivers. The CD/DVD devices + * are only exposed on the emulated controller. + * Request we ignore ATA devices on this host. + */ + .ident = "Hyper-V Virtual Machine", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), + }, + }, + { } /* terminate list */ + }; + static const struct dmi_system_id allow_virtual_pc[] = { + { + /* In MS Virtual PC guests the DMI ident is nearly + * identical to a Hyper-V guest. One difference is the + * product version which is used here to identify + * a Virtual PC guest. This entry allows ata_piix to + * drive the emulated hardware. + */ + .ident = "MS Virtual PC 2007", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), + DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), + }, + }, + { } /* terminate list */ + }; + const struct dmi_system_id *ignore = dmi_first_match(ignore_hyperv); + const struct dmi_system_id *allow = dmi_first_match(allow_virtual_pc); + + if (ignore && !allow && prefer_ms_hyperv) { + host->flags |= ATA_HOST_IGNORE_ATA; + dev_info(host->dev, "%s detected, ATA device ignore set\n", + ignore->ident); + } +#endif +} + /** * piix_init_one - Register PIIX ATA PCI device with kernel services * @pdev: PCI device to register @@ -1559,8 +1646,7 @@ static bool piix_broken_system_poweroff(struct pci_dev *pdev) * Zero on success, or -ERRNO value. */ -static int __devinit piix_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct device *dev = &pdev->dev; struct ata_port_info port_info[2]; @@ -1659,13 +1745,16 @@ static int __devinit piix_init_one(struct pci_dev *pdev, } host->flags |= ATA_HOST_PARALLEL_SCAN; + /* Allow hosts to specify device types to ignore when scanning. */ + piix_ignore_devices_quirk(host); + pci_set_master(pdev); return ata_pci_sff_activate_host(host, ata_bmdma_interrupt, sht); } static void piix_remove_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); struct piix_host_priv *hpriv = host->private_data; pci_write_config_dword(pdev, PIIX_IOCFG, hpriv->saved_iocfg); @@ -1673,6 +1762,17 @@ static void piix_remove_one(struct pci_dev *pdev) ata_pci_remove_one(pdev); } +static struct pci_driver piix_pci_driver = { + .name = DRV_NAME, + .id_table = piix_pci_tbl, + .probe = piix_init_one, + .remove = piix_remove_one, +#ifdef CONFIG_PM_SLEEP + .suspend = piix_pci_device_suspend, + .resume = piix_pci_device_resume, +#endif +}; + static int __init piix_init(void) { int rc; diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index a72bfd0ecfe..d72ce047030 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1,7 +1,7 @@ /* * libahci.c - Common AHCI SATA low-level routines * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -35,7 +35,6 @@ #include <linux/kernel.h> #include <linux/gfp.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -45,6 +44,7 @@ #include <scsi/scsi_cmnd.h> #include <linux/libata.h> #include "ahci.h" +#include "libata.h" static int ahci_skip_host_reset; int ahci_ignore_sss; @@ -68,7 +68,6 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); -static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); @@ -76,6 +75,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc); static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc); static void ahci_freeze(struct ata_port *ap); static void ahci_thaw(struct ata_port *ap); +static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep); static void ahci_enable_fbs(struct ata_port *ap); static void ahci_disable_fbs(struct ata_port *ap); static void ahci_pmp_attach(struct ata_port *ap); @@ -87,7 +87,6 @@ static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, static int ahci_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); static void ahci_postreset(struct ata_link *link, unsigned int *class); -static void ahci_error_handler(struct ata_port *ap); static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); static void ahci_dev_config(struct ata_device *dev); #ifdef CONFIG_PM @@ -171,6 +170,7 @@ struct ata_port_operations ahci_ops = { .em_store = ahci_led_store, .sw_activity_show = ahci_activity_show, .sw_activity_store = ahci_activity_store, + .transmit_led_message = ahci_transmit_led_message, #ifdef CONFIG_PM .port_suspend = ahci_port_suspend, .port_resume = ahci_port_resume, @@ -186,13 +186,18 @@ struct ata_port_operations ahci_pmp_retry_srst_ops = { }; EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops); -int ahci_em_messages = 1; +static bool ahci_em_messages __read_mostly = true; EXPORT_SYMBOL_GPL(ahci_em_messages); -module_param(ahci_em_messages, int, 0444); +module_param(ahci_em_messages, bool, 0444); /* add other LED protocol types when they become supported */ MODULE_PARM_DESC(ahci_em_messages, "AHCI Enclosure Management Message control (0 = off, 1 = on)"); +/* device sleep idle timeout in ms */ +static int devslp_idle_timeout __read_mostly = 1000; +module_param(devslp_idle_timeout, int, 0644); +MODULE_PARM_DESC(devslp_idle_timeout, "device sleep idle timeout"); + static void ahci_enable_ahci(void __iomem *mmio) { int i; @@ -387,6 +392,9 @@ static ssize_t ahci_show_em_supported(struct device *dev, * * If inconsistent, config values are fixed up by this function. * + * If it is not set already this function sets hpriv->start_engine to + * ahci_start_engine. + * * LOCKING: * None. */ @@ -443,11 +451,23 @@ void ahci_save_initial_config(struct device *dev, cap &= ~HOST_CAP_SNTF; } + if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) { + dev_info(dev, + "controller can't do DEVSLP, turning off\n"); + cap2 &= ~HOST_CAP2_SDS; + cap2 &= ~HOST_CAP2_SADM; + } + if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) { dev_info(dev, "controller can do FBS, turning on CAP_FBS\n"); cap |= HOST_CAP_FBS; } + if ((cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_NO_FBS)) { + dev_info(dev, "controller can't do FBS, turning off CAP_FBS\n"); + cap &= ~HOST_CAP_FBS; + } + if (force_port_map && port_map != force_port_map) { dev_info(dev, "forcing port_map 0x%x -> 0x%x\n", port_map, force_port_map); @@ -493,6 +513,9 @@ void ahci_save_initial_config(struct device *dev, hpriv->cap = cap; hpriv->cap2 = cap2; hpriv->port_map = port_map; + + if (!hpriv->start_engine) + hpriv->start_engine = ahci_start_engine; } EXPORT_SYMBOL_GPL(ahci_save_initial_config); @@ -596,7 +619,7 @@ int ahci_stop_engine(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ahci_stop_engine); -static void ahci_start_fis_rx(struct ata_port *ap) +void ahci_start_fis_rx(struct ata_port *ap) { void __iomem *port_mmio = ahci_port_base(ap); struct ahci_host_priv *hpriv = ap->host->private_data; @@ -622,6 +645,7 @@ static void ahci_start_fis_rx(struct ata_port *ap) /* flush */ readl(port_mmio + PORT_CMD); } +EXPORT_SYMBOL_GPL(ahci_start_fis_rx); static int ahci_stop_fis_rx(struct ata_port *ap) { @@ -702,6 +726,16 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, } } + /* set aggressive device sleep */ + if ((hpriv->cap2 & HOST_CAP2_SDS) && + (hpriv->cap2 & HOST_CAP2_SADM) && + (link->device->flags & ATA_DFLAG_DEVSLP)) { + if (policy == ATA_LPM_MIN_POWER) + ahci_set_aggressive_devslp(ap, true); + else + ahci_set_aggressive_devslp(ap, false); + } + if (policy == ATA_LPM_MAX_POWER) { sata_link_scr_lpm(link, policy, false); @@ -737,6 +771,7 @@ static void ahci_power_down(struct ata_port *ap) static void ahci_start_port(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_port_priv *pp = ap->private_data; struct ata_link *link; struct ahci_em_priv *emp; @@ -746,6 +781,10 @@ static void ahci_start_port(struct ata_port *ap) /* enable FIS reception */ ahci_start_fis_rx(ap); + /* enable DMA */ + if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) + hpriv->start_engine(ap); + /* turn on LEDs */ if (ap->flags & ATA_FLAG_EM) { ata_for_each_link(link, ap, EDGE) { @@ -753,11 +792,19 @@ static void ahci_start_port(struct ata_port *ap) /* EM Transmit bit maybe busy during init */ for (i = 0; i < EM_MAX_RETRY; i++) { - rc = ahci_transmit_led_message(ap, + rc = ap->ops->transmit_led_message(ap, emp->led_state, 4); + /* + * If busy, give a breather but do not + * release EH ownership by using msleep() + * instead of ata_msleep(). EM Transmit + * bit is busy for the whole host and + * releasing ownership will cause other + * ports to fail the same way. + */ if (rc == -EBUSY) - ata_msleep(ap, 1); + msleep(1); else break; } @@ -894,7 +941,7 @@ static void ahci_sw_activity_blink(unsigned long arg) led_message |= (1 << 16); } spin_unlock_irqrestore(ap->lock, flags); - ahci_transmit_led_message(ap, led_message, 4); + ap->ops->transmit_led_message(ap, led_message, 4); } static void ahci_init_sw_activity(struct ata_link *link) @@ -1002,12 +1049,13 @@ static ssize_t ahci_led_show(struct ata_port *ap, char *buf) static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, size_t size) { - int state; + unsigned int state; int pmp; struct ahci_port_priv *pp = ap->private_data; struct ahci_em_priv *emp; - state = simple_strtoul(buf, NULL, 0); + if (kstrtouint(buf, 0, &state) < 0) + return -EINVAL; /* get the slot number from the message */ pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; @@ -1023,7 +1071,7 @@ static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, if (emp->blink_policy) state &= ~EM_MSG_LED_VALUE_ACTIVITY; - return ahci_transmit_led_message(ap, state, size); + return ap->ops->transmit_led_message(ap, state, size); } static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) @@ -1042,7 +1090,7 @@ static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) /* set the LED to OFF */ port_led_state &= EM_MSG_LED_VALUE_OFF; port_led_state |= (ap->port_no | (link->pmp << 8)); - ahci_transmit_led_message(ap, port_led_state, 4); + ap->ops->transmit_led_message(ap, port_led_state, 4); } else { link->flags |= ATA_LFLAG_SW_ACTIVITY; if (val == BLINK_OFF) { @@ -1050,7 +1098,7 @@ static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) port_led_state &= EM_MSG_LED_VALUE_OFF; port_led_state |= (ap->port_no | (link->pmp << 8)); port_led_state |= EM_MSG_LED_VALUE_ON; /* check this */ - ahci_transmit_led_message(ap, port_led_state, 4); + ap->ops->transmit_led_message(ap, port_led_state, 4); } } emp->blink_policy = val; @@ -1134,7 +1182,7 @@ static void ahci_dev_config(struct ata_device *dev) } } -static unsigned int ahci_dev_classify(struct ata_port *ap) +unsigned int ahci_dev_classify(struct ata_port *ap) { void __iomem *port_mmio = ahci_port_base(ap); struct ata_taskfile tf; @@ -1148,6 +1196,7 @@ static unsigned int ahci_dev_classify(struct ata_port *ap) return ata_dev_classify(&tf); } +EXPORT_SYMBOL_GPL(ahci_dev_classify); void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, u32 opts) @@ -1203,7 +1252,7 @@ int ahci_kick_engine(struct ata_port *ap) /* restart engine */ out_restart: - ahci_start_engine(ap); + hpriv->start_engine(ap); return rc; } EXPORT_SYMBOL_GPL(ahci_kick_engine); @@ -1244,9 +1293,11 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, { struct ata_port *ap = link->ap; struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; const char *reason = NULL; unsigned long now, msecs; struct ata_taskfile tf; + bool fbs_disabled = false; int rc; DPRINTK("ENTER\n"); @@ -1256,6 +1307,16 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, if (rc && rc != -EOPNOTSUPP) ata_link_warn(link, "failed to reset engine (errno=%d)\n", rc); + /* + * According to AHCI-1.2 9.3.9: if FBS is enable, software shall + * clear PxFBS.EN to '0' prior to issuing software reset to devices + * that is attached to port multiplier. + */ + if (!ata_is_host_link(link) && pp->fbs_enabled) { + ahci_disable_fbs(ap); + fbs_disabled = true; + } + ata_tf_init(link->device, &tf); /* issue the first D2H Register FIS */ @@ -1296,6 +1357,10 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, } else *class = ahci_dev_classify(ap); + /* re-enable FBS if disabled before */ + if (fbs_disabled) + ahci_enable_fbs(ap); + DPRINTK("EXIT, class=%u\n", *class); return 0; @@ -1340,8 +1405,8 @@ static int ahci_bad_pmp_check_ready(struct ata_link *link) return ata_check_ready(status); } -int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) { struct ata_port *ap = link->ap; void __iomem *port_mmio = ahci_port_base(ap); @@ -1362,7 +1427,7 @@ int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, if (rc == -EIO) { irq_sts = readl(port_mmio + PORT_IRQ_STAT); if (irq_sts & PORT_IRQ_BAD_PMP) { - ata_link_printk(link, KERN_WARNING, + ata_link_warn(link, "applying PMP SRST workaround " "and retrying\n"); rc = ahci_do_softreset(link, class, 0, deadline, @@ -1379,6 +1444,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; bool online; @@ -1390,13 +1456,13 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(link->device, &tf); - tf.command = 0x80; + tf.command = ATA_BUSY; ata_tf_to_fis(&tf, 0, 0, d2h_fis); rc = sata_link_hardreset(link, timing, deadline, &online, ahci_check_ready); - ahci_start_engine(ap); + hpriv->start_engine(ap); if (online) *class = ahci_dev_classify(ap); @@ -1538,8 +1604,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) u32 fbs = readl(port_mmio + PORT_FBS); int pmp = fbs >> PORT_FBS_DWE_OFFSET; - if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links) && - ata_link_online(&ap->pmp_link[pmp])) { + if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links)) { link = &ap->pmp_link[pmp]; fbs_need_dec = true; } @@ -1583,7 +1648,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) } if (irq_stat & PORT_IRQ_UNK_FIS) { - u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK); + u32 *unk = pp->rx_fis + RX_FIS_UNK; active_ehi->err_mask |= AC_ERR_HSM; active_ehi->action |= ATA_EH_RESET; @@ -1633,19 +1698,16 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) ata_port_abort(ap); } -static void ahci_port_intr(struct ata_port *ap) +static void ahci_handle_port_interrupt(struct ata_port *ap, + void __iomem *port_mmio, u32 status) { - void __iomem *port_mmio = ahci_port_base(ap); struct ata_eh_info *ehi = &ap->link.eh_info; struct ahci_port_priv *pp = ap->private_data; struct ahci_host_priv *hpriv = ap->host->private_data; int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING); - u32 status, qc_active = 0; + u32 qc_active = 0; int rc; - status = readl(port_mmio + PORT_IRQ_STAT); - writel(status, port_mmio + PORT_IRQ_STAT); - /* ignore BAD_PMP while resetting */ if (unlikely(resetting)) status &= ~PORT_IRQ_BAD_PMP; @@ -1721,6 +1783,107 @@ static void ahci_port_intr(struct ata_port *ap) } } +static void ahci_port_intr(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + u32 status; + + status = readl(port_mmio + PORT_IRQ_STAT); + writel(status, port_mmio + PORT_IRQ_STAT); + + ahci_handle_port_interrupt(ap, port_mmio, status); +} + +irqreturn_t ahci_thread_fn(int irq, void *dev_instance) +{ + struct ata_port *ap = dev_instance; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + unsigned long flags; + u32 status; + + spin_lock_irqsave(&ap->host->lock, flags); + status = pp->intr_status; + if (status) + pp->intr_status = 0; + spin_unlock_irqrestore(&ap->host->lock, flags); + + spin_lock_bh(ap->lock); + ahci_handle_port_interrupt(ap, port_mmio, status); + spin_unlock_bh(ap->lock); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(ahci_thread_fn); + +static void ahci_hw_port_interrupt(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_port_priv *pp = ap->private_data; + u32 status; + + status = readl(port_mmio + PORT_IRQ_STAT); + writel(status, port_mmio + PORT_IRQ_STAT); + + pp->intr_status |= status; +} + +irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance) +{ + struct ata_port *ap_this = dev_instance; + struct ahci_port_priv *pp = ap_this->private_data; + struct ata_host *host = ap_this->host; + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + unsigned int i; + u32 irq_stat, irq_masked; + + VPRINTK("ENTER\n"); + + spin_lock(&host->lock); + + irq_stat = readl(mmio + HOST_IRQ_STAT); + + if (!irq_stat) { + u32 status = pp->intr_status; + + spin_unlock(&host->lock); + + VPRINTK("EXIT\n"); + + return status ? IRQ_WAKE_THREAD : IRQ_NONE; + } + + irq_masked = irq_stat & hpriv->port_map; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap; + + if (!(irq_masked & (1 << i))) + continue; + + ap = host->ports[i]; + if (ap) { + ahci_hw_port_interrupt(ap); + VPRINTK("port %u\n", i); + } else { + VPRINTK("port %u (no irq)\n", i); + if (ata_ratelimit()) + dev_warn(host->dev, + "interrupt on disabled port %u\n", i); + } + } + + writel(irq_stat, mmio + HOST_IRQ_STAT); + + spin_unlock(&host->lock); + + VPRINTK("EXIT\n"); + + return IRQ_WAKE_THREAD; +} +EXPORT_SYMBOL_GPL(ahci_hw_interrupt); + irqreturn_t ahci_interrupt(int irq, void *dev_instance) { struct ata_host *host = dev_instance; @@ -1782,7 +1945,7 @@ irqreturn_t ahci_interrupt(int irq, void *dev_instance) } EXPORT_SYMBOL_GPL(ahci_interrupt); -static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) +unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; void __iomem *port_mmio = ahci_port_base(ap); @@ -1811,6 +1974,7 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) return 0; } +EXPORT_SYMBOL_GPL(ahci_qc_issue); static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) { @@ -1861,12 +2025,14 @@ static void ahci_thaw(struct ata_port *ap) writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); } -static void ahci_error_handler(struct ata_port *ap) +void ahci_error_handler(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ ahci_stop_engine(ap); - ahci_start_engine(ap); + hpriv->start_engine(ap); } sata_pmp_error_handler(ap); @@ -1874,6 +2040,7 @@ static void ahci_error_handler(struct ata_port *ap) if (!ata_dev_enabled(ap->link.device)) ahci_stop_engine(ap); } +EXPORT_SYMBOL_GPL(ahci_error_handler); static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) { @@ -1884,8 +2051,85 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) ahci_kick_engine(ap); } +static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_device *dev = ap->link.device; + u32 devslp, dm, dito, mdat, deto; + int rc; + unsigned int err_mask; + + devslp = readl(port_mmio + PORT_DEVSLP); + if (!(devslp & PORT_DEVSLP_DSP)) { + dev_err(ap->host->dev, "port does not support device sleep\n"); + return; + } + + /* disable device sleep */ + if (!sleep) { + if (devslp & PORT_DEVSLP_ADSE) { + writel(devslp & ~PORT_DEVSLP_ADSE, + port_mmio + PORT_DEVSLP); + err_mask = ata_dev_set_feature(dev, + SETFEATURES_SATA_DISABLE, + SATA_DEVSLP); + if (err_mask && err_mask != AC_ERR_DEV) + ata_dev_warn(dev, "failed to disable DEVSLP\n"); + } + return; + } + + /* device sleep was already enabled */ + if (devslp & PORT_DEVSLP_ADSE) + return; + + /* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */ + rc = ahci_stop_engine(ap); + if (rc) + return; + + dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET; + dito = devslp_idle_timeout / (dm + 1); + if (dito > 0x3ff) + dito = 0x3ff; + + /* Use the nominal value 10 ms if the read MDAT is zero, + * the nominal value of DETO is 20 ms. + */ + if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & + ATA_LOG_DEVSLP_VALID_MASK) { + mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & + ATA_LOG_DEVSLP_MDAT_MASK; + if (!mdat) + mdat = 10; + deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; + if (!deto) + deto = 20; + } else { + mdat = 10; + deto = 20; + } + + devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) | + (mdat << PORT_DEVSLP_MDAT_OFFSET) | + (deto << PORT_DEVSLP_DETO_OFFSET) | + PORT_DEVSLP_ADSE); + writel(devslp, port_mmio + PORT_DEVSLP); + + hpriv->start_engine(ap); + + /* enable device sleep feature for the drive */ + err_mask = ata_dev_set_feature(dev, + SETFEATURES_SATA_ENABLE, + SATA_DEVSLP); + if (err_mask && err_mask != AC_ERR_DEV) + ata_dev_warn(dev, "failed to enable DEVSLP\n"); +} + static void ahci_enable_fbs(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_port_priv *pp = ap->private_data; void __iomem *port_mmio = ahci_port_base(ap); u32 fbs; @@ -1914,11 +2158,12 @@ static void ahci_enable_fbs(struct ata_port *ap) } else dev_err(ap->host->dev, "Failed to enable FBS\n"); - ahci_start_engine(ap); + hpriv->start_engine(ap); } static void ahci_disable_fbs(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_port_priv *pp = ap->private_data; void __iomem *port_mmio = ahci_port_base(ap); u32 fbs; @@ -1946,7 +2191,7 @@ static void ahci_disable_fbs(struct ata_port *ap) pp->fbs_enabled = false; } - ahci_start_engine(ap); + hpriv->start_engine(ap); } static void ahci_pmp_attach(struct ata_port *ap) @@ -2039,6 +2284,16 @@ static int ahci_port_start(struct ata_port *ap) if (!pp) return -ENOMEM; + if (ap->host->n_ports > 1) { + pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL); + if (!pp->irq_desc) { + devm_kfree(dev, pp); + return -ENOMEM; + } + snprintf(pp->irq_desc, 8, + "%s%d", dev_driver_string(dev), ap->port_no); + } + /* check FBS capability */ if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) { void __iomem *port_mmio = ahci_port_base(ap); @@ -2099,6 +2354,14 @@ static int ahci_port_start(struct ata_port *ap) */ pp->intr_mask = DEF_PORT_IRQ; + /* + * Switch to per-port locking in case each port has its own MSI vector. + */ + if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) { + spin_lock_init(&pp->lock); + ap->lock = &pp->lock; + } + ap->private_data = pp; /* engage engines, captain */ @@ -2158,7 +2421,8 @@ void ahci_print_info(struct ata_host *host, const char *scc_s) "flags: " "%s%s%s%s%s%s%s" "%s%s%s%s%s%s%s" - "%s%s%s%s%s%s\n" + "%s%s%s%s%s%s%s" + "%s%s\n" , cap & HOST_CAP_64 ? "64bit " : "", @@ -2178,6 +2442,9 @@ void ahci_print_info(struct ata_host *host, const char *scc_s) cap & HOST_CAP_CCC ? "ccc " : "", cap & HOST_CAP_EMS ? "ems " : "", cap & HOST_CAP_SXS ? "sxs " : "", + cap2 & HOST_CAP2_DESO ? "deso " : "", + cap2 & HOST_CAP2_SADM ? "sadm " : "", + cap2 & HOST_CAP2_SDS ? "sds " : "", cap2 & HOST_CAP2_APST ? "apst " : "", cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "", cap2 & HOST_CAP2_BOH ? "boh " : "" diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c new file mode 100644 index 00000000000..b0077589f06 --- /dev/null +++ b/drivers/ata/libahci_platform.c @@ -0,0 +1,549 @@ +/* + * AHCI SATA platform library + * + * Copyright 2004-2005 Red Hat, Inc. + * Jeff Garzik <jgarzik@pobox.com> + * Copyright 2010 MontaVista Software, LLC. + * Anton Vorontsov <avorontsov@ru.mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + */ + +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/ahci_platform.h> +#include <linux/phy/phy.h> +#include <linux/pm_runtime.h> +#include "ahci.h" + +static void ahci_host_stop(struct ata_host *host); + +struct ata_port_operations ahci_platform_ops = { + .inherits = &ahci_ops, + .host_stop = ahci_host_stop, +}; +EXPORT_SYMBOL_GPL(ahci_platform_ops); + +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT("ahci_platform"), +}; + +/** + * ahci_platform_enable_clks - Enable platform clocks + * @hpriv: host private area to store config values + * + * This function enables all the clks found in hpriv->clks, starting at + * index 0. If any clk fails to enable it disables all the clks already + * enabled in reverse order, and then returns an error. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) +{ + int c, rc; + + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { + rc = clk_prepare_enable(hpriv->clks[c]); + if (rc) + goto disable_unprepare_clk; + } + return 0; + +disable_unprepare_clk: + while (--c >= 0) + clk_disable_unprepare(hpriv->clks[c]); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); + +/** + * ahci_platform_disable_clks - Disable platform clocks + * @hpriv: host private area to store config values + * + * This function disables all the clks found in hpriv->clks, in reverse + * order of ahci_platform_enable_clks (starting at the end of the array). + */ +void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) +{ + int c; + + for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) + if (hpriv->clks[c]) + clk_disable_unprepare(hpriv->clks[c]); +} +EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); + +/** + * ahci_platform_enable_resources - Enable platform resources + * @hpriv: host private area to store config values + * + * This function enables all ahci_platform managed resources in the + * following order: + * 1) Regulator + * 2) Clocks (through ahci_platform_enable_clks) + * 3) Phy + * + * If resource enabling fails at any point the previous enabled resources + * are disabled in reverse order. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) +{ + int rc; + + if (hpriv->target_pwr) { + rc = regulator_enable(hpriv->target_pwr); + if (rc) + return rc; + } + + rc = ahci_platform_enable_clks(hpriv); + if (rc) + goto disable_regulator; + + if (hpriv->phy) { + rc = phy_init(hpriv->phy); + if (rc) + goto disable_clks; + + rc = phy_power_on(hpriv->phy); + if (rc) { + phy_exit(hpriv->phy); + goto disable_clks; + } + } + + return 0; + +disable_clks: + ahci_platform_disable_clks(hpriv); + +disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); + +/** + * ahci_platform_disable_resources - Disable platform resources + * @hpriv: host private area to store config values + * + * This function disables all ahci_platform managed resources in the + * following order: + * 1) Phy + * 2) Clocks (through ahci_platform_disable_clks) + * 3) Regulator + */ +void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) +{ + if (hpriv->phy) { + phy_power_off(hpriv->phy); + phy_exit(hpriv->phy); + } + + ahci_platform_disable_clks(hpriv); + + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); +} +EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); + +static void ahci_platform_put_resources(struct device *dev, void *res) +{ + struct ahci_host_priv *hpriv = res; + int c; + + if (hpriv->got_runtime_pm) { + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + } + + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); +} + +/** + * ahci_platform_get_resources - Get platform resources + * @pdev: platform device to get resources for + * + * This function allocates an ahci_host_priv struct, and gets the following + * resources, storing a reference to them inside the returned struct: + * + * 1) mmio registers (IORESOURCE_MEM 0, mandatory) + * 2) regulator for controlling the targets power (optional) + * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, + * or for non devicetree enabled platforms a single clock + * 4) phy (optional) + * + * RETURNS: + * The allocated ahci_host_priv on success, otherwise an ERR_PTR value + */ +struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct clk *clk; + int i, rc = -ENOMEM; + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); + + hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), + GFP_KERNEL); + if (!hpriv) + goto err_out; + + devres_add(dev, hpriv); + + hpriv->mmio = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (IS_ERR(hpriv->mmio)) { + dev_err(dev, "no mmio space\n"); + rc = PTR_ERR(hpriv->mmio); + goto err_out; + } + + hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); + if (IS_ERR(hpriv->target_pwr)) { + rc = PTR_ERR(hpriv->target_pwr); + if (rc == -EPROBE_DEFER) + goto err_out; + hpriv->target_pwr = NULL; + } + + for (i = 0; i < AHCI_MAX_CLKS; i++) { + /* + * For now we must use clk_get(dev, NULL) for the first clock, + * because some platforms (da850, spear13xx) are not yet + * converted to use devicetree for clocks. For new platforms + * this is equivalent to of_clk_get(dev->of_node, 0). + */ + if (i == 0) + clk = clk_get(dev, NULL); + else + clk = of_clk_get(dev->of_node, i); + + if (IS_ERR(clk)) { + rc = PTR_ERR(clk); + if (rc == -EPROBE_DEFER) + goto err_out; + break; + } + hpriv->clks[i] = clk; + } + + hpriv->phy = devm_phy_get(dev, "sata-phy"); + if (IS_ERR(hpriv->phy)) { + rc = PTR_ERR(hpriv->phy); + switch (rc) { + case -ENOSYS: + /* No PHY support. Check if PHY is required. */ + if (of_find_property(dev->of_node, "phys", NULL)) { + dev_err(dev, "couldn't get sata-phy: ENOSYS\n"); + goto err_out; + } + case -ENODEV: + /* continue normally */ + hpriv->phy = NULL; + break; + + case -EPROBE_DEFER: + goto err_out; + + default: + dev_err(dev, "couldn't get sata-phy\n"); + goto err_out; + } + } + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + hpriv->got_runtime_pm = true; + + devres_remove_group(dev, NULL); + return hpriv; + +err_out: + devres_release_group(dev, NULL); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(ahci_platform_get_resources); + +/** + * ahci_platform_init_host - Bring up an ahci-platform host + * @pdev: platform device pointer for the host + * @hpriv: ahci-host private data for the host + * @pi_template: template for the ata_port_info to use + * @host_flags: ahci host flags used in ahci_host_priv + * @force_port_map: param passed to ahci_save_initial_config + * @mask_port_map: param passed to ahci_save_initial_config + * + * This function does all the usual steps needed to bring up an + * ahci-platform host, note any necessary resources (ie clks, phy, etc.) + * must be initialized / enabled before calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_init_host(struct platform_device *pdev, + struct ahci_host_priv *hpriv, + const struct ata_port_info *pi_template, + unsigned long host_flags, + unsigned int force_port_map, + unsigned int mask_port_map) +{ + struct device *dev = &pdev->dev; + struct ata_port_info pi = *pi_template; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ata_host *host; + int i, irq, n_ports, rc; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "no irq\n"); + return -EINVAL; + } + + /* prepare host */ + pi.private_data = (void *)host_flags; + hpriv->flags |= host_flags; + + ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); + + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + + if (hpriv->cap & HOST_CAP_PMP) + pi.flags |= ATA_FLAG_PMP; + + ahci_set_em_messages(hpriv, &pi); + + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) + return -ENOMEM; + + host->private_data = hpriv; + + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) + host->flags |= ATA_HOST_PARALLEL_SCAN; + else + dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); + + if (pi.flags & ATA_FLAG_EM) + ahci_reset_em(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + ata_port_desc(ap, "mmio %pR", + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + } + + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + + return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, + &ahci_platform_sht); +} +EXPORT_SYMBOL_GPL(ahci_platform_init_host); + +static void ahci_host_stop(struct ata_host *host) +{ + struct device *dev = host->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + + if (pdata && pdata->exit) + pdata->exit(dev); + + ahci_platform_disable_resources(hpriv); +} + +#ifdef CONFIG_PM_SLEEP +/** + * ahci_platform_suspend_host - Suspend an ahci-platform host + * @dev: device pointer for the host + * + * This function does all the usual steps needed to suspend an + * ahci-platform host, note any necessary resources (ie clks, phy, etc.) + * must be disabled after calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_suspend_host(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 ctl; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(dev, "firmware update required for suspend/resume\n"); + return -EIO; + } + + /* + * AHCI spec rev1.1 section 8.3.3: + * Software must disable interrupts prior to requesting a + * transition of the HBA to D3 state. + */ + ctl = readl(mmio + HOST_CTL); + ctl &= ~HOST_IRQ_EN; + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + + return ata_host_suspend(host, PMSG_SUSPEND); +} +EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); + +/** + * ahci_platform_resume_host - Resume an ahci-platform host + * @dev: device pointer for the host + * + * This function does all the usual steps needed to resume an ahci-platform + * host, note any necessary resources (ie clks, phy, etc.) must be + * initialized / enabled before calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_resume_host(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + int rc; + + if (dev->power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + } + + ata_host_resume(host); + + return 0; +} +EXPORT_SYMBOL_GPL(ahci_platform_resume_host); + +/** + * ahci_platform_suspend - Suspend an ahci-platform device + * @dev: the platform device to suspend + * + * This function suspends the host associated with the device, followed by + * disabling all the resources of the device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_suspend(struct device *dev) +{ + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + + if (pdata && pdata->suspend) { + rc = pdata->suspend(dev); + if (rc) + goto resume_host; + } + + ahci_platform_disable_resources(hpriv); + + return 0; + +resume_host: + ahci_platform_resume_host(dev); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +/** + * ahci_platform_resume - Resume an ahci-platform device + * @dev: the platform device to resume + * + * This function enables all the resources of the device followed by + * resuming the host associated with the device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_resume(struct device *dev) +{ + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); + if (rc) + goto disable_resources; + } + + rc = ahci_platform_resume_host(dev); + if (rc) + goto disable_resources; + + /* We resumed so update PM runtime state */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_resume); +#endif + +MODULE_DESCRIPTION("AHCI SATA platform library"); +MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index bb7c5f1085c..97a14fe47de 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -16,11 +16,10 @@ #include <linux/libata.h> #include <linux/pci.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <scsi/scsi_device.h> #include "libata.h" -#include <acpi/acpi_bus.h> - unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT; module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644); MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock, 0x4=DIPM, 0x8=FPDMA non-zero offset, 0x10=FPDMA DMA Setup FIS auto-activate)"); @@ -33,76 +32,34 @@ struct ata_acpi_gtf { u8 tf[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */ } __packed; -/* - * Helper - belongs in the PCI layer somewhere eventually - */ -static int is_pci_dev(struct device *dev) -{ - return (dev->bus == &pci_bus_type); -} - static void ata_acpi_clear_gtf(struct ata_device *dev) { kfree(dev->gtf_cache); dev->gtf_cache = NULL; } +struct ata_acpi_hotplug_context { + struct acpi_hotplug_context hp; + union { + struct ata_port *ap; + struct ata_device *dev; + } data; +}; + +#define ata_hotplug_data(context) (container_of((context), struct ata_acpi_hotplug_context, hp)->data) + /** - * ata_acpi_associate_sata_port - associate SATA port with ACPI objects - * @ap: target SATA port + * ata_dev_acpi_handle - provide the acpi_handle for an ata_device + * @dev: the acpi_handle returned will correspond to this device * - * Look up ACPI objects associated with @ap and initialize acpi_handle - * fields of @ap, the port and devices accordingly. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -errno on failure. + * Returns the acpi_handle for the ACPI namespace object corresponding to + * the ata_device passed into the function, or NULL if no such object exists + * or ACPI is disabled for this device due to consecutive errors. */ -void ata_acpi_associate_sata_port(struct ata_port *ap) -{ - WARN_ON(!(ap->flags & ATA_FLAG_ACPI_SATA)); - - if (!sata_pmp_attached(ap)) { - u64 adr = SATA_ADR(ap->port_no, NO_PORT_MULT); - - ap->link.device->acpi_handle = - acpi_get_child(ap->host->acpi_handle, adr); - } else { - struct ata_link *link; - - ap->link.device->acpi_handle = NULL; - - ata_for_each_link(link, ap, EDGE) { - u64 adr = SATA_ADR(ap->port_no, link->pmp); - - link->device->acpi_handle = - acpi_get_child(ap->host->acpi_handle, adr); - } - } -} - -static void ata_acpi_associate_ide_port(struct ata_port *ap) +acpi_handle ata_dev_acpi_handle(struct ata_device *dev) { - int max_devices, i; - - ap->acpi_handle = acpi_get_child(ap->host->acpi_handle, ap->port_no); - if (!ap->acpi_handle) - return; - - max_devices = 1; - if (ap->flags & ATA_FLAG_SLAVE_POSS) - max_devices++; - - for (i = 0; i < max_devices; i++) { - struct ata_device *dev = &ap->link.device[i]; - - dev->acpi_handle = acpi_get_child(ap->acpi_handle, i); - } - - if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) - ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; + return dev->flags & ATA_DFLAG_ACPI_DISABLED ? + NULL : ACPI_HANDLE(&dev->tdev); } /* @ap and @dev are the same as ata_acpi_handle_hotplug() */ @@ -174,18 +131,17 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, ata_port_wait_eh(ap); } -static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data) +static int ata_acpi_dev_notify_dock(struct acpi_device *adev, u32 event) { - struct ata_device *dev = data; - + struct ata_device *dev = ata_hotplug_data(adev->hp).dev; ata_acpi_handle_hotplug(dev->link->ap, dev, event); + return 0; } -static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data) +static int ata_acpi_ap_notify_dock(struct acpi_device *adev, u32 event) { - struct ata_port *ap = data; - - ata_acpi_handle_hotplug(ap, NULL, event); + ata_acpi_handle_hotplug(ata_hotplug_data(adev->hp).ap, NULL, event); + return 0; } static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, @@ -207,75 +163,85 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, } } -static void ata_acpi_ap_uevent(acpi_handle handle, u32 event, void *data) +static void ata_acpi_ap_uevent(struct acpi_device *adev, u32 event) { - ata_acpi_uevent(data, NULL, event); + ata_acpi_uevent(ata_hotplug_data(adev->hp).ap, NULL, event); } -static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data) +static void ata_acpi_dev_uevent(struct acpi_device *adev, u32 event) { - struct ata_device *dev = data; + struct ata_device *dev = ata_hotplug_data(adev->hp).dev; ata_acpi_uevent(dev->link->ap, dev, event); } -static const struct acpi_dock_ops ata_acpi_dev_dock_ops = { - .handler = ata_acpi_dev_notify_dock, - .uevent = ata_acpi_dev_uevent, -}; +/* bind acpi handle to pata port */ +void ata_acpi_bind_port(struct ata_port *ap) +{ + struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); + struct acpi_device *adev; + struct ata_acpi_hotplug_context *context; -static const struct acpi_dock_ops ata_acpi_ap_dock_ops = { - .handler = ata_acpi_ap_notify_dock, - .uevent = ata_acpi_ap_uevent, -}; + if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_companion) + return; -/** - * ata_acpi_associate - associate ATA host with ACPI objects - * @host: target ATA host - * - * Look up ACPI objects associated with @host and initialize - * acpi_handle fields of @host, its ports and devices accordingly. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -void ata_acpi_associate(struct ata_host *host) -{ - int i, j; + acpi_preset_companion(&ap->tdev, host_companion, ap->port_no); - if (!is_pci_dev(host->dev) || libata_noacpi) + if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) + ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; + + adev = ACPI_COMPANION(&ap->tdev); + if (!adev || adev->hp) return; - host->acpi_handle = DEVICE_ACPI_HANDLE(host->dev); - if (!host->acpi_handle) + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) return; - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; + context->data.ap = ap; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_ap_notify_dock, + ata_acpi_ap_uevent); +} + +void ata_acpi_bind_dev(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + struct acpi_device *port_companion = ACPI_COMPANION(&ap->tdev); + struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); + struct acpi_device *parent, *adev; + struct ata_acpi_hotplug_context *context; + u64 adr; - if (host->ports[0]->flags & ATA_FLAG_ACPI_SATA) - ata_acpi_associate_sata_port(ap); + /* + * For both sata/pata devices, host companion device is required. + * For pata device, port companion device is also required. + */ + if (libata_noacpi || !host_companion || + (!(ap->flags & ATA_FLAG_ACPI_SATA) && !port_companion)) + return; + + if (ap->flags & ATA_FLAG_ACPI_SATA) { + if (!sata_pmp_attached(ap)) + adr = SATA_ADR(ap->port_no, NO_PORT_MULT); else - ata_acpi_associate_ide_port(ap); + adr = SATA_ADR(ap->port_no, dev->link->pmp); + parent = host_companion; + } else { + adr = dev->devno; + parent = port_companion; + } - if (ap->acpi_handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(ap->acpi_handle, - &ata_acpi_ap_dock_ops, ap); - } + acpi_preset_companion(&dev->tdev, parent, adr); + adev = ACPI_COMPANION(&dev->tdev); + if (!adev || adev->hp) + return; - for (j = 0; j < ata_link_max_devices(&ap->link); j++) { - struct ata_device *dev = &ap->link.device[j]; + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return; - if (dev->acpi_handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(dev->acpi_handle, - &ata_acpi_dev_dock_ops, dev); - } - } - } + context->data.dev = dev; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_dev_notify_dock, + ata_acpi_dev_uevent); } /** @@ -299,7 +265,7 @@ void ata_acpi_dissociate(struct ata_host *host) struct ata_port *ap = host->ports[i]; const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); - if (ap->acpi_handle && gtm) + if (ACPI_HANDLE(&ap->tdev) && gtm) ata_acpi_stm(ap, gtm); } } @@ -323,8 +289,12 @@ int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *gtm) union acpi_object *out_obj; acpi_status status; int rc = 0; + acpi_handle handle = ACPI_HANDLE(&ap->tdev); - status = acpi_evaluate_object(ap->acpi_handle, "_GTM", NULL, &output); + if (!handle) + return -EINVAL; + + status = acpi_evaluate_object(handle, "_GTM", NULL, &output); rc = -ENOENT; if (status == AE_NOT_FOUND) @@ -394,7 +364,8 @@ int ata_acpi_stm(struct ata_port *ap, const struct ata_acpi_gtm *stm) input.count = 3; input.pointer = in_params; - status = acpi_evaluate_object(ap->acpi_handle, "_STM", &input, NULL); + status = acpi_evaluate_object(ACPI_HANDLE(&ap->tdev), "_STM", + &input, NULL); if (status == AE_NOT_FOUND) return -ENOENT; @@ -451,7 +422,8 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf) __func__, ap->port_no); /* _GTF has no input parameters */ - status = acpi_evaluate_object(dev->acpi_handle, "_GTF", NULL, &output); + status = acpi_evaluate_object(ata_dev_acpi_handle(dev), "_GTF", NULL, + &output); out_obj = dev->gtf_cache = output.pointer; if (ACPI_FAILURE(status)) { @@ -817,7 +789,8 @@ static int ata_acpi_push_id(struct ata_device *dev) /* It's OK for _SDD to be missing too. */ swap_buf_le16(dev->id, ATA_ID_WORDS); - status = acpi_evaluate_object(dev->acpi_handle, "_SDD", &input, NULL); + status = acpi_evaluate_object(ata_dev_acpi_handle(dev), "_SDD", &input, + NULL); swap_buf_le16(dev->id, ATA_ID_WORDS); if (status == AE_NOT_FOUND) @@ -867,7 +840,7 @@ void ata_acpi_on_resume(struct ata_port *ap) const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); struct ata_device *dev; - if (ap->acpi_handle && gtm) { + if (ACPI_HANDLE(&ap->tdev) && gtm) { /* _GTM valid */ /* restore timing parameters */ @@ -880,6 +853,7 @@ void ata_acpi_on_resume(struct ata_port *ap) ata_for_each_dev(dev, &ap->link, ALL) { ata_acpi_clear_gtf(dev); if (ata_dev_enabled(dev) && + ata_dev_acpi_handle(dev) && ata_dev_get_GTF(dev, NULL) >= 0) dev->flags |= ATA_DFLAG_ACPI_PENDING; } @@ -896,34 +870,94 @@ void ata_acpi_on_resume(struct ata_port *ap) } } -/** - * ata_acpi_set_state - set the port power state - * @ap: target ATA port - * @state: state, on/off - * - * This function executes the _PS0/_PS3 ACPI method to set the power state. - * ACPI spec requires _PS0 when IDE power on and _PS3 when power off - */ -void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) +static int ata_acpi_choose_suspend_state(struct ata_device *dev, bool runtime) +{ + int d_max_in = ACPI_STATE_D3_COLD; + if (!runtime) + goto out; + + /* + * For ATAPI, runtime D3 cold is only allowed + * for ZPODD in zero power ready state + */ + if (dev->class == ATA_DEV_ATAPI && + !(zpodd_dev_enabled(dev) && zpodd_zpready(dev))) + d_max_in = ACPI_STATE_D3_HOT; + +out: + return acpi_pm_device_sleep_state(&dev->tdev, NULL, d_max_in); +} + +static void sata_acpi_set_state(struct ata_port *ap, pm_message_t state) +{ + bool runtime = PMSG_IS_AUTO(state); + struct ata_device *dev; + acpi_handle handle; + int acpi_state; + + ata_for_each_dev(dev, &ap->link, ENABLED) { + handle = ata_dev_acpi_handle(dev); + if (!handle) + continue; + + if (!(state.event & PM_EVENT_RESUME)) { + acpi_state = ata_acpi_choose_suspend_state(dev, runtime); + if (acpi_state == ACPI_STATE_D0) + continue; + if (runtime && zpodd_dev_enabled(dev) && + acpi_state == ACPI_STATE_D3_COLD) + zpodd_enable_run_wake(dev); + acpi_bus_set_power(handle, acpi_state); + } else { + if (runtime && zpodd_dev_enabled(dev)) + zpodd_disable_run_wake(dev); + acpi_bus_set_power(handle, ACPI_STATE_D0); + } + } +} + +/* ACPI spec requires _PS0 when IDE power on and _PS3 when power off */ +static void pata_acpi_set_state(struct ata_port *ap, pm_message_t state) { struct ata_device *dev; + acpi_handle port_handle; - if (!ap->acpi_handle || (ap->flags & ATA_FLAG_ACPI_SATA)) + port_handle = ACPI_HANDLE(&ap->tdev); + if (!port_handle) return; /* channel first and then drives for power on and vica versa for power off */ - if (state.event == PM_EVENT_ON) - acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D0); + if (state.event & PM_EVENT_RESUME) + acpi_bus_set_power(port_handle, ACPI_STATE_D0); ata_for_each_dev(dev, &ap->link, ENABLED) { - if (dev->acpi_handle) - acpi_bus_set_power(dev->acpi_handle, - state.event == PM_EVENT_ON ? - ACPI_STATE_D0 : ACPI_STATE_D3); + acpi_handle dev_handle = ata_dev_acpi_handle(dev); + if (!dev_handle) + continue; + + acpi_bus_set_power(dev_handle, state.event & PM_EVENT_RESUME ? + ACPI_STATE_D0 : ACPI_STATE_D3_COLD); } - if (state.event != PM_EVENT_ON) - acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D3); + + if (!(state.event & PM_EVENT_RESUME)) + acpi_bus_set_power(port_handle, ACPI_STATE_D3_COLD); +} + +/** + * ata_acpi_set_state - set the port power state + * @ap: target ATA port + * @state: state, on/off + * + * This function sets a proper ACPI D state for the device on + * system and runtime PM operations. + */ +void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) +{ + if (ap->flags & ATA_FLAG_ACPI_SATA) + sata_acpi_set_state(ap, state); + else + pata_acpi_set_state(ap, state); } /** @@ -948,7 +982,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev) int nr_executed = 0; int rc; - if (!dev->acpi_handle) + if (!ata_dev_acpi_handle(dev)) return 0; /* do we need to do _GTF? */ @@ -993,8 +1027,8 @@ int ata_acpi_on_devcfg(struct ata_device *dev) return rc; } + dev->flags |= ATA_DFLAG_ACPI_DISABLED; ata_dev_warn(dev, "ACPI: failed the second time, disabled\n"); - dev->acpi_handle = NULL; /* We can safely continue if no _GTF command has been executed * and port is not frozen. diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c06e0ec1155..677c0c1b03b 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1,7 +1,7 @@ /* * libata-core.c - helper library for ATA * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -67,6 +67,7 @@ #include <linux/cdrom.h> #include <linux/ratelimit.h> #include <linux/pm_runtime.h> +#include <linux/platform_device.h> #include "libata.h" #include "libata-transport.h" @@ -80,6 +81,8 @@ const struct ata_port_operations ata_base_port_ops = { .prereset = ata_std_prereset, .postreset = ata_std_postreset, .error_handler = ata_std_error_handler, + .sched_eh = ata_std_sched_eh, + .end_eh = ata_std_end_eh, }; const struct ata_port_operations sata_port_ops = { @@ -95,7 +98,7 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev); static void ata_dev_xfermask(struct ata_device *dev); static unsigned long ata_dev_blacklisted(const struct ata_device *dev); -unsigned int ata_print_id = 1; +atomic_t ata_print_id = ATOMIC_INIT(0); struct ata_force_param { const char *name; @@ -566,10 +569,10 @@ void ata_tf_to_fis(const struct ata_taskfile *tf, u8 pmp, int is_cmd, u8 *fis) fis[14] = 0; fis[15] = tf->ctl; - fis[16] = 0; - fis[17] = 0; - fis[18] = 0; - fis[19] = 0; + fis[16] = tf->auxiliary & 0xff; + fis[17] = (tf->auxiliary >> 8) & 0xff; + fis[18] = (tf->auxiliary >> 16) & 0xff; + fis[19] = (tf->auxiliary >> 24) & 0xff; } /** @@ -772,7 +775,7 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, tf->lbam = (block >> 8) & 0xff; tf->lbal = block & 0xff; - tf->device = 1 << 6; + tf->device = ATA_LBA; if (tf->flags & ATA_TFLAG_FUA) tf->device |= 1 << 7; } else if (dev->flags & ATA_DFLAG_LBA) { @@ -1521,7 +1524,7 @@ static void ata_qc_complete_internal(struct ata_queued_cmd *qc) * @dev: Device to which the command is sent * @tf: Taskfile registers for the command and the result * @cdb: CDB for packet command - * @dma_dir: Data tranfer direction of the command + * @dma_dir: Data transfer direction of the command * @sgl: sg list for the data buffer of the command * @n_elem: Number of sg entries * @timeout: Timeout in msecs (0 for default) @@ -1599,6 +1602,12 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, qc->tf = *tf; if (cdb) memcpy(qc->cdb, cdb, ATAPI_CDB_LEN); + + /* some SATA bridges need us to indicate data xfer direction */ + if (tf->protocol == ATAPI_PROT_DMA && (dev->flags & ATA_DFLAG_DMADIR) && + dma_dir == DMA_FROM_DEVICE) + qc->tf.feature |= ATAPI_DMADIR; + qc->flags |= ATA_QCFLAG_RESULT_TF; qc->dma_dir = dma_dir; if (dma_dir != DMA_NONE) { @@ -1703,7 +1712,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, * @dev: Device to which the command is sent * @tf: Taskfile registers for the command and the result * @cdb: CDB for packet command - * @dma_dir: Data tranfer direction of the command + * @dma_dir: Data transfer direction of the command * @buf: Data buffer of the command * @buflen: Length of data buffer * @timeout: Timeout in msecs (0 for default) @@ -1973,6 +1982,12 @@ retry: if (class == ATA_DEV_ATA) { if (!ata_id_is_ata(id) && !ata_id_is_cfa(id)) goto err_out; + if (ap->host->flags & ATA_HOST_IGNORE_ATA && + ata_id_is_ata(id)) { + ata_dev_dbg(dev, + "host indicates ignore ATA devices, ignored\n"); + return -ENOENT; + } } else { if (ata_id_is_ata(id)) goto err_out; @@ -2124,6 +2139,29 @@ static int ata_dev_config_ncq(struct ata_device *dev, else snprintf(desc, desc_sz, "NCQ (depth %d/%d)%s", hdepth, ddepth, aa_desc); + + if ((ap->flags & ATA_FLAG_FPDMA_AUX) && + ata_id_has_ncq_send_and_recv(dev->id)) { + err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get NCQ Send/Recv Log Emask 0x%x\n", + err_mask); + } else { + u8 *cmds = dev->ncq_send_recv_cmds; + + dev->flags |= ATA_DFLAG_NCQ_SEND_RECV; + memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE); + + if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) { + ata_dev_dbg(dev, "disabling queued TRIM support\n"); + cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &= + ~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM; + } + } + } + return 0; } @@ -2147,6 +2185,7 @@ int ata_dev_configure(struct ata_device *dev) int print_info = ehc->i.flags & ATA_EHI_PRINTINFO; const u16 *id = dev->id; unsigned long xfer_mask; + unsigned int err_mask; char revbuf[7]; /* XYZ-99\0 */ char fwrevbuf[ATA_ID_FW_REV_LEN+1]; char modelbuf[ATA_ID_PROD_LEN+1]; @@ -2183,6 +2222,16 @@ int ata_dev_configure(struct ata_device *dev) if (rc) return rc; + /* some WD SATA-1 drives have issues with LPM, turn on NOLPM for them */ + if ((dev->horkage & ATA_HORKAGE_WD_BROKEN_LPM) && + (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2) + dev->horkage |= ATA_HORKAGE_NOLPM; + + if (dev->horkage & ATA_HORKAGE_NOLPM) { + ata_dev_warn(dev, "LPM support broken, forcing max_power\n"); + dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER; + } + /* let ACPI work its magic */ rc = ata_acpi_on_devcfg(dev); if (rc) @@ -2315,6 +2364,30 @@ int ata_dev_configure(struct ata_device *dev) } } + /* Check and mark DevSlp capability. Get DevSlp timing variables + * from SATA Settings page of Identify Device Data Log. + */ + if (ata_id_has_devslp(dev->id)) { + u8 *sata_setting = ap->sector_buf; + int i, j; + + dev->flags |= ATA_DFLAG_DEVSLP; + err_mask = ata_read_log_page(dev, + ATA_LOG_SATA_ID_DEV_DATA, + ATA_LOG_SATA_SETTINGS, + sata_setting, + 1); + if (err_mask) + ata_dev_dbg(dev, + "failed to get Identify Device Data, Emask 0x%x\n", + err_mask); + else + for (i = 0; i < ATA_LOG_DEVSLP_SIZE; i++) { + j = ATA_LOG_DEVSLP_OFFSET + i; + dev->devslp_timing[i] = sata_setting[j]; + } + } + dev->cdb_len = 16; } @@ -2343,8 +2416,6 @@ int ata_dev_configure(struct ata_device *dev) (ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id) && (!sata_pmp_attached(ap) || sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf) == 0)) { - unsigned int err_mask; - /* issue SET feature command to turn this on */ err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE, SATA_AN); @@ -2363,11 +2434,16 @@ int ata_dev_configure(struct ata_device *dev) cdb_intr_string = ", CDB intr"; } - if (atapi_dmadir || atapi_id_dmadir(dev->id)) { + if (atapi_dmadir || (dev->horkage & ATA_HORKAGE_ATAPI_DMADIR) || atapi_id_dmadir(dev->id)) { dev->flags |= ATA_DFLAG_DMADIR; dma_dir_string = ", DMADIR"; } + if (ata_id_has_da(dev->id)) { + dev->flags |= ATA_DFLAG_DA; + zpodd_init(dev); + } + /* print device info to dmesg */ if (ata_msg_drv(ap) && print_info) ata_dev_info(dev, @@ -2402,6 +2478,9 @@ int ata_dev_configure(struct ata_device *dev) dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128, dev->max_sectors); + if (dev->horkage & ATA_HORKAGE_MAX_SEC_LBA48) + dev->max_sectors = ATA_MAX_SECTORS_LBA48; + if (ap->ops->dev_config) ap->ops->dev_config(dev); @@ -2530,6 +2609,7 @@ int ata_bus_probe(struct ata_port *ap) * bus as we may be talking too fast. */ dev->pio_mode = XFER_PIO_0; + dev->dma_mode = 0xff; /* If the controller has a pio mode setup function * then use it to set the chipset to rights. Don't @@ -2912,6 +2992,10 @@ const struct ata_timing *ata_timing_find_mode(u8 xfer_mode) if (xfer_mode == t->mode) return t; + + WARN_ONCE(true, "%s: unable to find timing for xfer_mode 0x%x\n", + __func__, xfer_mode); + return NULL; } @@ -3587,7 +3671,7 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, switch (policy) { case ATA_LPM_MAX_POWER: /* disable all LPM transitions */ - scontrol |= (0x3 << 8); + scontrol |= (0x7 << 8); /* initiate transition to active state */ if (spm_wakeup) { scontrol |= (0x4 << 12); @@ -3597,12 +3681,12 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, case ATA_LPM_MED_POWER: /* allow LPM to PARTIAL */ scontrol &= ~(0x1 << 8); - scontrol |= (0x2 << 8); + scontrol |= (0x6 << 8); break; case ATA_LPM_MIN_POWER: if (ata_link_nr_enabled(link) > 0) /* no restrictions on LPM transitions */ - scontrol &= ~(0x3 << 8); + scontrol &= ~(0x7 << 8); else { /* empty port, power off */ scontrol &= ~0xf; @@ -4051,12 +4135,15 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "_NEC DV5800A", NULL, ATA_HORKAGE_NODMA }, { "SAMSUNG CD-ROM SN-124", "N001", ATA_HORKAGE_NODMA }, { "Seagate STT20000A", NULL, ATA_HORKAGE_NODMA }, + { " 2GB ATA Flash Disk", "ADMA428M", ATA_HORKAGE_NODMA }, /* Odd clown on sil3726/4726 PMPs */ { "Config Disk", NULL, ATA_HORKAGE_DISABLE }, /* Weird ATAPI devices */ { "TORiSAN DVD-ROM DRD-N216", NULL, ATA_HORKAGE_MAX_SEC_128 }, { "QUANTUM DAT DAT72-000", NULL, ATA_HORKAGE_ATAPI_MOD16_DMA }, + { "Slimtype DVD A DS8A8SH", NULL, ATA_HORKAGE_MAX_SEC_LBA48 }, + { "Slimtype DVD A DS8A9SH", NULL, ATA_HORKAGE_MAX_SEC_LBA48 }, /* Devices we expect to fail diagnostics */ @@ -4086,6 +4173,10 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "ST3320[68]13AS", "SD1[5-9]", ATA_HORKAGE_NONCQ | ATA_HORKAGE_FIRMWARE_WARN }, + /* Seagate Momentus SpinPoint M8 seem to have FPMDA_AA issues */ + { "ST1000LM024 HN-M101MBB", "2AR10001", ATA_HORKAGE_BROKEN_FPDMA_AA }, + { "ST1000LM024 HN-M101MBB", "2BA30001", ATA_HORKAGE_BROKEN_FPDMA_AA }, + /* Blacklist entries taken from Silicon Image 3124/3132 Windows driver .inf file - also several Linux problem reports */ { "HTS541060G9SA00", "MB3OC60D", ATA_HORKAGE_NONCQ, }, @@ -4116,9 +4207,11 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { /* Devices that do not need bridging limits applied */ { "MTRON MSP-SATA*", NULL, ATA_HORKAGE_BRIDGE_OK, }, + { "BUFFALO HD-QSU2/R5", NULL, ATA_HORKAGE_BRIDGE_OK, }, /* Devices which aren't very happy with higher link speeds */ { "WD My Book", NULL, ATA_HORKAGE_1_5_GBPS, }, + { "Seagate FreeAgent GoFlex", NULL, ATA_HORKAGE_1_5_GBPS, }, /* * Devices which choke on SETXFER. Applies only if both the @@ -4130,6 +4223,29 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "PIONEER DVD-RW DVR-212D", NULL, ATA_HORKAGE_NOSETXFER }, { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER }, + /* devices that don't properly handle queued TRIM commands */ + { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + { "Crucial_CT???M500SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + { "Micron_M550*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + { "Crucial_CT???M550SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + + /* + * Some WD SATA-I drives spin up and down erratically when the link + * is put into the slumber mode. We don't have full list of the + * affected devices. Disable LPM if the device matches one of the + * known prefixes and is SATA-1. As a side effect LPM partial is + * lost too. + * + * https://bugzilla.kernel.org/show_bug.cgi?id=57211 + */ + { "WDC WD800JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, + { "WDC WD1200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, + { "WDC WD1600JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, + { "WDC WD2000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, + { "WDC WD2500JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, + { "WDC WD3000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, + { "WDC WD3200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, + /* End Marker */ { } }; @@ -4458,6 +4574,7 @@ unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature) DPRINTK("EXIT, err_mask=%x\n", err_mask); return err_mask; } +EXPORT_SYMBOL_GPL(ata_dev_set_feature); /** * ata_dev_init_params - Issue INIT DEV PARAMS command @@ -4670,6 +4787,10 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) * ata_qc_new - Request an available ATA command, for queueing * @ap: target port * + * Some ATA host controllers may implement a queue depth which is less + * than ATA_MAX_QUEUE. So we shouldn't allocate a tag which is beyond + * the hardware limitation. + * * LOCKING: * None. */ @@ -4677,21 +4798,27 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) { struct ata_queued_cmd *qc = NULL; - unsigned int i; + unsigned int max_queue = ap->host->n_tags; + unsigned int i, tag; /* no command while frozen */ if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) return NULL; - /* the last tag is reserved for internal command. */ - for (i = 0; i < ATA_MAX_QUEUE - 1; i++) - if (!test_and_set_bit(i, &ap->qc_allocated)) { - qc = __ata_qc_from_tag(ap, i); + for (i = 0, tag = ap->last_tag + 1; i < max_queue; i++, tag++) { + tag = tag < max_queue ? tag : 0; + + /* the last tag is reserved for internal command. */ + if (tag == ATA_TAG_INTERNAL) + continue; + + if (!test_and_set_bit(tag, &ap->qc_allocated)) { + qc = __ata_qc_from_tag(ap, tag); + qc->tag = tag; + ap->last_tag = tag; break; } - - if (qc) - qc->tag = i; + } return qc; } @@ -5237,13 +5364,12 @@ bool ata_link_offline(struct ata_link *link) } #ifdef CONFIG_PM -static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, - unsigned int action, unsigned int ehi_flags, - int wait) +static void ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, + unsigned int action, unsigned int ehi_flags, + bool async) { struct ata_link *link; unsigned long flags; - int rc; /* Previous resume operation might still be in * progress. Wait for PM_PENDING to clear. @@ -5257,11 +5383,6 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, spin_lock_irqsave(ap->lock, flags); ap->pm_mesg = mesg; - if (wait) { - rc = 0; - ap->pm_result = &rc; - } - ap->pflags |= ATA_PFLAG_PM_PENDING; ata_for_each_link(link, ap, HOST_FIRST) { link->eh_info.action |= action; @@ -5272,104 +5393,149 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, spin_unlock_irqrestore(ap->lock, flags); - /* wait and check result */ - if (wait) { + if (!async) { ata_port_wait_eh(ap); WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); } - - return rc; } -#define to_ata_port(d) container_of(d, struct ata_port, tdev) +/* + * On some hardware, device fails to respond after spun down for suspend. As + * the device won't be used before being resumed, we don't need to touch the + * device. Ask EH to skip the usual stuff and proceed directly to suspend. + * + * http://thread.gmane.org/gmane.linux.ide/46764 + */ +static const unsigned int ata_port_suspend_ehi = ATA_EHI_QUIET + | ATA_EHI_NO_AUTOPSY + | ATA_EHI_NO_RECOVERY; -static int ata_port_suspend_common(struct device *dev, pm_message_t mesg) +static void ata_port_suspend(struct ata_port *ap, pm_message_t mesg) { - struct ata_port *ap = to_ata_port(dev); - unsigned int ehi_flags = ATA_EHI_QUIET; - int rc; - - /* - * On some hardware, device fails to respond after spun down - * for suspend. As the device won't be used before being - * resumed, we don't need to touch the device. Ask EH to skip - * the usual stuff and proceed directly to suspend. - * - * http://thread.gmane.org/gmane.linux.ide/46764 - */ - if (mesg.event == PM_EVENT_SUSPEND) - ehi_flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_NO_RECOVERY; + ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, false); +} - rc = ata_port_request_pm(ap, mesg, 0, ehi_flags, 1); - return rc; +static void ata_port_suspend_async(struct ata_port *ap, pm_message_t mesg) +{ + ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, true); } -static int ata_port_suspend(struct device *dev) +static int ata_port_pm_suspend(struct device *dev) { + struct ata_port *ap = to_ata_port(dev); + if (pm_runtime_suspended(dev)) return 0; - return ata_port_suspend_common(dev, PMSG_SUSPEND); + ata_port_suspend(ap, PMSG_SUSPEND); + return 0; } -static int ata_port_do_freeze(struct device *dev) +static int ata_port_pm_freeze(struct device *dev) { + struct ata_port *ap = to_ata_port(dev); + if (pm_runtime_suspended(dev)) - pm_runtime_resume(dev); + return 0; - return ata_port_suspend_common(dev, PMSG_FREEZE); + ata_port_suspend(ap, PMSG_FREEZE); + return 0; } -static int ata_port_poweroff(struct device *dev) +static int ata_port_pm_poweroff(struct device *dev) { - if (pm_runtime_suspended(dev)) - return 0; + ata_port_suspend(to_ata_port(dev), PMSG_HIBERNATE); + return 0; +} - return ata_port_suspend_common(dev, PMSG_HIBERNATE); +static const unsigned int ata_port_resume_ehi = ATA_EHI_NO_AUTOPSY + | ATA_EHI_QUIET; + +static void ata_port_resume(struct ata_port *ap, pm_message_t mesg) +{ + ata_port_request_pm(ap, mesg, ATA_EH_RESET, ata_port_resume_ehi, false); } -static int ata_port_resume_common(struct device *dev) +static void ata_port_resume_async(struct ata_port *ap, pm_message_t mesg) { - struct ata_port *ap = to_ata_port(dev); - int rc; + ata_port_request_pm(ap, mesg, ATA_EH_RESET, ata_port_resume_ehi, true); +} - rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET, - ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 1); - return rc; +static int ata_port_pm_resume(struct device *dev) +{ + ata_port_resume_async(to_ata_port(dev), PMSG_RESUME); + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; } -static int ata_port_resume(struct device *dev) +/* + * For ODDs, the upper layer will poll for media change every few seconds, + * which will make it enter and leave suspend state every few seconds. And + * as each suspend will cause a hard/soft reset, the gain of runtime suspend + * is very little and the ODD may malfunction after constantly being reset. + * So the idle callback here will not proceed to suspend if a non-ZPODD capable + * ODD is attached to the port. + */ +static int ata_port_runtime_idle(struct device *dev) { - int rc; + struct ata_port *ap = to_ata_port(dev); + struct ata_link *link; + struct ata_device *adev; - rc = ata_port_resume_common(dev); - if (!rc) { - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ata_for_each_link(link, ap, HOST_FIRST) { + ata_for_each_dev(adev, link, ENABLED) + if (adev->class == ATA_DEV_ATAPI && + !zpodd_dev_enabled(adev)) + return -EBUSY; } - return rc; + return 0; } -static int ata_port_runtime_idle(struct device *dev) +static int ata_port_runtime_suspend(struct device *dev) +{ + ata_port_suspend(to_ata_port(dev), PMSG_AUTO_SUSPEND); + return 0; +} + +static int ata_port_runtime_resume(struct device *dev) { - return pm_runtime_suspend(dev); + ata_port_resume(to_ata_port(dev), PMSG_AUTO_RESUME); + return 0; } static const struct dev_pm_ops ata_port_pm_ops = { - .suspend = ata_port_suspend, - .resume = ata_port_resume, - .freeze = ata_port_do_freeze, - .thaw = ata_port_resume, - .poweroff = ata_port_poweroff, - .restore = ata_port_resume, - - .runtime_suspend = ata_port_suspend, - .runtime_resume = ata_port_resume_common, + .suspend = ata_port_pm_suspend, + .resume = ata_port_pm_resume, + .freeze = ata_port_pm_freeze, + .thaw = ata_port_pm_resume, + .poweroff = ata_port_pm_poweroff, + .restore = ata_port_pm_resume, + + .runtime_suspend = ata_port_runtime_suspend, + .runtime_resume = ata_port_runtime_resume, .runtime_idle = ata_port_runtime_idle, }; +/* sas ports don't participate in pm runtime management of ata_ports, + * and need to resume ata devices at the domain level, not the per-port + * level. sas suspend/resume is async to allow parallel port recovery + * since sas has multiple ata_port instances per Scsi_Host. + */ +void ata_sas_port_suspend(struct ata_port *ap) +{ + ata_port_suspend_async(ap, PMSG_SUSPEND); +} +EXPORT_SYMBOL_GPL(ata_sas_port_suspend); + +void ata_sas_port_resume(struct ata_port *ap) +{ + ata_port_resume_async(ap, PMSG_RESUME); +} +EXPORT_SYMBOL_GPL(ata_sas_port_resume); + /** * ata_host_suspend - suspend host * @host: host to suspend @@ -5532,6 +5698,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap->pflags |= ATA_PFLAG_INITIALIZING | ATA_PFLAG_FROZEN; ap->lock = &host->lock; ap->print_id = -1; + ap->local_port_no = -1; ap->host = host; ap->dev = host->dev; @@ -5915,50 +6082,47 @@ int ata_host_start(struct ata_host *host) } /** - * ata_sas_host_init - Initialize a host struct + * ata_sas_host_init - Initialize a host struct for sas (ipr, libsas) * @host: host to initialize * @dev: device host is attached to - * @flags: host flags * @ops: port_ops * - * LOCKING: - * PCI/etc. bus probe sem. - * */ -/* KILLME - the only user left is ipr */ void ata_host_init(struct ata_host *host, struct device *dev, - unsigned long flags, struct ata_port_operations *ops) + struct ata_port_operations *ops) { spin_lock_init(&host->lock); mutex_init(&host->eh_mutex); + host->n_tags = ATA_MAX_QUEUE - 1; host->dev = dev; - host->flags = flags; host->ops = ops; } -int ata_port_probe(struct ata_port *ap) +void __ata_port_probe(struct ata_port *ap) { - int rc = 0; + struct ata_eh_info *ehi = &ap->link.eh_info; + unsigned long flags; - /* probe */ - if (ap->ops->error_handler) { - struct ata_eh_info *ehi = &ap->link.eh_info; - unsigned long flags; + /* kick EH for boot probing */ + spin_lock_irqsave(ap->lock, flags); - /* kick EH for boot probing */ - spin_lock_irqsave(ap->lock, flags); + ehi->probe_mask |= ATA_ALL_DEVICES; + ehi->action |= ATA_EH_RESET; + ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; - ehi->probe_mask |= ATA_ALL_DEVICES; - ehi->action |= ATA_EH_RESET; - ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; + ap->pflags &= ~ATA_PFLAG_INITIALIZING; + ap->pflags |= ATA_PFLAG_LOADING; + ata_port_schedule_eh(ap); - ap->pflags &= ~ATA_PFLAG_INITIALIZING; - ap->pflags |= ATA_PFLAG_LOADING; - ata_port_schedule_eh(ap); + spin_unlock_irqrestore(ap->lock, flags); +} - spin_unlock_irqrestore(ap->lock, flags); +int ata_port_probe(struct ata_port *ap) +{ + int rc = 0; - /* wait for EH to finish */ + if (ap->ops->error_handler) { + __ata_port_probe(ap); ata_port_wait_eh(ap); } else { DPRINTK("ata%u: bus probe begin\n", ap->print_id); @@ -6011,6 +6175,8 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) { int i, rc; + host->n_tags = clamp(sht->can_queue, 1, ATA_MAX_QUEUE - 1); + /* host must have been started */ if (!(host->flags & ATA_HOST_STARTED)) { dev_err(host->dev, "BUG: trying to register unstarted host\n"); @@ -6026,9 +6192,10 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) kfree(host->ports[i]); /* give ports names and add SCSI hosts */ - for (i = 0; i < host->n_ports; i++) - host->ports[i]->print_id = ata_print_id++; - + for (i = 0; i < host->n_ports; i++) { + host->ports[i]->print_id = atomic_inc_return(&ata_print_id); + host->ports[i]->local_port_no = i + 1; + } /* Create associated sysfs transport objects */ for (i = 0; i < host->n_ports; i++) { @@ -6042,9 +6209,6 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) if (rc) goto err_tadd; - /* associate with ACPI nodes */ - ata_acpi_associate(host); - /* set cable, sata_spd_limit and report */ for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; @@ -6158,6 +6322,8 @@ int ata_host_activate(struct ata_host *host, int irq, static void ata_port_detach(struct ata_port *ap) { unsigned long flags; + struct ata_link *link; + struct ata_device *dev; if (!ap->ops->error_handler) goto skip_eh; @@ -6177,15 +6343,21 @@ static void ata_port_detach(struct ata_port *ap) cancel_delayed_work_sync(&ap->hotplug_task); skip_eh: + /* clean up zpodd on port removal */ + ata_for_each_link(link, ap, HOST_FIRST) { + ata_for_each_dev(dev, link, ALL) { + if (zpodd_dev_enabled(dev)) + zpodd_exit(dev); + } + } if (ap->pmp_link) { int i; for (i = 0; i < SATA_PMP_MAX_PORTS; i++) ata_tlink_delete(&ap->pmp_link[i]); } - ata_tport_delete(ap); - /* remove the associated SCSI host */ scsi_remove_host(ap->scsi_host); + ata_tport_delete(ap); } /** @@ -6223,8 +6395,7 @@ void ata_host_detach(struct ata_host *host) */ void ata_pci_remove_one(struct pci_dev *pdev) { - struct device *dev = &pdev->dev; - struct ata_host *host = dev_get_drvdata(dev); + struct ata_host *host = pci_get_drvdata(pdev); ata_host_detach(host); } @@ -6293,7 +6464,7 @@ int ata_pci_device_do_resume(struct pci_dev *pdev) int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc = 0; rc = ata_host_suspend(host, mesg); @@ -6307,7 +6478,7 @@ int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) int ata_pci_device_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -6319,6 +6490,26 @@ int ata_pci_device_resume(struct pci_dev *pdev) #endif /* CONFIG_PCI */ +/** + * ata_platform_remove_one - Platform layer callback for device removal + * @pdev: Platform device that was removed + * + * Platform layer indicates to libata via this hook that hot-unplug or + * module unload event has occurred. Detach all ports. Resource + * release is handled via devres. + * + * LOCKING: + * Inherited from platform layer (may sleep). + */ +int ata_platform_remove_one(struct platform_device *pdev) +{ + struct ata_host *host = platform_get_drvdata(pdev); + + ata_host_detach(host); + + return 0; +} + static int __init ata_parse_force_one(char **cur, struct ata_force_ent *force_ent, const char **reason) @@ -6377,6 +6568,9 @@ static int __init ata_parse_force_one(char **cur, { "nohrst", .lflags = ATA_LFLAG_NO_HRST }, { "nosrst", .lflags = ATA_LFLAG_NO_SRST }, { "norst", .lflags = ATA_LFLAG_NO_HRST | ATA_LFLAG_NO_SRST }, + { "rstonce", .lflags = ATA_LFLAG_RST_ONCE }, + { "atapi_dmadir", .horkage_on = ATA_HORKAGE_ATAPI_DMADIR }, + { "disable", .horkage_on = ATA_HORKAGE_DISABLE }, }; char *start = *cur, *p = *cur; char *id, *val, *endp; @@ -6633,6 +6827,8 @@ struct ata_port_operations ata_dummy_port_ops = { .qc_prep = ata_noop_qc_prep, .qc_issue = ata_dummy_qc_issue, .error_handler = ata_dummy_error_handler, + .sched_eh = ata_std_sched_eh, + .end_eh = ata_std_end_eh, }; const struct ata_port_info ata_dummy_port_info = { @@ -6808,6 +7004,8 @@ EXPORT_SYMBOL_GPL(ata_pci_device_resume); #endif /* CONFIG_PM */ #endif /* CONFIG_PCI */ +EXPORT_SYMBOL_GPL(ata_platform_remove_one); + EXPORT_SYMBOL_GPL(__ata_ehi_push_desc); EXPORT_SYMBOL_GPL(ata_ehi_push_desc); EXPORT_SYMBOL_GPL(ata_ehi_clear_desc); diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a9b28203800..dad83df555c 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1,7 +1,7 @@ /* * libata-eh.c - libata error handling * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -95,12 +95,13 @@ enum { * represents timeout for that try. The first try can be soft or * hardreset. All others are hardreset if available. In most cases * the first reset w/ 10sec timeout should succeed. Following entries - * are mostly for error handling, hotplug and retarded devices. + * are mostly for error handling, hotplug and those outlier devices that + * take an exceptionally long time to recover from reset. */ static const unsigned long ata_eh_reset_timeouts[] = { 10000, /* most drives spin up by 10sec */ 10000, /* > 99% working drives spin up before 20sec */ - 35000, /* give > 30 secs of idleness for retarded devices */ + 35000, /* give > 30 secs of idleness for outlier devices */ 5000, /* and sweet one last chance */ ULONG_MAX, /* > 1 min has elapsed, give up */ }; @@ -419,7 +420,7 @@ int ata_ering_map(struct ata_ering *ering, return rc; } -int ata_ering_clear_cb(struct ata_ering_entry *ent, void *void_arg) +static int ata_ering_clear_cb(struct ata_ering_entry *ent, void *void_arg) { ent->eflags |= ATA_EFLAG_OLD_ER; return 0; @@ -793,12 +794,12 @@ void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap) ata_for_each_link(link, ap, HOST_FIRST) memset(&link->eh_info, 0, sizeof(link->eh_info)); - /* Clear host_eh_scheduled while holding ap->lock such - * that if exception occurs after this point but - * before EH completion, SCSI midlayer will + /* end eh (clear host_eh_scheduled) while holding + * ap->lock such that if exception occurs after this + * point but before EH completion, SCSI midlayer will * re-initiate EH. */ - host->host_eh_scheduled = 0; + ap->ops->end_eh(ap); spin_unlock_irqrestore(ap->lock, flags); ata_eh_release(ap); @@ -863,6 +864,7 @@ void ata_port_wait_eh(struct ata_port *ap) goto retry; } } +EXPORT_SYMBOL_GPL(ata_port_wait_eh); static int ata_eh_nr_in_flight(struct ata_port *ap) { @@ -985,16 +987,13 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) } /** - * ata_port_schedule_eh - schedule error handling without a qc - * @ap: ATA port to schedule EH for - * - * Schedule error handling for @ap. EH will kick in as soon as - * all commands are drained. + * ata_std_sched_eh - non-libsas ata_ports issue eh with this common routine + * @ap: ATA port to schedule EH for * - * LOCKING: + * LOCKING: inherited from ata_port_schedule_eh * spin_lock_irqsave(host lock) */ -void ata_port_schedule_eh(struct ata_port *ap) +void ata_std_sched_eh(struct ata_port *ap) { WARN_ON(!ap->ops->error_handler); @@ -1006,6 +1005,44 @@ void ata_port_schedule_eh(struct ata_port *ap) DPRINTK("port EH scheduled\n"); } +EXPORT_SYMBOL_GPL(ata_std_sched_eh); + +/** + * ata_std_end_eh - non-libsas ata_ports complete eh with this common routine + * @ap: ATA port to end EH for + * + * In the libata object model there is a 1:1 mapping of ata_port to + * shost, so host fields can be directly manipulated under ap->lock, in + * the libsas case we need to hold a lock at the ha->level to coordinate + * these events. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_std_end_eh(struct ata_port *ap) +{ + struct Scsi_Host *host = ap->scsi_host; + + host->host_eh_scheduled = 0; +} +EXPORT_SYMBOL(ata_std_end_eh); + + +/** + * ata_port_schedule_eh - schedule error handling without a qc + * @ap: ATA port to schedule EH for + * + * Schedule error handling for @ap. EH will kick in as soon as + * all commands are drained. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_port_schedule_eh(struct ata_port *ap) +{ + /* see: ata_std_sched_eh, unless you know better */ + ap->ops->sched_eh(ap); +} static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link) { @@ -1286,14 +1323,14 @@ void ata_eh_qc_complete(struct ata_queued_cmd *qc) * should be retried. To be used from EH. * * SCSI midlayer limits the number of retries to scmd->allowed. - * scmd->retries is decremented for commands which get retried + * scmd->allowed is incremented for commands which get retried * due to unrelated failures (qc->err_mask is zero). */ void ata_eh_qc_retry(struct ata_queued_cmd *qc) { struct scsi_cmnd *scmd = qc->scsicmd; - if (!qc->err_mask && scmd->retries) - scmd->retries--; + if (!qc->err_mask) + scmd->allowed++; __ata_eh_qc_complete(qc); } @@ -1451,6 +1488,7 @@ static const char *ata_err_string(unsigned int err_mask) /** * ata_read_log_page - read a specific log page * @dev: target device + * @log: log to read * @page: page to read * @buf: buffer to store read page * @sectors: number of sectors to read @@ -1463,17 +1501,18 @@ static const char *ata_err_string(unsigned int err_mask) * RETURNS: * 0 on success, AC_ERR_* mask otherwise. */ -static unsigned int ata_read_log_page(struct ata_device *dev, - u8 page, void *buf, unsigned int sectors) +unsigned int ata_read_log_page(struct ata_device *dev, u8 log, + u8 page, void *buf, unsigned int sectors) { struct ata_taskfile tf; unsigned int err_mask; - DPRINTK("read log page - page %d\n", page); + DPRINTK("read log page - log 0x%x, page 0x%x\n", log, page); ata_tf_init(dev, &tf); tf.command = ATA_CMD_READ_LOG_EXT; - tf.lbal = page; + tf.lbal = log; + tf.lbam = page; tf.nsect = sectors; tf.hob_nsect = sectors >> 8; tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_LBA48 | ATA_TFLAG_DEVICE; @@ -1509,7 +1548,7 @@ static int ata_eh_read_log_10h(struct ata_device *dev, u8 csum; int i; - err_mask = ata_read_log_page(dev, ATA_LOG_SATA_NCQ, buf, 1); + err_mask = ata_read_log_page(dev, ATA_LOG_SATA_NCQ, 0, buf, 1); if (err_mask) return -EIO; @@ -1553,7 +1592,7 @@ static int ata_eh_read_log_10h(struct ata_device *dev, * RETURNS: * 0 on success, AC_ERR_* mask on failure. */ -static unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) +unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) { u8 cdb[ATAPI_CDB_LEN] = { TEST_UNIT_READY, 0, 0, 0, 0, 0 }; struct ata_taskfile tf; @@ -1586,7 +1625,7 @@ static unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) * RETURNS: * 0 on success, AC_ERR_* mask on failure */ -static unsigned int atapi_eh_request_sense(struct ata_device *dev, +unsigned int atapi_eh_request_sense(struct ata_device *dev, u8 *sense_buf, u8 dfl_sense_key) { u8 cdb[ATAPI_CDB_LEN] = @@ -1772,7 +1811,7 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, case ATA_DEV_ATA: if (err & ATA_ICRC) qc->err_mask |= AC_ERR_ATA_BUS; - if (err & ATA_UNC) + if (err & (ATA_UNC | ATA_AMNF)) qc->err_mask |= AC_ERR_MEDIA; if (err & ATA_IDNF) qc->err_mask |= AC_ERR_INVALID; @@ -2046,6 +2085,26 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev, } /** + * ata_eh_worth_retry - analyze error and decide whether to retry + * @qc: qc to possibly retry + * + * Look at the cause of the error and decide if a retry + * might be useful or not. We don't want to retry media errors + * because the drive itself has probably already taken 10-30 seconds + * doing its own internal retries before reporting the failure. + */ +static inline int ata_eh_worth_retry(struct ata_queued_cmd *qc) +{ + if (qc->err_mask & AC_ERR_MEDIA) + return 0; /* don't retry media errors */ + if (qc->flags & ATA_QCFLAG_IO) + return 1; /* otherwise retry anything from fs stack */ + if (qc->err_mask & AC_ERR_INVALID) + return 0; /* don't retry these */ + return qc->err_mask != AC_ERR_DEV; /* retry if not dev error */ +} + +/** * ata_eh_link_autopsy - analyze error and determine recovery action * @link: host link to perform autopsy on * @@ -2119,9 +2178,7 @@ static void ata_eh_link_autopsy(struct ata_link *link) qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_OTHER); /* determine whether the command is worth retrying */ - if (qc->flags & ATA_QCFLAG_IO || - (!(qc->err_mask & AC_ERR_INVALID) && - qc->err_mask != AC_ERR_DEV)) + if (ata_eh_worth_retry(qc)) qc->flags |= ATA_QCFLAG_RETRY; /* accumulate error info */ @@ -2237,6 +2294,7 @@ const char *ata_get_cmd_descript(u8 command) { ATA_CMD_IDLE, "IDLE" }, { ATA_CMD_EDD, "EXECUTE DEVICE DIAGNOSTIC" }, { ATA_CMD_DOWNLOAD_MICRO, "DOWNLOAD MICROCODE" }, + { ATA_CMD_DOWNLOAD_MICRO_DMA, "DOWNLOAD MICROCODE DMA" }, { ATA_CMD_NOP, "NOP" }, { ATA_CMD_FLUSH, "FLUSH CACHE" }, { ATA_CMD_FLUSH_EXT, "FLUSH CACHE EXT" }, @@ -2257,6 +2315,8 @@ const char *ata_get_cmd_descript(u8 command) { ATA_CMD_WRITE_QUEUED_FUA_EXT, "WRITE DMA QUEUED FUA EXT" }, { ATA_CMD_FPDMA_READ, "READ FPDMA QUEUED" }, { ATA_CMD_FPDMA_WRITE, "WRITE FPDMA QUEUED" }, + { ATA_CMD_FPDMA_SEND, "SEND FPDMA QUEUED" }, + { ATA_CMD_FPDMA_RECV, "RECEIVE FPDMA QUEUED" }, { ATA_CMD_PIO_READ, "READ SECTOR(S)" }, { ATA_CMD_PIO_READ_EXT, "READ SECTOR(S) EXT" }, { ATA_CMD_PIO_WRITE, "WRITE SECTOR(S)" }, @@ -2283,12 +2343,15 @@ const char *ata_get_cmd_descript(u8 command) { ATA_CMD_WRITE_LOG_EXT, "WRITE LOG EXT" }, { ATA_CMD_READ_LOG_DMA_EXT, "READ LOG DMA EXT" }, { ATA_CMD_WRITE_LOG_DMA_EXT, "WRITE LOG DMA EXT" }, + { ATA_CMD_TRUSTED_NONDATA, "TRUSTED NON-DATA" }, { ATA_CMD_TRUSTED_RCV, "TRUSTED RECEIVE" }, { ATA_CMD_TRUSTED_RCV_DMA, "TRUSTED RECEIVE DMA" }, { ATA_CMD_TRUSTED_SND, "TRUSTED SEND" }, { ATA_CMD_TRUSTED_SND_DMA, "TRUSTED SEND DMA" }, { ATA_CMD_PMP_READ, "READ BUFFER" }, + { ATA_CMD_PMP_READ_DMA, "READ BUFFER DMA" }, { ATA_CMD_PMP_WRITE, "WRITE BUFFER" }, + { ATA_CMD_PMP_WRITE_DMA, "WRITE BUFFER DMA" }, { ATA_CMD_CONF_OVERLAY, "DEVICE CONFIGURATION OVERLAY" }, { ATA_CMD_SEC_SET_PASS, "SECURITY SET PASSWORD" }, { ATA_CMD_SEC_UNLOCK, "SECURITY UNLOCK" }, @@ -2307,6 +2370,8 @@ const char *ata_get_cmd_descript(u8 command) { ATA_CMD_CFA_TRANS_SECT, "CFA TRANSLATE SECTOR" }, { ATA_CMD_CFA_ERASE, "CFA ERASE SECTORS" }, { ATA_CMD_CFA_WRITE_MULT_NE, "CFA WRITE MULTIPLE WITHOUT ERASE" }, + { ATA_CMD_REQ_SENSE_DATA, "REQUEST SENSE DATA EXT" }, + { ATA_CMD_SANITIZE_DEVICE, "SANITIZE DEVICE" }, { ATA_CMD_READ_LONG, "READ LONG (with retries)" }, { ATA_CMD_READ_LONG_ONCE, "READ LONG (without retries)" }, { ATA_CMD_WRITE_LONG, "WRITE LONG (with retries)" }, @@ -2338,7 +2403,7 @@ static void ata_eh_link_report(struct ata_link *link) struct ata_port *ap = link->ap; struct ata_eh_context *ehc = &link->eh_context; const char *frozen, *desc; - char tries_buf[6]; + char tries_buf[6] = ""; int tag, nr_failed = 0; if (ehc->i.flags & ATA_EHI_QUIET) @@ -2369,9 +2434,8 @@ static void ata_eh_link_report(struct ata_link *link) if (ap->pflags & ATA_PFLAG_FROZEN) frozen = " frozen"; - memset(tries_buf, 0, sizeof(tries_buf)); if (ap->eh_tries < ATA_EH_MAX_TRIES) - snprintf(tries_buf, sizeof(tries_buf) - 1, " t%d", + snprintf(tries_buf, sizeof(tries_buf), " t%d", ap->eh_tries); if (ehc->i.dev) { @@ -2492,11 +2556,12 @@ static void ata_eh_link_report(struct ata_link *link) } if (cmd->command != ATA_CMD_PACKET && - (res->feature & (ATA_ICRC | ATA_UNC | ATA_IDNF | - ATA_ABORTED))) - ata_dev_err(qc->dev, "error: { %s%s%s%s}\n", + (res->feature & (ATA_ICRC | ATA_UNC | ATA_AMNF | + ATA_IDNF | ATA_ABORTED))) + ata_dev_err(qc->dev, "error: { %s%s%s%s%s}\n", res->feature & ATA_ICRC ? "ICRC " : "", res->feature & ATA_UNC ? "UNC " : "", + res->feature & ATA_AMNF ? "AMNF " : "", res->feature & ATA_IDNF ? "IDNF " : "", res->feature & ATA_ABORTED ? "ABRT " : ""); #endif @@ -2569,6 +2634,8 @@ int ata_eh_reset(struct ata_link *link, int classify, */ while (ata_eh_reset_timeouts[max_tries] != ULONG_MAX) max_tries++; + if (link->flags & ATA_LFLAG_RST_ONCE) + max_tries = 1; if (link->flags & ATA_LFLAG_NO_HRST) hardreset = NULL; if (link->flags & ATA_LFLAG_NO_SRST) @@ -2599,6 +2666,7 @@ int ata_eh_reset(struct ata_link *link, int classify, * bus as we may be talking too fast. */ dev->pio_mode = XFER_PIO_0; + dev->dma_mode = 0xff; /* If the controller has a pio mode setup function * then use it to set the chipset to rights. Don't @@ -2950,7 +3018,7 @@ static inline void ata_eh_pull_park_action(struct ata_port *ap) * ourselves at the beginning of each pass over the loop. * * Additionally, all write accesses to &ap->park_req_pending - * through INIT_COMPLETION() (see below) or complete_all() + * through reinit_completion() (see below) or complete_all() * (see ata_scsi_park_store()) are protected by the host lock. * As a result we have that park_req_pending.done is zero on * exit from this function, i.e. when ATA_EH_PARK actions for @@ -2964,7 +3032,7 @@ static inline void ata_eh_pull_park_action(struct ata_port *ap) */ spin_lock_irqsave(ap->lock, flags); - INIT_COMPLETION(ap->park_req_pending); + reinit_completion(&ap->park_req_pending); ata_for_each_link(link, ap, EDGE) { ata_for_each_dev(dev, link, ALL) { struct ata_eh_info *ehi = &link->eh_info; @@ -3500,7 +3568,8 @@ static int ata_count_probe_trials_cb(struct ata_ering_entry *ent, void *void_arg u64 now = get_jiffies_64(); int *trials = void_arg; - if (ent->timestamp < now - min(now, interval)) + if ((ent->eflags & ATA_EFLAG_OLD_ER) || + (ent->timestamp < now - min(now, interval))) return -1; (*trials)++; @@ -3797,6 +3866,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, rc = atapi_eh_clear_ua(dev); if (rc) goto rest_fail; + if (zpodd_dev_enabled(dev)) + zpodd_post_poweron(dev); } } @@ -3962,11 +4033,12 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) { unsigned long flags; int rc = 0; + struct ata_device *dev; /* are we suspending? */ spin_lock_irqsave(ap->lock, flags); if (!(ap->pflags & ATA_PFLAG_PM_PENDING) || - ap->pm_mesg.event == PM_EVENT_ON) { + ap->pm_mesg.event & PM_EVENT_RESUME) { spin_unlock_irqrestore(ap->lock, flags); return; } @@ -3974,6 +4046,18 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); + /* + * If we have a ZPODD attached, check its zero + * power ready status before the port is frozen. + * Only needed for runtime suspend. + */ + if (PMSG_IS_AUTO(ap->pm_mesg)) { + ata_for_each_dev(dev, &ap->link, ENABLED) { + if (zpodd_dev_enabled(dev)) + zpodd_on_suspend(dev); + } + } + /* tell ACPI we're suspending */ rc = ata_acpi_on_suspend(ap); if (rc) @@ -3985,9 +4069,9 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) if (ap->ops->port_suspend) rc = ap->ops->port_suspend(ap, ap->pm_mesg); - ata_acpi_set_state(ap, PMSG_SUSPEND); + ata_acpi_set_state(ap, ap->pm_mesg); out: - /* report result */ + /* update the flags */ spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~ATA_PFLAG_PM_PENDING; @@ -3996,11 +4080,6 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) else if (ap->pflags & ATA_PFLAG_FROZEN) ata_port_schedule_eh(ap); - if (ap->pm_result) { - *ap->pm_result = rc; - ap->pm_result = NULL; - } - spin_unlock_irqrestore(ap->lock, flags); return; @@ -4025,7 +4104,7 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) /* are we resuming? */ spin_lock_irqsave(ap->lock, flags); if (!(ap->pflags & ATA_PFLAG_PM_PENDING) || - ap->pm_mesg.event != PM_EVENT_ON) { + !(ap->pm_mesg.event & PM_EVENT_RESUME)) { spin_unlock_irqrestore(ap->lock, flags); return; } @@ -4044,7 +4123,7 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) ata_for_each_dev(dev, link, ALL) ata_ering_clear(&dev->ering); - ata_acpi_set_state(ap, PMSG_ON); + ata_acpi_set_state(ap, ap->pm_mesg); if (ap->ops->port_resume) rc = ap->ops->port_resume(ap); @@ -4052,13 +4131,9 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) /* tell ACPI that we're resuming */ ata_acpi_on_resume(ap); - /* report result */ + /* update the flags */ spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED); - if (ap->pm_result) { - *ap->pm_result = rc; - ap->pm_result = NULL; - } spin_unlock_irqrestore(ap->lock, flags); } #endif /* CONFIG_PM */ diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 21b80c555c6..7ccc084bf1d 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -289,24 +289,24 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) /* Disable sending Early R_OK. * With "cached read" HDD testing and multiple ports busy on a SATA - * host controller, 3726 PMP will very rarely drop a deferred + * host controller, 3x26 PMP will very rarely drop a deferred * R_OK that was intended for the host. Symptom will be all * 5 drives under test will timeout, get reset, and recover. */ - if (vendor == 0x1095 && devid == 0x3726) { + if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { u32 reg; err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, ®); if (err_mask) { rc = -EIO; - reason = "failed to read Sil3726 Private Register"; + reason = "failed to read Sil3x26 Private Register"; goto fail; } reg &= ~0x1; err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg); if (err_mask) { rc = -EIO; - reason = "failed to write Sil3726 Private Register"; + reason = "failed to write Sil3x26 Private Register"; goto fail; } } @@ -383,15 +383,19 @@ static void sata_pmp_quirks(struct ata_port *ap) u16 devid = sata_pmp_gscr_devid(gscr); struct ata_link *link; - if (vendor == 0x1095 && devid == 0x3726) { - /* sil3726 quirks */ + if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { + /* sil3x26 quirks */ ata_for_each_link(link, ap, EDGE) { /* link reports offline after LPM */ link->flags |= ATA_LFLAG_NO_LPM; - /* Class code report is unreliable. */ + /* + * Class code report is unreliable and SRST times + * out under certain configurations. + */ if (link->pmp < 5) - link->flags |= ATA_LFLAG_ASSUME_ATA; + link->flags |= ATA_LFLAG_NO_SRST | + ATA_LFLAG_ASSUME_ATA; /* port 5 is for SEMB device and it doesn't like SRST */ if (link->pmp == 5) @@ -399,20 +403,17 @@ static void sata_pmp_quirks(struct ata_port *ap) ATA_LFLAG_ASSUME_SEMB; } } else if (vendor == 0x1095 && devid == 0x4723) { - /* sil4723 quirks */ - ata_for_each_link(link, ap, EDGE) { - /* link reports offline after LPM */ - link->flags |= ATA_LFLAG_NO_LPM; - - /* class code report is unreliable */ - if (link->pmp < 2) - link->flags |= ATA_LFLAG_ASSUME_ATA; - - /* the config device at port 2 locks up on SRST */ - if (link->pmp == 2) - link->flags |= ATA_LFLAG_NO_SRST | - ATA_LFLAG_ASSUME_ATA; - } + /* + * sil4723 quirks + * + * Link reports offline after LPM. Class code report is + * unreliable. SIMG PMPs never got SRST reliable and the + * config device at port 2 locks up on SRST. + */ + ata_for_each_link(link, ap, EDGE) + link->flags |= ATA_LFLAG_NO_LPM | + ATA_LFLAG_NO_SRST | + ATA_LFLAG_ASSUME_ATA; } else if (vendor == 0x1095 && devid == 0x4726) { /* sil4726 quirks */ ata_for_each_link(link, ap, EDGE) { @@ -446,8 +447,11 @@ static void sata_pmp_quirks(struct ata_port *ap) * otherwise. Don't try hard to recover it. */ ap->pmp_link[ap->nr_pmp_links - 1].flags |= ATA_LFLAG_NO_RETRY; - } else if (vendor == 0x197b && devid == 0x2352) { - /* chip found in Thermaltake BlackX Duet, jmicron JMB350? */ + } else if (vendor == 0x197b && (devid == 0x2352 || devid == 0x0325)) { + /* + * 0x2352: found in Thermaltake BlackX Duet, jmicron JMB350? + * 0x0325: jmicron JMB394. + */ ata_for_each_link(link, ap, EDGE) { /* SRST breaks detection and disks get misclassified * LPM disabled to avoid potential problems @@ -529,8 +533,6 @@ int sata_pmp_attach(struct ata_device *dev) ata_for_each_link(tlink, ap, EDGE) sata_link_init_spd(tlink); - ata_acpi_associate_sata_port(ap); - return 0; fail: @@ -570,8 +572,6 @@ static void sata_pmp_detach(struct ata_device *dev) ap->nr_pmp_links = 0; link->pmp = 0; spin_unlock_irqrestore(ap->lock, flags); - - ata_acpi_associate_sata_port(ap); } /** diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 508a60bfe5c..72691fd9394 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1,7 +1,7 @@ /* * libata-scsi.c - helper library for ATA * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -111,12 +111,14 @@ static const char *ata_lpm_policy_names[] = { [ATA_LPM_MIN_POWER] = "min_power", }; -static ssize_t ata_scsi_lpm_store(struct device *dev, +static ssize_t ata_scsi_lpm_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { - struct Scsi_Host *shost = class_to_shost(dev); + struct Scsi_Host *shost = class_to_shost(device); struct ata_port *ap = ata_shost_to_port(shost); + struct ata_link *link; + struct ata_device *dev; enum ata_lpm_policy policy; unsigned long flags; @@ -132,10 +134,20 @@ static ssize_t ata_scsi_lpm_store(struct device *dev, return -EINVAL; spin_lock_irqsave(ap->lock, flags); + + ata_for_each_link(link, ap, EDGE) { + ata_for_each_dev(dev, &ap->link, ENABLED) { + if (dev->horkage & ATA_HORKAGE_NOLPM) { + count = -EOPNOTSUPP; + goto out_unlock; + } + } + } + ap->target_lpm_policy = policy; ata_port_schedule_eh(ap); +out_unlock: spin_unlock_irqrestore(ap->lock, flags); - return count; } @@ -205,8 +217,10 @@ static ssize_t ata_scsi_park_store(struct device *device, unsigned long flags; int rc; - rc = strict_strtol(buf, 10, &input); - if (rc || input < -2) + rc = kstrtol(buf, 10, &input); + if (rc) + return rc; + if (input < -2) return -EINVAL; if (input > ATA_TMOUT_MAX_PARK) { rc = -EOVERFLOW; @@ -309,7 +323,8 @@ ata_scsi_activity_show(struct device *dev, struct device_attribute *attr, struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *atadev = ata_scsi_find_dev(ap, sdev); - if (ap->ops->sw_activity_show && (ap->flags & ATA_FLAG_SW_ACTIVITY)) + if (atadev && ap->ops->sw_activity_show && + (ap->flags & ATA_FLAG_SW_ACTIVITY)) return ap->ops->sw_activity_show(atadev, buf); return -EINVAL; } @@ -324,7 +339,8 @@ ata_scsi_activity_store(struct device *dev, struct device_attribute *attr, enum sw_activity val; int rc; - if (ap->ops->sw_activity_store && (ap->flags & ATA_FLAG_SW_ACTIVITY)) { + if (atadev && ap->ops->sw_activity_store && + (ap->flags & ATA_FLAG_SW_ACTIVITY)) { val = simple_strtoul(buf, NULL, 0); switch (val) { case OFF: case BLINK_ON: case BLINK_OFF: @@ -530,8 +546,8 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) struct scsi_sense_hdr sshdr; scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sshdr); - if (sshdr.sense_key == 0 && - sshdr.asc == 0 && sshdr.ascq == 0) + if (sshdr.sense_key == RECOVERED_ERROR && + sshdr.asc == 0 && sshdr.ascq == 0x1d) cmd_result &= ~SAM_STAT_CHECK_CONDITION; } @@ -616,8 +632,8 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) struct scsi_sense_hdr sshdr; scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sshdr); - if (sshdr.sense_key == 0 && - sshdr.asc == 0 && sshdr.ascq == 0) + if (sshdr.sense_key == RECOVERED_ERROR && + sshdr.asc == 0 && sshdr.ascq == 0x1d) cmd_result &= ~SAM_STAT_CHECK_CONDITION; } @@ -846,25 +862,24 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, /* Bad address mark */ {0x01, MEDIUM_ERROR, 0x13, 0x00}, // Address mark not found Address mark not found for data field /* TRK0 */ - {0x02, HARDWARE_ERROR, 0x00, 0x00}, // Track 0 not found Hardware error - /* Abort & !ICRC */ - {0x04, ABORTED_COMMAND, 0x00, 0x00}, // Aborted command Aborted command + {0x02, HARDWARE_ERROR, 0x00, 0x00}, // Track 0 not found Hardware error + /* Abort: 0x04 is not translated here, see below */ /* Media change request */ {0x08, NOT_READY, 0x04, 0x00}, // Media change request FIXME: faking offline - /* SRV */ - {0x10, ABORTED_COMMAND, 0x14, 0x00}, // ID not found Recorded entity not found - /* Media change */ - {0x08, NOT_READY, 0x04, 0x00}, // Media change FIXME: faking offline + /* SRV/IDNF */ + {0x10, ILLEGAL_REQUEST, 0x21, 0x00}, // ID not found Logical address out of range + /* MC */ + {0x20, UNIT_ATTENTION, 0x28, 0x00}, // Media Changed Not ready to ready change, medium may have changed /* ECC */ {0x40, MEDIUM_ERROR, 0x11, 0x04}, // Uncorrectable ECC error Unrecovered read error /* BBD - block marked bad */ - {0x80, MEDIUM_ERROR, 0x11, 0x04}, // Block marked bad Medium error, unrecovered read error + {0x80, MEDIUM_ERROR, 0x11, 0x04}, // Block marked bad Medium error, unrecovered read error {0xFF, 0xFF, 0xFF, 0xFF}, // END mark }; static const unsigned char stat_table[][4] = { /* Must be first because BUSY means no other bits valid */ {0x80, ABORTED_COMMAND, 0x47, 0x00}, // Busy, fake parity for now - {0x20, HARDWARE_ERROR, 0x00, 0x00}, // Device fault + {0x20, HARDWARE_ERROR, 0x44, 0x00}, // Device fault, internal target failure {0x08, ABORTED_COMMAND, 0x47, 0x00}, // Timed out in xfer, fake parity for now {0x04, RECOVERED_ERROR, 0x11, 0x00}, // Recovered ECC error Medium error, recovered {0xFF, 0xFF, 0xFF, 0xFF}, // END mark @@ -889,13 +904,13 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, goto translate_done; } } - /* No immediate match */ - if (verbose) - printk(KERN_WARNING "ata%u: no sense translation for " - "error 0x%02x\n", id, drv_err); } - /* Fall back to interpreting status bits */ + /* + * Fall back to interpreting status bits. Note that if the drv_err + * has only the ABRT bit set, we decode drv_stat. ABRT by itself + * is not descriptive enough. + */ for (i = 0; stat_table[i][0] != 0xFF; i++) { if (stat_table[i][0] & drv_stat) { *sk = stat_table[i][1]; @@ -904,13 +919,11 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, goto translate_done; } } - /* No error? Undecoded? */ - if (verbose) - printk(KERN_WARNING "ata%u: no sense translation for " - "status: 0x%02x\n", id, drv_stat); - /* We need a sensible error return here, which is tricky, and one - that won't cause people to do things like return a disk wrongly */ + /* + * We need a sensible error return here, which is tricky, and one + * that won't cause people to do things like return a disk wrongly. + */ *sk = ABORTED_COMMAND; *asc = 0x00; *ascq = 0x00; @@ -931,7 +944,11 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, * block specified for the ATA pass through commands. Regardless * of whether the command errored or not, return a sense * block. Copy all controller registers into the sense - * block. Clear sense key, ASC & ASCQ if there is no error. + * block. If there was no error, we get the request from an ATA + * passthrough command, so we use the following sense data: + * sk = RECOVERED ERROR + * asc,ascq = ATA PASS-THROUGH INFORMATION AVAILABLE + * * * LOCKING: * None. @@ -957,6 +974,10 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, &sb[1], &sb[2], &sb[3], verbose); sb[1] &= 0x0f; + } else { + sb[1] = RECOVERED_ERROR; + sb[2] = 0; + sb[3] = 0x1D; } /* @@ -1052,6 +1073,8 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev) { sdev->use_10_for_rw = 1; sdev->use_10_for_ms = 1; + sdev->no_report_opcodes = 1; + sdev->no_write_same = 1; /* Schedule policy is determined by ->qc_defer() callback and * it needs to see every deferred qc. Set dev_blocked to 1 to @@ -1655,7 +1678,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) if (unlikely(scmd->cmd_len < 10)) goto invalid_fld; scsi_10_lba_len(cdb, &block, &n_block); - if (unlikely(cdb[1] & (1 << 3))) + if (cdb[1] & (1 << 3)) tf_flags |= ATA_TFLAG_FUA; break; case READ_6: @@ -1675,7 +1698,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) if (unlikely(scmd->cmd_len < 16)) goto invalid_fld; scsi_16_lba_len(cdb, &block, &n_block); - if (unlikely(cdb[1] & (1 << 3))) + if (cdb[1] & (1 << 3)) tf_flags |= ATA_TFLAG_FUA; break; default: @@ -1729,10 +1752,12 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) /* For ATA pass thru (SAT) commands, generate a sense block if * user mandated it or if there's an error. Note that if we - * generate because the user forced us to, a check condition - * is generated and the ATA register values are returned + * generate because the user forced us to [CK_COND =1], a check + * condition is generated and the ATA register values are returned * whether the command completed successfully or not. If there - * was no error, SK, ASC and ASCQ will all be zero. + * was no error, we use the following sense data: + * sk = RECOVERED ERROR + * asc,ascq = ATA PASS-THROUGH INFORMATION AVAILABLE */ if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) && ((cdb[2] & 0x20) || need_sense)) { @@ -1968,7 +1993,11 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) memcpy(rbuf, hdr, sizeof(hdr)); memcpy(&rbuf[8], "ATA ", 8); ata_id_string(args->id, &rbuf[16], ATA_ID_PROD, 16); - ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4); + + /* From SAT, use last 2 words from fw rev unless they are spaces */ + ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV + 2, 4); + if (strncmp(&rbuf[32], " ", 4) == 0) + ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4); if (rbuf[32] == 0 || rbuf[32] == ' ') memcpy(&rbuf[32], "n/a ", 4); @@ -2112,7 +2141,6 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf) memcpy(&rbuf[8], "linux ", 8); memcpy(&rbuf[16], "libata ", 16); memcpy(&rbuf[32], DRV_VERSION, 4); - ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4); /* we don't store the ATA device signature, so we fake it */ @@ -2205,9 +2233,33 @@ static unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf) } /** + * modecpy - Prepare response for MODE SENSE + * @dest: output buffer + * @src: data being copied + * @n: length of mode page + * @changeable: whether changeable parameters are requested + * + * Generate a generic MODE SENSE page for either current or changeable + * parameters. + * + * LOCKING: + * None. + */ +static void modecpy(u8 *dest, const u8 *src, int n, bool changeable) +{ + if (changeable) { + memcpy(dest, src, 2); + memset(dest + 2, 0, n - 2); + } else { + memcpy(dest, src, n); + } +} + +/** * ata_msense_caching - Simulate MODE SENSE caching info page * @id: device IDENTIFY data * @buf: output buffer + * @changeable: whether changeable parameters are requested * * Generate a caching info page, which conditionally indicates * write caching to the SCSI layer, depending on device @@ -2216,12 +2268,12 @@ static unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf) * LOCKING: * None. */ -static unsigned int ata_msense_caching(u16 *id, u8 *buf) +static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) { - memcpy(buf, def_cache_mpage, sizeof(def_cache_mpage)); - if (ata_id_wcache_enabled(id)) + modecpy(buf, def_cache_mpage, sizeof(def_cache_mpage), changeable); + if (changeable || ata_id_wcache_enabled(id)) buf[2] |= (1 << 2); /* write cache enable */ - if (!ata_id_rahead_enabled(id)) + if (!changeable && !ata_id_rahead_enabled(id)) buf[12] |= (1 << 5); /* disable read ahead */ return sizeof(def_cache_mpage); } @@ -2229,30 +2281,33 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf) /** * ata_msense_ctl_mode - Simulate MODE SENSE control mode page * @buf: output buffer + * @changeable: whether changeable parameters are requested * * Generate a generic MODE SENSE control mode page. * * LOCKING: * None. */ -static unsigned int ata_msense_ctl_mode(u8 *buf) +static unsigned int ata_msense_ctl_mode(u8 *buf, bool changeable) { - memcpy(buf, def_control_mpage, sizeof(def_control_mpage)); + modecpy(buf, def_control_mpage, sizeof(def_control_mpage), changeable); return sizeof(def_control_mpage); } /** * ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page * @buf: output buffer + * @changeable: whether changeable parameters are requested * * Generate a generic MODE SENSE r/w error recovery page. * * LOCKING: * None. */ -static unsigned int ata_msense_rw_recovery(u8 *buf) +static unsigned int ata_msense_rw_recovery(u8 *buf, bool changeable) { - memcpy(buf, def_rw_recovery_mpage, sizeof(def_rw_recovery_mpage)); + modecpy(buf, def_rw_recovery_mpage, sizeof(def_rw_recovery_mpage), + changeable); return sizeof(def_rw_recovery_mpage); } @@ -2316,11 +2371,11 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) page_control = scsicmd[2] >> 6; switch (page_control) { case 0: /* current */ + case 1: /* changeable */ + case 2: /* defaults */ break; /* supported */ case 3: /* saved */ goto saving_not_supp; - case 1: /* changeable */ - case 2: /* defaults */ default: goto invalid_fld; } @@ -2341,21 +2396,21 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) switch(pg) { case RW_RECOVERY_MPAGE: - p += ata_msense_rw_recovery(p); + p += ata_msense_rw_recovery(p, page_control == 1); break; case CACHE_MPAGE: - p += ata_msense_caching(args->id, p); + p += ata_msense_caching(args->id, p, page_control == 1); break; case CONTROL_MPAGE: - p += ata_msense_ctl_mode(p); + p += ata_msense_ctl_mode(p, page_control == 1); break; case ALL_MPAGES: - p += ata_msense_rw_recovery(p); - p += ata_msense_caching(args->id, p); - p += ata_msense_ctl_mode(p); + p += ata_msense_rw_recovery(p, page_control == 1); + p += ata_msense_caching(args->id, p, page_control == 1); + p += ata_msense_ctl_mode(p, page_control == 1); break; default: /* invalid page code */ @@ -3060,12 +3115,25 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) buf = page_address(sg_page(scsi_sglist(scmd))); size = ata_set_lba_range_entries(buf, 512, block, n_block); - tf->protocol = ATA_PROT_DMA; - tf->hob_feature = 0; - tf->feature = ATA_DSM_TRIM; - tf->hob_nsect = (size / 512) >> 8; - tf->nsect = size / 512; - tf->command = ATA_CMD_DSM; + if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) { + /* Newer devices support queued TRIM commands */ + tf->protocol = ATA_PROT_NCQ; + tf->command = ATA_CMD_FPDMA_SEND; + tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f; + tf->nsect = qc->tag << 3; + tf->hob_feature = (size / 512) >> 8; + tf->feature = size / 512; + + tf->auxiliary = 1; + } else { + tf->protocol = ATA_PROT_DMA; + tf->hob_feature = 0; + tf->feature = ATA_DSM_TRIM; + tf->hob_nsect = (size / 512) >> 8; + tf->nsect = size / 512; + tf->command = ATA_CMD_DSM; + } + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_WRITE; @@ -3080,6 +3148,188 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) } /** + * ata_mselect_caching - Simulate MODE SELECT for caching info page + * @qc: Storage for translated ATA taskfile + * @buf: input buffer + * @len: number of valid bytes in the input buffer + * + * Prepare a taskfile to modify caching information for the device. + * + * LOCKING: + * None. + */ +static int ata_mselect_caching(struct ata_queued_cmd *qc, + const u8 *buf, int len) +{ + struct ata_taskfile *tf = &qc->tf; + struct ata_device *dev = qc->dev; + char mpage[CACHE_MPAGE_LEN]; + u8 wce; + + /* + * The first two bytes of def_cache_mpage are a header, so offsets + * in mpage are off by 2 compared to buf. Same for len. + */ + + if (len != CACHE_MPAGE_LEN - 2) + return -EINVAL; + + wce = buf[0] & (1 << 2); + + /* + * Check that read-only bits are not modified. + */ + ata_msense_caching(dev->id, mpage, false); + mpage[2] &= ~(1 << 2); + mpage[2] |= wce; + if (memcmp(mpage + 2, buf, CACHE_MPAGE_LEN - 2) != 0) + return -EINVAL; + + tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf->protocol = ATA_PROT_NODATA; + tf->nsect = 0; + tf->command = ATA_CMD_SET_FEATURES; + tf->feature = wce ? SETFEATURES_WC_ON : SETFEATURES_WC_OFF; + return 0; +} + +/** + * ata_scsiop_mode_select - Simulate MODE SELECT 6, 10 commands + * @qc: Storage for translated ATA taskfile + * + * Converts a MODE SELECT command to an ATA SET FEATURES taskfile. + * Assume this is invoked for direct access devices (e.g. disks) only. + * There should be no block descriptor for other device types. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + const u8 *cdb = scmd->cmnd; + const u8 *p; + u8 pg, spg; + unsigned six_byte, pg_len, hdr_len, bd_len; + int len; + + VPRINTK("ENTER\n"); + + six_byte = (cdb[0] == MODE_SELECT); + if (six_byte) { + if (scmd->cmd_len < 5) + goto invalid_fld; + + len = cdb[4]; + hdr_len = 4; + } else { + if (scmd->cmd_len < 9) + goto invalid_fld; + + len = (cdb[7] << 8) + cdb[8]; + hdr_len = 8; + } + + /* We only support PF=1, SP=0. */ + if ((cdb[1] & 0x11) != 0x10) + goto invalid_fld; + + /* Test early for possible overrun. */ + if (!scsi_sg_count(scmd) || scsi_sglist(scmd)->length < len) + goto invalid_param_len; + + p = page_address(sg_page(scsi_sglist(scmd))); + + /* Move past header and block descriptors. */ + if (len < hdr_len) + goto invalid_param_len; + + if (six_byte) + bd_len = p[3]; + else + bd_len = (p[6] << 8) + p[7]; + + len -= hdr_len; + p += hdr_len; + if (len < bd_len) + goto invalid_param_len; + if (bd_len != 0 && bd_len != 8) + goto invalid_param; + + len -= bd_len; + p += bd_len; + if (len == 0) + goto skip; + + /* Parse both possible formats for the mode page headers. */ + pg = p[0] & 0x3f; + if (p[0] & 0x40) { + if (len < 4) + goto invalid_param_len; + + spg = p[1]; + pg_len = (p[2] << 8) | p[3]; + p += 4; + len -= 4; + } else { + if (len < 2) + goto invalid_param_len; + + spg = 0; + pg_len = p[1]; + p += 2; + len -= 2; + } + + /* + * No mode subpages supported (yet) but asking for _all_ + * subpages may be valid + */ + if (spg && (spg != ALL_SUB_MPAGES)) + goto invalid_param; + if (pg_len > len) + goto invalid_param_len; + + switch (pg) { + case CACHE_MPAGE: + if (ata_mselect_caching(qc, p, pg_len) < 0) + goto invalid_param; + break; + + default: /* invalid page code */ + goto invalid_param; + } + + /* + * Only one page has changeable data, so we only support setting one + * page at a time. + */ + if (len > pg_len) + goto invalid_param; + + return 0; + + invalid_fld: + /* "Invalid field in CDB" */ + ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); + return 1; + + invalid_param: + /* "Invalid field in parameter list" */ + ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x26, 0x0); + return 1; + + invalid_param_len: + /* "Parameter list length error" */ + ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x1a, 0x0); + return 1; + + skip: + scmd->result = SAM_STAT_GOOD; + return 1; +} + +/** * ata_get_xlat_func - check if SCSI to ATA translation is possible * @dev: ATA device * @cmd: SCSI command opcode to consider @@ -3119,6 +3369,11 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) case ATA_16: return ata_scsi_pass_thru; + case MODE_SELECT: + case MODE_SELECT_10: + return ata_scsi_mode_select_xlat; + break; + case START_STOP: return ata_scsi_start_stop_xlat; } @@ -3311,11 +3566,6 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense); break; - case MODE_SELECT: /* unconditionally return */ - case MODE_SELECT_10: /* bad-field-in-cdb */ - ata_scsi_invalid_field(cmd); - break; - case READ_CAPACITY: ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); break; @@ -3391,6 +3641,7 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) shost->max_lun = 1; shost->max_channel = 1; shost->max_cmd_len = 16; + shost->no_write_same = 1; /* Schedule policy is determined by ->qc_defer() * callback and it needs to see every deferred qc. @@ -3399,7 +3650,8 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) */ shost->max_host_blocked = 1; - rc = scsi_add_host(ap->scsi_host, &ap->tdev); + rc = scsi_add_host_with_dma(ap->scsi_host, + &ap->tdev, ap->host->dev); if (rc) goto err_add; } @@ -3589,6 +3841,9 @@ static void ata_scsi_handle_link_detach(struct ata_link *link) dev->flags &= ~ATA_DFLAG_DETACHED; spin_unlock_irqrestore(ap->lock, flags); + if (zpodd_dev_enabled(dev)) + zpodd_exit(dev); + ata_scsi_remove_dev(dev); } } @@ -3633,6 +3888,27 @@ void ata_scsi_hotplug(struct work_struct *work) return; } + /* + * XXX - UGLY HACK + * + * The block layer suspend/resume path is fundamentally broken due + * to freezable kthreads and workqueue and may deadlock if a block + * device gets removed while resume is in progress. I don't know + * what the solution is short of removing freezable kthreads and + * workqueues altogether. + * + * The following is an ugly hack to avoid kicking off device + * removal while freezer is active. This is a joke but does avoid + * this particular deadlock scenario. + * + * https://bugzilla.kernel.org/show_bug.cgi?id=62801 + * http://marc.info/?l=linux-kernel&m=138695698516487 + */ +#ifdef CONFIG_FREEZER + while (pm_freezing) + msleep(10); +#endif + DPRINTK("ENTER\n"); mutex_lock(&ap->scsi_scan_mutex); @@ -3839,6 +4115,26 @@ void ata_sas_port_stop(struct ata_port *ap) EXPORT_SYMBOL_GPL(ata_sas_port_stop); /** + * ata_sas_async_probe - simply schedule probing and return + * @ap: Port to probe + * + * For batch scheduling of probe for sas attached ata devices, assumes + * the port has already been through ata_sas_port_init() + */ +void ata_sas_async_probe(struct ata_port *ap) +{ + __ata_port_probe(ap); +} +EXPORT_SYMBOL_GPL(ata_sas_async_probe); + +int ata_sas_sync_probe(struct ata_port *ap) +{ + return ata_port_probe(ap); +} +EXPORT_SYMBOL_GPL(ata_sas_sync_probe); + + +/** * ata_sas_port_init - Initialize a SATA device * @ap: SATA port to initialize * @@ -3853,12 +4149,10 @@ int ata_sas_port_init(struct ata_port *ap) { int rc = ap->ops->port_start(ap); - if (!rc) { - ap->print_id = ata_print_id++; - rc = ata_port_probe(ap); - } - - return rc; + if (rc) + return rc; + ap->print_id = atomic_inc_return(&ata_print_id); + return 0; } EXPORT_SYMBOL_GPL(ata_sas_port_init); diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 9691dd0966d..1121153f1ec 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1,7 +1,7 @@ /* * libata-sff.c - helper library for PCI IDE BMDMA * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -720,13 +720,13 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) /* FIXME: use a bounce buffer */ local_irq_save(flags); - buf = kmap_atomic(page, KM_IRQ0); + buf = kmap_atomic(page); /* do the actual data transfer */ ap->ops->sff_data_xfer(qc->dev, buf + offset, qc->sect_size, do_write); - kunmap_atomic(buf, KM_IRQ0); + kunmap_atomic(buf); local_irq_restore(flags); } else { buf = page_address(page); @@ -865,13 +865,13 @@ next_sg: /* FIXME: use bounce buffer */ local_irq_save(flags); - buf = kmap_atomic(page, KM_IRQ0); + buf = kmap_atomic(page); /* do the actual data transfer */ consumed = ap->ops->sff_data_xfer(dev, buf + offset, count, rw); - kunmap_atomic(buf, KM_IRQ0); + kunmap_atomic(buf); local_irq_restore(flags); } else { buf = page_address(page); @@ -2433,15 +2433,6 @@ int ata_pci_sff_activate_host(struct ata_host *host, mask = (1 << 2) | (1 << 0); if ((tmp8 & mask) != mask) legacy_mode = 1; -#if defined(CONFIG_NO_ATA_LEGACY) - /* Some platforms with PCI limits cannot address compat - port space. In that case we punt if their firmware has - left a device in compatibility mode */ - if (legacy_mode) { - printk(KERN_ERR "ata: Compatibility mode ATA is not supported on this platform, skipping.\n"); - return -EOPNOTSUPP; - } -#endif } if (!devres_open_group(dev, NULL, GFP_KERNEL)) diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 74aaee30e26..e3741322822 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -37,7 +37,7 @@ #include "libata.h" #include "libata-transport.h" -#define ATA_PORT_ATTRS 2 +#define ATA_PORT_ATTRS 3 #define ATA_LINK_ATTRS 3 #define ATA_DEV_ATTRS 9 @@ -216,6 +216,7 @@ static DEVICE_ATTR(name, S_IRUGO, show_ata_port_##name, NULL) ata_port_simple_attr(nr_pmp_links, nr_pmp_links, "%d\n", int); ata_port_simple_attr(stats.idle_irq, idle_irq, "%ld\n", unsigned long); +ata_port_simple_attr(local_port_no, port_no, "%u\n", unsigned int); static DECLARE_TRANSPORT_CLASS(ata_port_class, "ata_port", NULL, NULL, NULL); @@ -232,7 +233,7 @@ static void ata_tport_release(struct device *dev) * Returns: * %1 if the device represents a ATA Port, %0 else */ -int ata_is_port(const struct device *dev) +static int ata_is_port(const struct device *dev) { return dev->release == ata_tport_release; } @@ -286,6 +287,7 @@ int ata_tport_add(struct device *parent, dev->release = ata_tport_release; dev_set_name(dev, "ata%d", ap->print_id); transport_setup_device(dev); + ata_acpi_bind_port(ap); error = device_add(dev); if (error) { goto tport_err; @@ -294,6 +296,7 @@ int ata_tport_add(struct device *parent, device_enable_async_suspend(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); + pm_runtime_forbid(dev); transport_add_device(dev); transport_configure_device(dev); @@ -318,25 +321,25 @@ int ata_tport_add(struct device *parent, /* * ATA link attributes */ +static int noop(int x) { return x; } - -#define ata_link_show_linkspeed(field) \ +#define ata_link_show_linkspeed(field, format) \ static ssize_t \ show_ata_link_##field(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct ata_link *link = transport_class_to_link(dev); \ \ - return sprintf(buf,"%s\n", sata_spd_string(fls(link->field))); \ + return sprintf(buf, "%s\n", sata_spd_string(format(link->field))); \ } -#define ata_link_linkspeed_attr(field) \ - ata_link_show_linkspeed(field) \ +#define ata_link_linkspeed_attr(field, format) \ + ata_link_show_linkspeed(field, format) \ static DEVICE_ATTR(field, S_IRUGO, show_ata_link_##field, NULL) -ata_link_linkspeed_attr(hw_sata_spd_limit); -ata_link_linkspeed_attr(sata_spd_limit); -ata_link_linkspeed_attr(sata_spd); +ata_link_linkspeed_attr(hw_sata_spd_limit, fls); +ata_link_linkspeed_attr(sata_spd_limit, fls); +ata_link_linkspeed_attr(sata_spd, noop); static DECLARE_TRANSPORT_CLASS(ata_link_class, @@ -354,7 +357,7 @@ static void ata_tlink_release(struct device *dev) * Returns: * %1 if the device represents a ATA link, %0 else */ -int ata_is_link(const struct device *dev) +static int ata_is_link(const struct device *dev) { return dev->release == ata_tlink_release; } @@ -571,7 +574,7 @@ static void ata_tdev_release(struct device *dev) * Returns: * %1 if the device represents a ATA device, %0 else */ -int ata_is_ata_dev(const struct device *dev) +static int ata_is_ata_dev(const struct device *dev) { return dev->release == ata_tdev_release; } @@ -642,6 +645,7 @@ static int ata_tdev_add(struct ata_device *ata_dev) dev_set_name(dev, "dev%d.%d.0", ap->print_id, link->pmp); transport_setup_device(dev); + ata_acpi_bind_dev(ata_dev); error = device_add(dev); if (error) { ata_tdev_free(ata_dev); @@ -708,6 +712,7 @@ struct scsi_transport_template *ata_attach_transport(void) count = 0; SETUP_PORT_ATTRIBUTE(nr_pmp_links); SETUP_PORT_ATTRIBUTE(idle_irq); + SETUP_PORT_ATTRIBUTE(port_no); BUG_ON(count > ATA_PORT_ATTRS); i->port_attrs[count] = NULL; diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c new file mode 100644 index 00000000000..f3a65a3140d --- /dev/null +++ b/drivers/ata/libata-zpodd.c @@ -0,0 +1,283 @@ +#include <linux/libata.h> +#include <linux/cdrom.h> +#include <linux/pm_runtime.h> +#include <linux/module.h> +#include <linux/pm_qos.h> +#include <scsi/scsi_device.h> + +#include "libata.h" + +static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */ +module_param(zpodd_poweroff_delay, int, 0644); +MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds"); + +enum odd_mech_type { + ODD_MECH_TYPE_SLOT, + ODD_MECH_TYPE_DRAWER, + ODD_MECH_TYPE_UNSUPPORTED, +}; + +struct zpodd { + enum odd_mech_type mech_type; /* init during probe, RO afterwards */ + struct ata_device *dev; + + /* The following fields are synchronized by PM core. */ + bool from_notify; /* resumed as a result of + * acpi wake notification */ + bool zp_ready; /* ZP ready state */ + unsigned long last_ready; /* last ZP ready timestamp */ + bool zp_sampled; /* ZP ready state sampled */ + bool powered_off; /* ODD is powered off + * during suspend */ +}; + +static int eject_tray(struct ata_device *dev) +{ + struct ata_taskfile tf; + const char cdb[] = { GPCMD_START_STOP_UNIT, + 0, 0, 0, + 0x02, /* LoEj */ + 0, 0, 0, 0, 0, 0, 0, + }; + + ata_tf_init(dev, &tf); + tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.command = ATA_CMD_PACKET; + tf.protocol = ATAPI_PROT_NODATA; + + return ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0); +} + +/* Per the spec, only slot type and drawer type ODD can be supported */ +static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev) +{ + char buf[16]; + unsigned int ret; + struct rm_feature_desc *desc = (void *)(buf + 8); + struct ata_taskfile tf; + char cdb[] = { GPCMD_GET_CONFIGURATION, + 2, /* only 1 feature descriptor requested */ + 0, 3, /* 3, removable medium feature */ + 0, 0, 0,/* reserved */ + 0, sizeof(buf), + 0, 0, 0, + }; + + ata_tf_init(dev, &tf); + tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.command = ATA_CMD_PACKET; + tf.protocol = ATAPI_PROT_PIO; + tf.lbam = sizeof(buf); + + ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE, + buf, sizeof(buf), 0); + if (ret) + return ODD_MECH_TYPE_UNSUPPORTED; + + if (be16_to_cpu(desc->feature_code) != 3) + return ODD_MECH_TYPE_UNSUPPORTED; + + if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) + return ODD_MECH_TYPE_SLOT; + else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1) + return ODD_MECH_TYPE_DRAWER; + else + return ODD_MECH_TYPE_UNSUPPORTED; +} + +/* Test if ODD is zero power ready by sense code */ +static bool zpready(struct ata_device *dev) +{ + u8 sense_key, *sense_buf; + unsigned int ret, asc, ascq, add_len; + struct zpodd *zpodd = dev->zpodd; + + ret = atapi_eh_tur(dev, &sense_key); + + if (!ret || sense_key != NOT_READY) + return false; + + sense_buf = dev->link->ap->sector_buf; + ret = atapi_eh_request_sense(dev, sense_buf, sense_key); + if (ret) + return false; + + /* sense valid */ + if ((sense_buf[0] & 0x7f) != 0x70) + return false; + + add_len = sense_buf[7]; + /* has asc and ascq */ + if (add_len < 6) + return false; + + asc = sense_buf[12]; + ascq = sense_buf[13]; + + if (zpodd->mech_type == ODD_MECH_TYPE_SLOT) + /* no media inside */ + return asc == 0x3a; + else + /* no media inside and door closed */ + return asc == 0x3a && ascq == 0x01; +} + +/* + * Update the zpodd->zp_ready field. This field will only be set + * if the ODD has stayed in ZP ready state for zpodd_poweroff_delay + * time, and will be used to decide if power off is allowed. If it + * is set, it will be cleared during resume from powered off state. + */ +void zpodd_on_suspend(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + unsigned long expires; + + if (!zpready(dev)) { + zpodd->zp_sampled = false; + zpodd->zp_ready = false; + return; + } + + if (!zpodd->zp_sampled) { + zpodd->zp_sampled = true; + zpodd->last_ready = jiffies; + return; + } + + expires = zpodd->last_ready + + msecs_to_jiffies(zpodd_poweroff_delay * 1000); + if (time_before(jiffies, expires)) + return; + + zpodd->zp_ready = true; +} + +bool zpodd_zpready(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + return zpodd->zp_ready; +} + +/* + * Enable runtime wake capability through ACPI and set the powered_off flag, + * this flag will be used during resume to decide what operations are needed + * to take. + * + * Also, media poll needs to be silenced, so that it doesn't bring the ODD + * back to full power state every few seconds. + */ +void zpodd_enable_run_wake(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + sdev_disable_disk_events(dev->sdev); + + zpodd->powered_off = true; + device_set_run_wake(&dev->tdev, true); + acpi_pm_device_run_wake(&dev->tdev, true); +} + +/* Disable runtime wake capability if it is enabled */ +void zpodd_disable_run_wake(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + if (zpodd->powered_off) { + acpi_pm_device_run_wake(&dev->tdev, false); + device_set_run_wake(&dev->tdev, false); + } +} + +/* + * Post power on processing after the ODD has been recovered. If the + * ODD wasn't powered off during suspend, it doesn't do anything. + * + * For drawer type ODD, if it is powered on due to user pressed the + * eject button, the tray needs to be ejected. This can only be done + * after the ODD has been recovered, i.e. link is initialized and + * device is able to process NON_DATA PIO command, as eject needs to + * send command for the ODD to process. + * + * The from_notify flag set in wake notification handler function + * zpodd_wake_dev represents if power on is due to user's action. + * + * For both types of ODD, several fields need to be reset. + */ +void zpodd_post_poweron(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + if (!zpodd->powered_off) + return; + + zpodd->powered_off = false; + + if (zpodd->from_notify) { + zpodd->from_notify = false; + if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER) + eject_tray(dev); + } + + zpodd->zp_sampled = false; + zpodd->zp_ready = false; + + sdev_enable_disk_events(dev->sdev); +} + +static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) +{ + struct ata_device *ata_dev = context; + struct zpodd *zpodd = ata_dev->zpodd; + struct device *dev = &ata_dev->sdev->sdev_gendev; + + if (event == ACPI_NOTIFY_DEVICE_WAKE && pm_runtime_suspended(dev)) { + zpodd->from_notify = true; + pm_runtime_resume(dev); + } +} + +static void ata_acpi_add_pm_notifier(struct ata_device *dev) +{ + acpi_handle handle = ata_dev_acpi_handle(dev); + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + zpodd_wake_dev, dev); +} + +static void ata_acpi_remove_pm_notifier(struct ata_device *dev) +{ + acpi_handle handle = ata_dev_acpi_handle(dev); + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev); +} + +void zpodd_init(struct ata_device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(&dev->tdev); + enum odd_mech_type mech_type; + struct zpodd *zpodd; + + if (dev->zpodd || !adev || !acpi_device_can_poweroff(adev)) + return; + + mech_type = zpodd_get_mech_type(dev); + if (mech_type == ODD_MECH_TYPE_UNSUPPORTED) + return; + + zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL); + if (!zpodd) + return; + + zpodd->mech_type = mech_type; + + ata_acpi_add_pm_notifier(dev); + zpodd->dev = dev; + dev->zpodd = zpodd; + dev_pm_qos_expose_flags(&dev->tdev, 0); +} + +void zpodd_exit(struct ata_device *dev) +{ + ata_acpi_remove_pm_notifier(dev); + kfree(dev->zpodd); + dev->zpodd = NULL; +} diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 814486d35c4..45b5ab3a95d 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -53,7 +53,7 @@ enum { ATA_DNXFER_QUIET = (1 << 31), }; -extern unsigned int ata_print_id; +extern atomic_t ata_print_id; extern int atapi_passthru16; extern int libata_fua; extern int libata_noacpi; @@ -105,22 +105,23 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct ata_port *ata_port_alloc(struct ata_host *host); extern const char *sata_spd_string(unsigned int spd); extern int ata_port_probe(struct ata_port *ap); +extern void __ata_port_probe(struct ata_port *ap); + +#define to_ata_port(d) container_of(d, struct ata_port, tdev) /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI extern unsigned int ata_acpi_gtf_filter; - -extern void ata_acpi_associate_sata_port(struct ata_port *ap); -extern void ata_acpi_associate(struct ata_host *host); extern void ata_acpi_dissociate(struct ata_host *host); extern int ata_acpi_on_suspend(struct ata_port *ap); extern void ata_acpi_on_resume(struct ata_port *ap); extern int ata_acpi_on_devcfg(struct ata_device *dev); extern void ata_acpi_on_disable(struct ata_device *dev); extern void ata_acpi_set_state(struct ata_port *ap, pm_message_t state); +extern void ata_acpi_bind_port(struct ata_port *ap); +extern void ata_acpi_bind_dev(struct ata_device *dev); +extern acpi_handle ata_dev_acpi_handle(struct ata_device *dev); #else -static inline void ata_acpi_associate_sata_port(struct ata_port *ap) { } -static inline void ata_acpi_associate(struct ata_host *host) { } static inline void ata_acpi_dissociate(struct ata_host *host) { } static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; } static inline void ata_acpi_on_resume(struct ata_port *ap) { } @@ -128,6 +129,8 @@ static inline int ata_acpi_on_devcfg(struct ata_device *dev) { return 0; } static inline void ata_acpi_on_disable(struct ata_device *dev) { } static inline void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) { } +static inline void ata_acpi_bind_port(struct ata_port *ap) {} +static inline void ata_acpi_bind_dev(struct ata_device *dev) {} #endif /* libata-scsi.c */ @@ -151,7 +154,6 @@ extern void ata_eh_acquire(struct ata_port *ap); extern void ata_eh_release(struct ata_port *ap); extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); -extern void ata_port_wait_eh(struct ata_port *ap); extern void ata_eh_fastdrain_timerfn(unsigned long arg); extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); extern void ata_dev_disable(struct ata_device *dev); @@ -160,6 +162,8 @@ extern void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev, unsigned int action); extern void ata_eh_done(struct ata_link *link, struct ata_device *dev, unsigned int action); +extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log, + u8 page, void *buf, unsigned int sectors); extern void ata_eh_autopsy(struct ata_port *ap); const char *ata_get_cmd_descript(u8 command); extern void ata_eh_report(struct ata_port *ap); @@ -175,6 +179,9 @@ extern void ata_eh_finish(struct ata_port *ap); extern int ata_ering_map(struct ata_ering *ering, int (*map_fn)(struct ata_ering_entry *, void *), void *arg); +extern unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key); +extern unsigned int atapi_eh_request_sense(struct ata_device *dev, + u8 *sense_buf, u8 dfl_sense_key); /* libata-pmp.c */ #ifdef CONFIG_SATA_PMP @@ -223,4 +230,28 @@ static inline void ata_sff_exit(void) { } #endif /* CONFIG_ATA_SFF */ +/* libata-zpodd.c */ +#ifdef CONFIG_SATA_ZPODD +void zpodd_init(struct ata_device *dev); +void zpodd_exit(struct ata_device *dev); +static inline bool zpodd_dev_enabled(struct ata_device *dev) +{ + return dev->zpodd != NULL; +} +void zpodd_on_suspend(struct ata_device *dev); +bool zpodd_zpready(struct ata_device *dev); +void zpodd_enable_run_wake(struct ata_device *dev); +void zpodd_disable_run_wake(struct ata_device *dev); +void zpodd_post_poweron(struct ata_device *dev); +#else /* CONFIG_SATA_ZPODD */ +static inline void zpodd_init(struct ata_device *dev) {} +static inline void zpodd_exit(struct ata_device *dev) {} +static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; } +static inline void zpodd_on_suspend(struct ata_device *dev) {} +static inline bool zpodd_zpready(struct ata_device *dev) { return false; } +static inline void zpodd_enable_run_wake(struct ata_device *dev) {} +static inline void zpodd_disable_run_wake(struct ata_device *dev) {} +static inline void zpodd_post_poweron(struct ata_device *dev) {} +#endif /* CONFIG_SATA_ZPODD */ + #endif /* __LIBATA_H__ */ diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index 54145edf50e..b70fce2a38e 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -7,16 +7,14 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/gfp.h> -#include <scsi/scsi_host.h> -#include <acpi/acpi_bus.h> - +#include <linux/acpi.h> #include <linux/libata.h> #include <linux/ata.h> +#include <scsi/scsi_host.h> #define DRV_NAME "pata_acpi" #define DRV_VERSION "0.2.3" @@ -39,7 +37,7 @@ static int pacpi_pre_reset(struct ata_link *link, unsigned long deadline) { struct ata_port *ap = link->ap; struct pata_acpi *acpi = ap->private_data; - if (ap->acpi_handle == NULL || ata_acpi_gtm(ap, &acpi->gtm) < 0) + if (ACPI_HANDLE(&ap->tdev) == NULL || ata_acpi_gtm(ap, &acpi->gtm) < 0) return -ENODEV; return ata_sff_prereset(link, deadline); @@ -195,7 +193,7 @@ static int pacpi_port_start(struct ata_port *ap) struct pci_dev *pdev = to_pci_dev(ap->host->dev); struct pata_acpi *acpi; - if (ap->acpi_handle == NULL) + if (ACPI_HANDLE(&ap->tdev) == NULL) return -ENODEV; acpi = ap->private_data = devm_kzalloc(&pdev->dev, sizeof(struct pata_acpi), GFP_KERNEL); @@ -267,28 +265,16 @@ static struct pci_driver pacpi_pci_driver = { .id_table = pacpi_pci_tbl, .probe = pacpi_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init pacpi_init(void) -{ - return pci_register_driver(&pacpi_pci_driver); -} - -static void __exit pacpi_exit(void) -{ - pci_unregister_driver(&pacpi_pci_driver); -} - -module_init(pacpi_init); -module_exit(pacpi_exit); +module_pci_driver(pacpi_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for ATA in ACPI mode"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pacpi_pci_tbl); MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index 61da0694aec..d19cd88ed2d 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -589,10 +589,10 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_bmdma_init_one(pdev, ppi, &ali_sht, NULL, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ali_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -616,7 +616,7 @@ static struct pci_driver ali_pci_driver = { .id_table = ali, .probe = ali_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ali_reinit_one, #endif diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c index dc6b5dae046..8d4d959a821 100644 --- a/drivers/ata/pata_amd.c +++ b/drivers/ata/pata_amd.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -575,10 +574,10 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_bmdma_init_one(pdev, ppi, &amd_sht, hpriv, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int amd_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -626,27 +625,16 @@ static struct pci_driver amd_pci_driver = { .id_table = amd, .probe = amd_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = amd_reinit_one, #endif }; -static int __init amd_init(void) -{ - return pci_register_driver(&amd_pci_driver); -} - -static void __exit amd_exit(void) -{ - pci_unregister_driver(&amd_pci_driver); -} +module_pci_driver(amd_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for AMD and Nvidia PATA IDE"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, amd); MODULE_VERSION(DRV_VERSION); - -module_init(amd_init); -module_exit(amd_exit); diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index 048589fad2c..4edb1a81f63 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -4,7 +4,7 @@ * Arasan Compact Flash host controller source file * * Copyright (C) 2011 ST Microelectronics - * Viresh Kumar <viresh.kumar@st.com> + * Viresh Kumar <viresh.linux@gmail.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -31,6 +31,7 @@ #include <linux/kernel.h> #include <linux/libata.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/pata_arasan_cf_data.h> #include <linux/platform_device.h> #include <linux/pm.h> @@ -184,10 +185,8 @@ struct arasan_cf_dev { /* pointer to ata_host structure */ struct ata_host *host; - /* clk structure, only if HAVE_CLK is defined */ -#ifdef CONFIG_HAVE_CLK + /* clk structure */ struct clk *clk; -#endif /* physical base address of controller */ dma_addr_t pbase; @@ -210,8 +209,6 @@ struct arasan_cf_dev { struct dma_chan *dma_chan; /* Mask for DMA transfers */ dma_cap_mask_t mask; - /* dma channel private data */ - void *dma_priv; /* DMA transfer work */ struct work_struct work; /* DMA delayed finish work */ @@ -309,21 +306,31 @@ static void cf_card_detect(struct arasan_cf_dev *acdev, bool hotplugged) static int cf_init(struct arasan_cf_dev *acdev) { struct arasan_cf_pdata *pdata = dev_get_platdata(acdev->host->dev); + unsigned int if_clk; unsigned long flags; int ret = 0; -#ifdef CONFIG_HAVE_CLK - ret = clk_enable(acdev->clk); + ret = clk_prepare_enable(acdev->clk); if (ret) { dev_dbg(acdev->host->dev, "clock enable failed"); return ret; } -#endif + + ret = clk_set_rate(acdev->clk, 166000000); + if (ret) { + dev_warn(acdev->host->dev, "clock set rate failed"); + clk_disable_unprepare(acdev->clk); + return ret; + } spin_lock_irqsave(&acdev->host->lock, flags); /* configure CF interface clock */ - writel((pdata->cf_if_clk <= CF_IF_CLK_200M) ? pdata->cf_if_clk : - CF_IF_CLK_166M, acdev->vbase + CLK_CFG); + /* TODO: read from device tree */ + if_clk = CF_IF_CLK_166M; + if (pdata && pdata->cf_if_clk <= CF_IF_CLK_200M) + if_clk = pdata->cf_if_clk; + + writel(if_clk, acdev->vbase + CLK_CFG); writel(TRUE_IDE_MODE | CFHOST_ENB, acdev->vbase + OP_MODE); cf_interrupt_enable(acdev, CARD_DETECT_IRQ, 1); @@ -344,24 +351,16 @@ static void cf_exit(struct arasan_cf_dev *acdev) writel(readl(acdev->vbase + OP_MODE) & ~CFHOST_ENB, acdev->vbase + OP_MODE); spin_unlock_irqrestore(&acdev->host->lock, flags); -#ifdef CONFIG_HAVE_CLK - clk_disable(acdev->clk); -#endif + clk_disable_unprepare(acdev->clk); } static void dma_callback(void *dev) { - struct arasan_cf_dev *acdev = (struct arasan_cf_dev *) dev; + struct arasan_cf_dev *acdev = dev; complete(&acdev->dma_completion); } -static bool filter(struct dma_chan *chan, void *slave) -{ - chan->private = slave; - return true; -} - static inline void dma_complete(struct arasan_cf_dev *acdev) { struct ata_queued_cmd *qc = acdev->qc; @@ -398,8 +397,7 @@ dma_xfer(struct arasan_cf_dev *acdev, dma_addr_t src, dma_addr_t dest, u32 len) struct dma_async_tx_descriptor *tx; struct dma_chan *chan = acdev->dma_chan; dma_cookie_t cookie; - unsigned long flags = DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP | - DMA_COMPL_SKIP_DEST_UNMAP; + unsigned long flags = DMA_PREP_INTERRUPT; int ret = 0; tx = chan->device->device_prep_dma_memcpy(chan, dest, src, len, flags); @@ -529,8 +527,7 @@ static void data_xfer(struct work_struct *work) /* request dma channels */ /* dma_request_channel may sleep, so calling from process context */ - acdev->dma_chan = dma_request_channel(acdev->mask, filter, - acdev->dma_priv); + acdev->dma_chan = dma_request_slave_channel(acdev->host->dev, "data"); if (!acdev->dma_chan) { dev_err(acdev->host->dev, "Unable to get dma_chan\n"); goto chan_request_fail; @@ -657,7 +654,7 @@ static void arasan_cf_freeze(struct ata_port *ap) ata_sff_freeze(ap); } -void arasan_cf_error_handler(struct ata_port *ap) +static void arasan_cf_error_handler(struct ata_port *ap) { struct arasan_cf_dev *acdev = ap->host->private_data; @@ -673,17 +670,20 @@ void arasan_cf_error_handler(struct ata_port *ap) static void arasan_cf_dma_start(struct arasan_cf_dev *acdev) { + struct ata_queued_cmd *qc = acdev->qc; + struct ata_port *ap = qc->ap; + struct ata_taskfile *tf = &qc->tf; u32 xfer_ctr = readl(acdev->vbase + XFER_CTR) & ~XFER_DIR_MASK; - u32 write = acdev->qc->tf.flags & ATA_TFLAG_WRITE; + u32 write = tf->flags & ATA_TFLAG_WRITE; xfer_ctr |= write ? XFER_WRITE : XFER_READ; writel(xfer_ctr, acdev->vbase + XFER_CTR); - acdev->qc->ap->ops->sff_exec_command(acdev->qc->ap, &acdev->qc->tf); + ap->ops->sff_exec_command(ap, tf); ata_sff_queue_work(&acdev->work); } -unsigned int arasan_cf_qc_issue(struct ata_queued_cmd *qc) +static unsigned int arasan_cf_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; struct arasan_cf_dev *acdev = ap->host->private_data; @@ -787,13 +787,14 @@ static struct ata_port_operations arasan_cf_ops = { .set_dmamode = arasan_cf_set_dmamode, }; -static int __devinit arasan_cf_probe(struct platform_device *pdev) +static int arasan_cf_probe(struct platform_device *pdev) { struct arasan_cf_dev *acdev; struct arasan_cf_pdata *pdata = dev_get_platdata(&pdev->dev); struct ata_host *host; struct ata_port *ap; struct resource *res; + u32 quirk; irq_handler_t irq_handler = NULL; int ret = 0; @@ -813,12 +814,17 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev) return -ENOMEM; } + if (pdata) + quirk = pdata->quirk; + else + quirk = CF_BROKEN_UDMA; /* as it is on spear1340 */ + /* if irq is 0, support only PIO */ acdev->irq = platform_get_irq(pdev, 0); if (acdev->irq) irq_handler = arasan_cf_interrupt; else - pdata->quirk |= CF_BROKEN_MWDMA | CF_BROKEN_UDMA; + quirk |= CF_BROKEN_MWDMA | CF_BROKEN_UDMA; acdev->pbase = res->start; acdev->vbase = devm_ioremap_nocache(&pdev->dev, res->start, @@ -828,13 +834,11 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev) return -ENOMEM; } -#ifdef CONFIG_HAVE_CLK acdev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(acdev->clk)) { dev_warn(&pdev->dev, "Clock not found\n"); return PTR_ERR(acdev->clk); } -#endif /* allocate host */ host = ata_host_alloc(&pdev->dev, 1); @@ -857,17 +861,16 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev) INIT_WORK(&acdev->work, data_xfer); INIT_DELAYED_WORK(&acdev->dwork, delayed_finish); dma_cap_set(DMA_MEMCPY, acdev->mask); - acdev->dma_priv = pdata->dma_priv; /* Handle platform specific quirks */ - if (pdata->quirk) { - if (pdata->quirk & CF_BROKEN_PIO) { + if (quirk) { + if (quirk & CF_BROKEN_PIO) { ap->ops->set_piomode = NULL; ap->pio_mask = 0; } - if (pdata->quirk & CF_BROKEN_MWDMA) + if (quirk & CF_BROKEN_MWDMA) ap->mwdma_mask = 0; - if (pdata->quirk & CF_BROKEN_UDMA) + if (quirk & CF_BROKEN_UDMA) ap->udma_mask = 0; } ap->flags |= ATA_FLAG_PIO_POLLING | ATA_FLAG_NO_ATAPI; @@ -895,41 +898,39 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev) cf_card_detect(acdev, 0); - return ata_host_activate(host, acdev->irq, irq_handler, 0, - &arasan_cf_sht); + ret = ata_host_activate(host, acdev->irq, irq_handler, 0, + &arasan_cf_sht); + if (!ret) + return 0; + cf_exit(acdev); free_clk: -#ifdef CONFIG_HAVE_CLK clk_put(acdev->clk); -#endif return ret; } -static int __devexit arasan_cf_remove(struct platform_device *pdev) +static int arasan_cf_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct arasan_cf_dev *acdev = host->ports[0]->private_data; ata_host_detach(host); cf_exit(acdev); -#ifdef CONFIG_HAVE_CLK clk_put(acdev->clk); -#endif return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int arasan_cf_suspend(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); struct arasan_cf_dev *acdev = host->ports[0]->private_data; - if (acdev->dma_chan) { + if (acdev->dma_chan) acdev->dma_chan->device->device_control(acdev->dma_chan, DMA_TERMINATE_ALL, 0); - dma_release_channel(acdev->dma_chan); - } + cf_exit(acdev); return ata_host_suspend(host, PMSG_SUSPEND); } @@ -944,28 +945,32 @@ static int arasan_cf_resume(struct device *dev) return 0; } +#endif + +static SIMPLE_DEV_PM_OPS(arasan_cf_pm_ops, arasan_cf_suspend, arasan_cf_resume); -static const struct dev_pm_ops arasan_cf_pm_ops = { - .suspend = arasan_cf_suspend, - .resume = arasan_cf_resume, +#ifdef CONFIG_OF +static const struct of_device_id arasan_cf_id_table[] = { + { .compatible = "arasan,cf-spear1340" }, + {} }; +MODULE_DEVICE_TABLE(of, arasan_cf_id_table); #endif static struct platform_driver arasan_cf_driver = { .probe = arasan_cf_probe, - .remove = __devexit_p(arasan_cf_remove), + .remove = arasan_cf_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &arasan_cf_pm_ops, -#endif + .pm = &arasan_cf_pm_ops, + .of_match_table = of_match_ptr(arasan_cf_id_table), }, }; module_platform_driver(arasan_cf_driver); -MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); +MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>"); MODULE_DESCRIPTION("Arasan ATA Compact Flash driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c index 4b8b22efc00..96c05c9494f 100644 --- a/drivers/ata/pata_artop.c +++ b/drivers/ata/pata_artop.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -423,10 +422,10 @@ static const struct pci_device_id artop_pci_tbl[] = { { } /* terminate list */ }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int atp8xx_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -445,24 +444,13 @@ static struct pci_driver artop_pci_driver = { .id_table = artop_pci_tbl, .probe = artop_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = atp8xx_reinit_one, #endif }; -static int __init artop_init(void) -{ - return pci_register_driver(&artop_pci_driver); -} - -static void __exit artop_exit(void) -{ - pci_unregister_driver(&artop_pci_driver); -} - -module_init(artop_init); -module_exit(artop_exit); +module_pci_driver(artop_pci_driver); MODULE_AUTHOR("Alan Cox, Bartlomiej Zolnierkiewicz"); MODULE_DESCRIPTION("SCSI low-level driver for ARTOP PATA"); diff --git a/drivers/ata/pata_at32.c b/drivers/ata/pata_at32.c index 36f189c7ee8..d59d5239405 100644 --- a/drivers/ata/pata_at32.c +++ b/drivers/ata/pata_at32.c @@ -271,7 +271,7 @@ static int __init pata_at32_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct at32_ide_info *info; - struct ide_platform_data *board = pdev->dev.platform_data; + struct ide_platform_data *board = dev_get_platdata(&pdev->dev); struct resource *res; int irq; @@ -393,18 +393,7 @@ static struct platform_driver pata_at32_driver = { }, }; -static int __init pata_at32_init(void) -{ - return platform_driver_probe(&pata_at32_driver, pata_at32_probe); -} - -static void __exit pata_at32_exit(void) -{ - platform_driver_unregister(&pata_at32_driver); -} - -module_init(pata_at32_init); -module_exit(pata_at32_exit); +module_platform_driver_probe(pata_at32_driver, pata_at32_probe); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("AVR32 SMC/CFC PATA Driver"); diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index 53d3770a0b1..8a66f23af4c 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/gfp.h> #include <scsi/scsi_host.h> @@ -27,9 +26,9 @@ #include <linux/libata.h> #include <linux/platform_device.h> #include <linux/ata_platform.h> +#include <linux/platform_data/atmel.h> #include <mach/at91sam9_smc.h> -#include <mach/board.h> #include <asm/gpio.h> #define DRV_NAME "pata_at91" @@ -313,9 +312,9 @@ static struct ata_port_operations pata_at91_port_ops = { .cable_detect = ata_cable_40wire, }; -static int __devinit pata_at91_probe(struct platform_device *pdev) +static int pata_at91_probe(struct platform_device *pdev) { - struct at91_cf_data *board = pdev->dev.platform_data; + struct at91_cf_data *board = dev_get_platdata(&pdev->dev); struct device *dev = &pdev->dev; struct at91_ide_info *info; struct resource *mem_res; @@ -408,21 +407,22 @@ static int __devinit pata_at91_probe(struct platform_device *pdev) host->private_data = info; - return ata_host_activate(host, gpio_is_valid(irq) ? gpio_to_irq(irq) : 0, - gpio_is_valid(irq) ? ata_sff_interrupt : NULL, - irq_flags, &pata_at91_sht); + ret = ata_host_activate(host, gpio_is_valid(irq) ? gpio_to_irq(irq) : 0, + gpio_is_valid(irq) ? ata_sff_interrupt : NULL, + irq_flags, &pata_at91_sht); + if (ret) + goto err_put; - if (!ret) - return 0; + return 0; err_put: clk_put(info->mck); return ret; } -static int __devexit pata_at91_remove(struct platform_device *pdev) +static int pata_at91_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct at91_ide_info *info; if (!host) @@ -441,7 +441,7 @@ static int __devexit pata_at91_remove(struct platform_device *pdev) static struct platform_driver pata_at91_driver = { .probe = pata_at91_probe, - .remove = __devexit_p(pata_at91_remove), + .remove = pata_at91_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index be1aa1486d3..970f7767e5f 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -15,11 +15,11 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> #include <linux/libata.h> +#include <linux/dmi.h> #define DRV_NAME "pata_atiixp" #define DRV_VERSION "0.4.6" @@ -33,11 +33,26 @@ enum { ATIIXP_IDE_UDMA_MODE = 0x56 }; +static const struct dmi_system_id attixp_cable_override_dmi_table[] = { + { + /* Board has onboard PATA<->SATA converters */ + .ident = "MSI E350DM-E33", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "E350DM-E33(MS-7720)"), + }, + }, + { } +}; + static int atiixp_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 udma; + if (dmi_check_system(attixp_cable_override_dmi_table)) + return ATA_CBL_PATA40_SHORT; + /* Hack from drivers/ide/pci. Really we want to know how to do the raw detection not play follow the bios mode guess */ pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma); @@ -283,28 +298,16 @@ static struct pci_driver atiixp_pci_driver = { .id_table = atiixp, .probe = atiixp_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .resume = ata_pci_device_resume, .suspend = ata_pci_device_suspend, #endif }; -static int __init atiixp_init(void) -{ - return pci_register_driver(&atiixp_pci_driver); -} - - -static void __exit atiixp_exit(void) -{ - pci_unregister_driver(&atiixp_pci_driver); -} +module_pci_driver(atiixp_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for ATI IXP200/300/400"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, atiixp); MODULE_VERSION(DRV_VERSION); - -module_init(atiixp_init); -module_exit(atiixp_exit); diff --git a/drivers/ata/pata_atp867x.c b/drivers/ata/pata_atp867x.c index 3cfabb262af..a705cfca90f 100644 --- a/drivers/ata/pata_atp867x.c +++ b/drivers/ata/pata_atp867x.c @@ -29,7 +29,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -531,10 +530,10 @@ err_out: return rc; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int atp867x_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -559,27 +558,16 @@ static struct pci_driver atp867x_driver = { .id_table = atp867x_pci_tbl, .probe = atp867x_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = atp867x_reinit_one, #endif }; -static int __init atp867x_init(void) -{ - return pci_register_driver(&atp867x_driver); -} - -static void __exit atp867x_exit(void) -{ - pci_unregister_driver(&atp867x_driver); -} +module_pci_driver(atp867x_driver); MODULE_AUTHOR("John(Jung-Ik) Lee, Google Inc."); MODULE_DESCRIPTION("low level driver for Artop/Acard 867x ATA controller"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, atp867x_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(atp867x_init); -module_exit(atp867x_exit); diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index 1e65842e2ca..03f2f2bc83b 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -1538,7 +1538,7 @@ static unsigned short atapi_io_port[] = { * - IRQ (IORESOURCE_IRQ) * */ -static int __devinit bfin_atapi_probe(struct platform_device *pdev) +static int bfin_atapi_probe(struct platform_device *pdev) { int board_idx = 0; struct resource *res; @@ -1596,7 +1596,7 @@ static int __devinit bfin_atapi_probe(struct platform_device *pdev) return -ENODEV; } - dev_set_drvdata(&pdev->dev, host); + platform_set_drvdata(pdev, host); return 0; } @@ -1608,23 +1608,21 @@ static int __devinit bfin_atapi_probe(struct platform_device *pdev) * A bfin atapi device has been unplugged. Perform the needed * cleanup. Also called on module unload for any active devices. */ -static int __devexit bfin_atapi_remove(struct platform_device *pdev) +static int bfin_atapi_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct ata_host *host = dev_get_drvdata(dev); + struct ata_host *host = platform_get_drvdata(pdev); ata_host_detach(host); - dev_set_drvdata(&pdev->dev, NULL); peripheral_free_list(atapi_io_port); return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int bfin_atapi_suspend(struct platform_device *pdev, pm_message_t state) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); if (host) return ata_host_suspend(host, state); else @@ -1633,7 +1631,7 @@ static int bfin_atapi_suspend(struct platform_device *pdev, pm_message_t state) static int bfin_atapi_resume(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); int ret; if (host) { @@ -1654,7 +1652,7 @@ static int bfin_atapi_resume(struct platform_device *pdev) static struct platform_driver bfin_atapi_driver = { .probe = bfin_atapi_probe, - .remove = __devexit_p(bfin_atapi_remove), + .remove = bfin_atapi_remove, .suspend = bfin_atapi_suspend, .resume = bfin_atapi_resume, .driver = { diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c index 549d28dbf90..c47caa807fa 100644 --- a/drivers/ata/pata_cmd640.c +++ b/drivers/ata/pata_cmd640.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/gfp.h> @@ -232,10 +231,10 @@ static int cmd640_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_sff_init_one(pdev, ppi, &cmd640_sht, NULL, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int cmd640_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -257,27 +256,16 @@ static struct pci_driver cmd640_pci_driver = { .id_table = cmd640, .probe = cmd640_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = cmd640_reinit_one, #endif }; -static int __init cmd640_init(void) -{ - return pci_register_driver(&cmd640_pci_driver); -} - -static void __exit cmd640_exit(void) -{ - pci_unregister_driver(&cmd640_pci_driver); -} +module_pci_driver(cmd640_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for CMD640 PATA controllers"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, cmd640); MODULE_VERSION(DRV_VERSION); - -module_init(cmd640_init); -module_exit(cmd640_exit); diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c index e1fb39a74ce..13ca5883285 100644 --- a/drivers/ata/pata_cmd64x.c +++ b/drivers/ata/pata_cmd64x.c @@ -3,6 +3,7 @@ * (C) 2005 Red Hat Inc * Alan Cox <alan@lxorguk.ukuu.org.uk> * (C) 2009-2010 Bartlomiej Zolnierkiewicz + * (C) 2012 MontaVista Software, LLC <source@mvista.com> * * Based upon * linux/drivers/ide/pci/cmd64x.c Version 1.30 Sept 10, 2002 @@ -25,14 +26,13 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> #include <linux/libata.h> #define DRV_NAME "pata_cmd64x" -#define DRV_VERSION "0.2.5" +#define DRV_VERSION "0.2.18" /* * CMD64x specific registers definition. @@ -229,28 +229,85 @@ static void cmd64x_set_dmamode(struct ata_port *ap, struct ata_device *adev) } /** - * cmd648_dma_stop - DMA stop callback - * @qc: Command in progress + * cmd64x_sff_irq_check - check IDE interrupt + * @ap: ATA interface * - * DMA has completed. + * Check IDE interrupt in CFR/ARTTIM23 registers. */ -static void cmd648_bmdma_stop(struct ata_queued_cmd *qc) +static bool cmd64x_sff_irq_check(struct ata_port *ap) { - struct ata_port *ap = qc->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 dma_intr; - int dma_mask = ap->port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; - int dma_reg = ap->port_no ? ARTTIM23 : CFR; + int irq_mask = ap->port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; + int irq_reg = ap->port_no ? ARTTIM23 : CFR; + u8 irq_stat; - ata_bmdma_stop(qc); + /* NOTE: reading the register should clear the interrupt */ + pci_read_config_byte(pdev, irq_reg, &irq_stat); + + return irq_stat & irq_mask; +} + +/** + * cmd64x_sff_irq_clear - clear IDE interrupt + * @ap: ATA interface + * + * Clear IDE interrupt in CFR/ARTTIM23 and DMA status registers. + */ + +static void cmd64x_sff_irq_clear(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + int irq_reg = ap->port_no ? ARTTIM23 : CFR; + u8 irq_stat; + + ata_bmdma_irq_clear(ap); + + /* Reading the register should be enough to clear the interrupt */ + pci_read_config_byte(pdev, irq_reg, &irq_stat); +} + +/** + * cmd648_sff_irq_check - check IDE interrupt + * @ap: ATA interface + * + * Check IDE interrupt in MRDMODE register. + */ + +static bool cmd648_sff_irq_check(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + unsigned long base = pci_resource_start(pdev, 4); + int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; + u8 mrdmode = inb(base + 1); + + return mrdmode & irq_mask; +} + +/** + * cmd648_sff_irq_clear - clear IDE interrupt + * @ap: ATA interface + * + * Clear IDE interrupt in MRDMODE and DMA status registers. + */ + +static void cmd648_sff_irq_clear(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + unsigned long base = pci_resource_start(pdev, 4); + int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; + u8 mrdmode; + + ata_bmdma_irq_clear(ap); - pci_read_config_byte(pdev, dma_reg, &dma_intr); - pci_write_config_byte(pdev, dma_reg, dma_intr | dma_mask); + /* Clear this port's interrupt bit (leaving the other port alone) */ + mrdmode = inb(base + 1); + mrdmode &= ~(MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1); + outb(mrdmode | irq_mask, base + 1); } /** - * cmd646r1_dma_stop - DMA stop callback + * cmd646r1_bmdma_stop - DMA stop callback * @qc: Command in progress * * Stub for now while investigating the r1 quirk in the old driver. @@ -273,18 +330,30 @@ static const struct ata_port_operations cmd64x_base_ops = { static struct ata_port_operations cmd64x_port_ops = { .inherits = &cmd64x_base_ops, + .sff_irq_check = cmd64x_sff_irq_check, + .sff_irq_clear = cmd64x_sff_irq_clear, .cable_detect = ata_cable_40wire, }; static struct ata_port_operations cmd646r1_port_ops = { .inherits = &cmd64x_base_ops, + .sff_irq_check = cmd64x_sff_irq_check, + .sff_irq_clear = cmd64x_sff_irq_clear, .bmdma_stop = cmd646r1_bmdma_stop, .cable_detect = ata_cable_40wire, }; +static struct ata_port_operations cmd646r3_port_ops = { + .inherits = &cmd64x_base_ops, + .sff_irq_check = cmd648_sff_irq_check, + .sff_irq_clear = cmd648_sff_irq_clear, + .cable_detect = ata_cable_40wire, +}; + static struct ata_port_operations cmd648_port_ops = { .inherits = &cmd64x_base_ops, - .bmdma_stop = cmd648_bmdma_stop, + .sff_irq_check = cmd648_sff_irq_check, + .sff_irq_clear = cmd648_sff_irq_clear, .cable_detect = cmd648_cable_detect, }; @@ -306,7 +375,7 @@ static void cmd64x_fixup(struct pci_dev *pdev) static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { - static const struct ata_port_info cmd_info[6] = { + static const struct ata_port_info cmd_info[7] = { { /* CMD 643 - no UDMA */ .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, @@ -319,12 +388,18 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) .mwdma_mask = ATA_MWDMA2, .port_ops = &cmd64x_port_ops }, - { /* CMD 646 with working UDMA */ + { /* CMD 646U with broken UDMA */ + .flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .port_ops = &cmd646r3_port_ops + }, + { /* CMD 646U2 with working UDMA */ .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA2, - .port_ops = &cmd64x_port_ops + .port_ops = &cmd646r3_port_ops }, { /* CMD 646 rev 1 */ .flags = ATA_FLAG_SLAVE_POSS, @@ -347,7 +422,7 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) .port_ops = &cmd648_port_ops } }; - const struct ata_port_info *ppi[] = { + const struct ata_port_info *ppi[] = { &cmd_info[id->driver_data], &cmd_info[id->driver_data], NULL @@ -368,45 +443,54 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (id->driver_data == 0) /* 643 */ ata_pci_bmdma_clear_simplex(pdev); - if (pdev->device == PCI_DEVICE_ID_CMD_646) { - /* Does UDMA work ? */ - if (pdev->revision > 4) { - ppi[0] = &cmd_info[2]; - ppi[1] = &cmd_info[2]; - } - /* Early rev with other problems ? */ - else if (pdev->revision == 1) { + if (pdev->device == PCI_DEVICE_ID_CMD_646) + switch (pdev->revision) { + /* UDMA works since rev 5 */ + default: ppi[0] = &cmd_info[3]; ppi[1] = &cmd_info[3]; - } - /* revs 1,2 have no CNTRL_CH0 */ - if (pdev->revision < 3) + break; + /* Interrupts in MRDMODE since rev 3 */ + case 3: + case 4: + ppi[0] = &cmd_info[2]; + ppi[1] = &cmd_info[2]; + break; + /* Rev 1 with other problems? */ + case 1: + ppi[0] = &cmd_info[4]; + ppi[1] = &cmd_info[4]; + /* FALL THRU */ + /* Early revs have no CNTRL_CH0 */ + case 2: + case 0: cntrl_ch0_ok = 0; - } + break; + } cmd64x_fixup(pdev); /* check for enabled ports */ pci_read_config_byte(pdev, CNTRL, ®); if (!port_ok) - dev_printk(KERN_NOTICE, &pdev->dev, "Mobility Bridge detected, ignoring CNTRL port enable/disable\n"); + dev_notice(&pdev->dev, "Mobility Bridge detected, ignoring CNTRL port enable/disable\n"); if (port_ok && cntrl_ch0_ok && !(reg & CNTRL_CH0)) { - dev_printk(KERN_NOTICE, &pdev->dev, "Primary port is disabled\n"); + dev_notice(&pdev->dev, "Primary port is disabled\n"); ppi[0] = &ata_dummy_port_info; - + } if (port_ok && !(reg & CNTRL_CH1)) { - dev_printk(KERN_NOTICE, &pdev->dev, "Secondary port is disabled\n"); + dev_notice(&pdev->dev, "Secondary port is disabled\n"); ppi[1] = &ata_dummy_port_info; } return ata_pci_bmdma_init_one(pdev, ppi, &cmd64x_sht, NULL, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int cmd64x_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -423,8 +507,8 @@ static int cmd64x_reinit_one(struct pci_dev *pdev) static const struct pci_device_id cmd64x[] = { { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 }, { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 }, - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 4 }, - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 5 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 5 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 6 }, { }, }; @@ -434,27 +518,16 @@ static struct pci_driver cmd64x_pci_driver = { .id_table = cmd64x, .probe = cmd64x_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = cmd64x_reinit_one, #endif }; -static int __init cmd64x_init(void) -{ - return pci_register_driver(&cmd64x_pci_driver); -} - -static void __exit cmd64x_exit(void) -{ - pci_unregister_driver(&cmd64x_pci_driver); -} +module_pci_driver(cmd64x_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for CMD64x series PATA controllers"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, cmd64x); MODULE_VERSION(DRV_VERSION); - -module_init(cmd64x_init); -module_exit(cmd64x_exit); diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c index 9ddcddc66a2..d65cb9d2fa8 100644 --- a/drivers/ata/pata_cs5520.c +++ b/drivers/ata/pata_cs5520.c @@ -34,7 +34,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -115,7 +114,7 @@ static struct ata_port_operations cs5520_port_ops = { .set_piomode = cs5520_set_piomode, }; -static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +static int cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { static const unsigned int cmd_port[] = { 0x1F0, 0x170 }; static const unsigned int ctl_port[] = { 0x3F6, 0x376 }; @@ -230,7 +229,7 @@ static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_devi return ata_host_register(host, &cs5520_sht); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /** * cs5520_reinit_one - device resume * @pdev: PCI device @@ -241,7 +240,7 @@ static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_devi static int cs5520_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); u8 pcicfg; int rc; @@ -269,7 +268,7 @@ static int cs5520_reinit_one(struct pci_dev *pdev) static int cs5520_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc = 0; rc = ata_host_suspend(host, mesg); @@ -279,7 +278,7 @@ static int cs5520_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) pci_save_state(pdev); return 0; } -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ /* For now keep DMA off. We can set it for all but A rev CS5510 once the core ATA code can handle it */ @@ -296,28 +295,16 @@ static struct pci_driver cs5520_pci_driver = { .id_table = pata_cs5520, .probe = cs5520_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = cs5520_pci_device_suspend, .resume = cs5520_reinit_one, #endif }; -static int __init cs5520_init(void) -{ - return pci_register_driver(&cs5520_pci_driver); -} - -static void __exit cs5520_exit(void) -{ - pci_unregister_driver(&cs5520_pci_driver); -} +module_pci_driver(cs5520_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Cyrix CS5510/5520"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pata_cs5520); MODULE_VERSION(DRV_VERSION); - -module_init(cs5520_init); -module_exit(cs5520_exit); - diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c index f792330f0d8..48ae4b43447 100644 --- a/drivers/ata/pata_cs5530.c +++ b/drivers/ata/pata_cs5530.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -327,10 +326,10 @@ static int cs5530_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_bmdma_init_one(pdev, ppi, &cs5530_sht, NULL, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int cs5530_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -344,7 +343,7 @@ static int cs5530_reinit_one(struct pci_dev *pdev) ata_host_resume(host); return 0; } -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static const struct pci_device_id cs5530[] = { { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), }, @@ -357,27 +356,16 @@ static struct pci_driver cs5530_pci_driver = { .id_table = cs5530, .probe = cs5530_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = cs5530_reinit_one, #endif }; -static int __init cs5530_init(void) -{ - return pci_register_driver(&cs5530_pci_driver); -} - -static void __exit cs5530_exit(void) -{ - pci_unregister_driver(&cs5530_pci_driver); -} +module_pci_driver(cs5530_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Cyrix/NS/AMD 5530"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, cs5530); MODULE_VERSION(DRV_VERSION); - -module_init(cs5530_init); -module_exit(cs5530_exit); diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c index a0b4640125a..97584e8456d 100644 --- a/drivers/ata/pata_cs5535.c +++ b/drivers/ata/pata_cs5535.c @@ -31,7 +31,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -201,27 +200,16 @@ static struct pci_driver cs5535_pci_driver = { .id_table = cs5535, .probe = cs5535_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init cs5535_init(void) -{ - return pci_register_driver(&cs5535_pci_driver); -} - -static void __exit cs5535_exit(void) -{ - pci_unregister_driver(&cs5535_pci_driver); -} +module_pci_driver(cs5535_pci_driver); MODULE_AUTHOR("Alan Cox, Jens Altmann, Wolfgan Zuleger, Alexander Kiausch"); MODULE_DESCRIPTION("low-level driver for the NS/AMD 5535"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, cs5535); MODULE_VERSION(DRV_VERSION); - -module_init(cs5535_init); -module_exit(cs5535_exit); diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c index 7a402c75ab9..6c15a554efb 100644 --- a/drivers/ata/pata_cs5536.c +++ b/drivers/ata/pata_cs5536.c @@ -33,11 +33,11 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/libata.h> #include <scsi/scsi_host.h> +#include <linux/dmi.h> #ifdef CONFIG_X86_32 #include <asm/msr.h> @@ -80,6 +80,21 @@ enum { IDE_ETC_UDMA_MASK = 0xc0, }; +/* Some Bachmann OT200 devices have a non working UDMA support due a + * missing resistor. + */ +static const struct dmi_system_id udma_quirk_dmi_table[] = { + { + .ident = "Bachmann electronic OT200", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Bachmann electronic"), + DMI_MATCH(DMI_PRODUCT_NAME, "OT200"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1") + }, + }, + { } +}; + static int cs5536_read(struct pci_dev *pdev, int reg, u32 *val) { if (unlikely(use_msr)) { @@ -242,9 +257,23 @@ static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id) .port_ops = &cs5536_port_ops, }; - const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info }; + static const struct ata_port_info no_udma_info = { + .flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = ATA_PIO4, + .port_ops = &cs5536_port_ops, + }; + + + const struct ata_port_info *ppi[2]; u32 cfg; + if (dmi_check_system(udma_quirk_dmi_table)) + ppi[0] = &no_udma_info; + else + ppi[0] = &info; + + ppi[1] = &ata_dummy_port_info; + if (use_msr) printk(KERN_ERR DRV_NAME ": Using MSR regs instead of PCI\n"); @@ -268,27 +297,16 @@ static struct pci_driver cs5536_pci_driver = { .id_table = cs5536, .probe = cs5536_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init cs5536_init(void) -{ - return pci_register_driver(&cs5536_pci_driver); -} - -static void __exit cs5536_exit(void) -{ - pci_unregister_driver(&cs5536_pci_driver); -} +module_pci_driver(cs5536_pci_driver); MODULE_AUTHOR("Martin K. Petersen"); MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, cs5536); MODULE_VERSION(DRV_VERSION); - -module_init(cs5536_init); -module_exit(cs5536_exit); diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c index 6d915b063d9..793018460d8 100644 --- a/drivers/ata/pata_cypress.c +++ b/drivers/ata/pata_cypress.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -152,29 +151,16 @@ static struct pci_driver cy82c693_pci_driver = { .id_table = cy82c693, .probe = cy82c693_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init cy82c693_init(void) -{ - return pci_register_driver(&cy82c693_pci_driver); -} - - -static void __exit cy82c693_exit(void) -{ - pci_unregister_driver(&cy82c693_pci_driver); -} - +module_pci_driver(cy82c693_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the CY82C693 PATA controller"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, cy82c693); MODULE_VERSION(DRV_VERSION); - -module_init(cy82c693_init); -module_exit(cy82c693_exit); diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c index f0243ed206f..4a57a6f032d 100644 --- a/drivers/ata/pata_efar.c +++ b/drivers/ata/pata_efar.c @@ -14,7 +14,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -289,28 +288,16 @@ static struct pci_driver efar_pci_driver = { .id_table = efar_pci_tbl, .probe = efar_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init efar_init(void) -{ - return pci_register_driver(&efar_pci_driver); -} - -static void __exit efar_exit(void) -{ - pci_unregister_driver(&efar_pci_driver); -} - -module_init(efar_init); -module_exit(efar_exit); +module_pci_driver(efar_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for EFAR PIIX clones"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, efar_pci_tbl); MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c new file mode 100644 index 00000000000..4d37c5415fc --- /dev/null +++ b/drivers/ata/pata_ep93xx.c @@ -0,0 +1,1037 @@ +/* + * EP93XX PATA controller driver. + * + * Copyright (c) 2012, Metasoft s.c. + * Rafal Prylowski <prylowski@metasoft.pl> + * + * Based on pata_scc.c, pata_icside.c and on earlier version of EP93XX + * PATA driver by Lennert Buytenhek and Alessandro Zummo. + * Read/Write timings, resource management and other improvements + * from driver by Joao Ramos and Bartlomiej Zolnierkiewicz. + * DMA engine support based on spi-ep93xx.c by Mika Westerberg. + * + * Original copyrights: + * + * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs + * PATA host controller driver. + * + * Copyright (c) 2009, Bartlomiej Zolnierkiewicz + * + * Heavily based on the ep93xx-ide.c driver: + * + * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt> + * INESC Inovacao (INOV) + * + * EP93XX PATA controller driver. + * Copyright (C) 2007 Lennert Buytenhek <buytenh@wantstofly.org> + * + * An ATA driver for the Cirrus Logic EP93xx PATA controller. + * + * Based on an earlier version by Alessandro Zummo, which is: + * Copyright (C) 2006 Tower Technologies + */ + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/blkdev.h> +#include <scsi/scsi_host.h> +#include <linux/ata.h> +#include <linux/libata.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/ktime.h> + +#include <linux/platform_data/dma-ep93xx.h> +#include <mach/platform.h> + +#define DRV_NAME "ep93xx-ide" +#define DRV_VERSION "1.0" + +enum { + /* IDE Control Register */ + IDECTRL = 0x00, + IDECTRL_CS0N = (1 << 0), + IDECTRL_CS1N = (1 << 1), + IDECTRL_DIORN = (1 << 5), + IDECTRL_DIOWN = (1 << 6), + IDECTRL_INTRQ = (1 << 9), + IDECTRL_IORDY = (1 << 10), + /* + * the device IDE register to be accessed is selected through + * IDECTRL register's specific bitfields 'DA', 'CS1N' and 'CS0N': + * b4 b3 b2 b1 b0 + * A2 A1 A0 CS1N CS0N + * the values filled in this structure allows the value to be directly + * ORed to the IDECTRL register, hence giving directly the A[2:0] and + * CS1N/CS0N values for each IDE register. + * The values correspond to the transformation: + * ((real IDE address) << 2) | CS1N value << 1 | CS0N value + */ + IDECTRL_ADDR_CMD = 0 + 2, /* CS1 */ + IDECTRL_ADDR_DATA = (ATA_REG_DATA << 2) + 2, + IDECTRL_ADDR_ERROR = (ATA_REG_ERR << 2) + 2, + IDECTRL_ADDR_FEATURE = (ATA_REG_FEATURE << 2) + 2, + IDECTRL_ADDR_NSECT = (ATA_REG_NSECT << 2) + 2, + IDECTRL_ADDR_LBAL = (ATA_REG_LBAL << 2) + 2, + IDECTRL_ADDR_LBAM = (ATA_REG_LBAM << 2) + 2, + IDECTRL_ADDR_LBAH = (ATA_REG_LBAH << 2) + 2, + IDECTRL_ADDR_DEVICE = (ATA_REG_DEVICE << 2) + 2, + IDECTRL_ADDR_STATUS = (ATA_REG_STATUS << 2) + 2, + IDECTRL_ADDR_COMMAND = (ATA_REG_CMD << 2) + 2, + IDECTRL_ADDR_ALTSTATUS = (0x06 << 2) + 1, /* CS0 */ + IDECTRL_ADDR_CTL = (0x06 << 2) + 1, /* CS0 */ + + /* IDE Configuration Register */ + IDECFG = 0x04, + IDECFG_IDEEN = (1 << 0), + IDECFG_PIO = (1 << 1), + IDECFG_MDMA = (1 << 2), + IDECFG_UDMA = (1 << 3), + IDECFG_MODE_SHIFT = 4, + IDECFG_MODE_MASK = (0xf << 4), + IDECFG_WST_SHIFT = 8, + IDECFG_WST_MASK = (0x3 << 8), + + /* MDMA Operation Register */ + IDEMDMAOP = 0x08, + + /* UDMA Operation Register */ + IDEUDMAOP = 0x0c, + IDEUDMAOP_UEN = (1 << 0), + IDEUDMAOP_RWOP = (1 << 1), + + /* PIO/MDMA/UDMA Data Registers */ + IDEDATAOUT = 0x10, + IDEDATAIN = 0x14, + IDEMDMADATAOUT = 0x18, + IDEMDMADATAIN = 0x1c, + IDEUDMADATAOUT = 0x20, + IDEUDMADATAIN = 0x24, + + /* UDMA Status Register */ + IDEUDMASTS = 0x28, + IDEUDMASTS_DMAIDE = (1 << 16), + IDEUDMASTS_INTIDE = (1 << 17), + IDEUDMASTS_SBUSY = (1 << 18), + IDEUDMASTS_NDO = (1 << 24), + IDEUDMASTS_NDI = (1 << 25), + IDEUDMASTS_N4X = (1 << 26), + + /* UDMA Debug Status Register */ + IDEUDMADEBUG = 0x2c, +}; + +struct ep93xx_pata_data { + const struct platform_device *pdev; + void __iomem *ide_base; + struct ata_timing t; + bool iordy; + + unsigned long udma_in_phys; + unsigned long udma_out_phys; + + struct dma_chan *dma_rx_channel; + struct ep93xx_dma_data dma_rx_data; + struct dma_chan *dma_tx_channel; + struct ep93xx_dma_data dma_tx_data; +}; + +static void ep93xx_pata_clear_regs(void __iomem *base) +{ + writel(IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN | + IDECTRL_DIOWN, base + IDECTRL); + + writel(0, base + IDECFG); + writel(0, base + IDEMDMAOP); + writel(0, base + IDEUDMAOP); + writel(0, base + IDEDATAOUT); + writel(0, base + IDEDATAIN); + writel(0, base + IDEMDMADATAOUT); + writel(0, base + IDEMDMADATAIN); + writel(0, base + IDEUDMADATAOUT); + writel(0, base + IDEUDMADATAIN); + writel(0, base + IDEUDMADEBUG); +} + +static bool ep93xx_pata_check_iordy(void __iomem *base) +{ + return !!(readl(base + IDECTRL) & IDECTRL_IORDY); +} + +/* + * According to EP93xx User's Guide, WST field of IDECFG specifies number + * of HCLK cycles to hold the data bus after a PIO write operation. + * It should be programmed to guarantee following delays: + * + * PIO Mode [ns] + * 0 30 + * 1 20 + * 2 15 + * 3 10 + * 4 5 + * + * Maximum possible value for HCLK is 100MHz. + */ +static int ep93xx_pata_get_wst(int pio_mode) +{ + int val; + + if (pio_mode == 0) + val = 3; + else if (pio_mode < 3) + val = 2; + else + val = 1; + + return val << IDECFG_WST_SHIFT; +} + +static void ep93xx_pata_enable_pio(void __iomem *base, int pio_mode) +{ + writel(IDECFG_IDEEN | IDECFG_PIO | + ep93xx_pata_get_wst(pio_mode) | + (pio_mode << IDECFG_MODE_SHIFT), base + IDECFG); +} + +/* + * Based on delay loop found in mach-pxa/mp900.c. + * + * Single iteration should take 5 cpu cycles. This is 25ns assuming the + * fastest ep93xx cpu speed (200MHz) and is better optimized for PIO4 timings + * than eg. 20ns. + */ +static void ep93xx_pata_delay(unsigned long count) +{ + __asm__ volatile ( + "0:\n" + "mov r0, r0\n" + "subs %0, %1, #1\n" + "bge 0b\n" + : "=r" (count) + : "0" (count) + ); +} + +static unsigned long ep93xx_pata_wait_for_iordy(void __iomem *base, + unsigned long t2) +{ + /* + * According to ATA specification, IORDY pin can be first sampled + * tA = 35ns after activation of DIOR-/DIOW-. Maximum IORDY pulse + * width is tB = 1250ns. + * + * We are already t2 delay loop iterations after activation of + * DIOR-/DIOW-, so we set timeout to (1250 + 35) / 25 - t2 additional + * delay loop iterations. + */ + unsigned long start = (1250 + 35) / 25 - t2; + unsigned long counter = start; + + while (!ep93xx_pata_check_iordy(base) && counter--) + ep93xx_pata_delay(1); + return start - counter; +} + +/* common part at start of ep93xx_pata_read/write() */ +static void ep93xx_pata_rw_begin(void __iomem *base, unsigned long addr, + unsigned long t1) +{ + writel(IDECTRL_DIOWN | IDECTRL_DIORN | addr, base + IDECTRL); + ep93xx_pata_delay(t1); +} + +/* common part at end of ep93xx_pata_read/write() */ +static void ep93xx_pata_rw_end(void __iomem *base, unsigned long addr, + bool iordy, unsigned long t0, unsigned long t2, + unsigned long t2i) +{ + ep93xx_pata_delay(t2); + /* lengthen t2 if needed */ + if (iordy) + t2 += ep93xx_pata_wait_for_iordy(base, t2); + writel(IDECTRL_DIOWN | IDECTRL_DIORN | addr, base + IDECTRL); + if (t0 > t2 && t0 - t2 > t2i) + ep93xx_pata_delay(t0 - t2); + else + ep93xx_pata_delay(t2i); +} + +static u16 ep93xx_pata_read(struct ep93xx_pata_data *drv_data, + unsigned long addr, + bool reg) +{ + void __iomem *base = drv_data->ide_base; + const struct ata_timing *t = &drv_data->t; + unsigned long t0 = reg ? t->cyc8b : t->cycle; + unsigned long t2 = reg ? t->act8b : t->active; + unsigned long t2i = reg ? t->rec8b : t->recover; + + ep93xx_pata_rw_begin(base, addr, t->setup); + writel(IDECTRL_DIOWN | addr, base + IDECTRL); + /* + * The IDEDATAIN register is loaded from the DD pins at the positive + * edge of the DIORN signal. (EP93xx UG p27-14) + */ + ep93xx_pata_rw_end(base, addr, drv_data->iordy, t0, t2, t2i); + return readl(base + IDEDATAIN); +} + +/* IDE register read */ +static u16 ep93xx_pata_read_reg(struct ep93xx_pata_data *drv_data, + unsigned long addr) +{ + return ep93xx_pata_read(drv_data, addr, true); +} + +/* PIO data read */ +static u16 ep93xx_pata_read_data(struct ep93xx_pata_data *drv_data, + unsigned long addr) +{ + return ep93xx_pata_read(drv_data, addr, false); +} + +static void ep93xx_pata_write(struct ep93xx_pata_data *drv_data, + u16 value, unsigned long addr, + bool reg) +{ + void __iomem *base = drv_data->ide_base; + const struct ata_timing *t = &drv_data->t; + unsigned long t0 = reg ? t->cyc8b : t->cycle; + unsigned long t2 = reg ? t->act8b : t->active; + unsigned long t2i = reg ? t->rec8b : t->recover; + + ep93xx_pata_rw_begin(base, addr, t->setup); + /* + * Value from IDEDATAOUT register is driven onto the DD pins when + * DIOWN is low. (EP93xx UG p27-13) + */ + writel(value, base + IDEDATAOUT); + writel(IDECTRL_DIORN | addr, base + IDECTRL); + ep93xx_pata_rw_end(base, addr, drv_data->iordy, t0, t2, t2i); +} + +/* IDE register write */ +static void ep93xx_pata_write_reg(struct ep93xx_pata_data *drv_data, + u16 value, unsigned long addr) +{ + ep93xx_pata_write(drv_data, value, addr, true); +} + +/* PIO data write */ +static void ep93xx_pata_write_data(struct ep93xx_pata_data *drv_data, + u16 value, unsigned long addr) +{ + ep93xx_pata_write(drv_data, value, addr, false); +} + +static void ep93xx_pata_set_piomode(struct ata_port *ap, + struct ata_device *adev) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + struct ata_device *pair = ata_dev_pair(adev); + /* + * Calculate timings for the delay loop, assuming ep93xx cpu speed + * is 200MHz (maximum possible for ep93xx). If actual cpu speed is + * slower, we will wait a bit longer in each delay. + * Additional division of cpu speed by 5, because single iteration + * of our delay loop takes 5 cpu cycles (25ns). + */ + unsigned long T = 1000000 / (200 / 5); + + ata_timing_compute(adev, adev->pio_mode, &drv_data->t, T, 0); + if (pair && pair->pio_mode) { + struct ata_timing t; + ata_timing_compute(pair, pair->pio_mode, &t, T, 0); + ata_timing_merge(&t, &drv_data->t, &drv_data->t, + ATA_TIMING_SETUP | ATA_TIMING_8BIT); + } + drv_data->iordy = ata_pio_need_iordy(adev); + + ep93xx_pata_enable_pio(drv_data->ide_base, + adev->pio_mode - XFER_PIO_0); +} + +/* Note: original code is ata_sff_check_status */ +static u8 ep93xx_pata_check_status(struct ata_port *ap) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + + return ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_STATUS); +} + +static u8 ep93xx_pata_check_altstatus(struct ata_port *ap) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + + return ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_ALTSTATUS); +} + +/* Note: original code is ata_sff_tf_load */ +static void ep93xx_pata_tf_load(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; + + if (tf->ctl != ap->last_ctl) { + ep93xx_pata_write_reg(drv_data, tf->ctl, IDECTRL_ADDR_CTL); + ap->last_ctl = tf->ctl; + ata_wait_idle(ap); + } + + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { + ep93xx_pata_write_reg(drv_data, tf->hob_feature, + IDECTRL_ADDR_FEATURE); + ep93xx_pata_write_reg(drv_data, tf->hob_nsect, + IDECTRL_ADDR_NSECT); + ep93xx_pata_write_reg(drv_data, tf->hob_lbal, + IDECTRL_ADDR_LBAL); + ep93xx_pata_write_reg(drv_data, tf->hob_lbam, + IDECTRL_ADDR_LBAM); + ep93xx_pata_write_reg(drv_data, tf->hob_lbah, + IDECTRL_ADDR_LBAH); + } + + if (is_addr) { + ep93xx_pata_write_reg(drv_data, tf->feature, + IDECTRL_ADDR_FEATURE); + ep93xx_pata_write_reg(drv_data, tf->nsect, IDECTRL_ADDR_NSECT); + ep93xx_pata_write_reg(drv_data, tf->lbal, IDECTRL_ADDR_LBAL); + ep93xx_pata_write_reg(drv_data, tf->lbam, IDECTRL_ADDR_LBAM); + ep93xx_pata_write_reg(drv_data, tf->lbah, IDECTRL_ADDR_LBAH); + } + + if (tf->flags & ATA_TFLAG_DEVICE) + ep93xx_pata_write_reg(drv_data, tf->device, + IDECTRL_ADDR_DEVICE); + + ata_wait_idle(ap); +} + +/* Note: original code is ata_sff_tf_read */ +static void ep93xx_pata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + + tf->command = ep93xx_pata_check_status(ap); + tf->feature = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_FEATURE); + tf->nsect = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_NSECT); + tf->lbal = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_LBAL); + tf->lbam = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_LBAM); + tf->lbah = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_LBAH); + tf->device = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_DEVICE); + + if (tf->flags & ATA_TFLAG_LBA48) { + ep93xx_pata_write_reg(drv_data, tf->ctl | ATA_HOB, + IDECTRL_ADDR_CTL); + tf->hob_feature = ep93xx_pata_read_reg(drv_data, + IDECTRL_ADDR_FEATURE); + tf->hob_nsect = ep93xx_pata_read_reg(drv_data, + IDECTRL_ADDR_NSECT); + tf->hob_lbal = ep93xx_pata_read_reg(drv_data, + IDECTRL_ADDR_LBAL); + tf->hob_lbam = ep93xx_pata_read_reg(drv_data, + IDECTRL_ADDR_LBAM); + tf->hob_lbah = ep93xx_pata_read_reg(drv_data, + IDECTRL_ADDR_LBAH); + ep93xx_pata_write_reg(drv_data, tf->ctl, IDECTRL_ADDR_CTL); + ap->last_ctl = tf->ctl; + } +} + +/* Note: original code is ata_sff_exec_command */ +static void ep93xx_pata_exec_command(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + + ep93xx_pata_write_reg(drv_data, tf->command, + IDECTRL_ADDR_COMMAND); + ata_sff_pause(ap); +} + +/* Note: original code is ata_sff_dev_select */ +static void ep93xx_pata_dev_select(struct ata_port *ap, unsigned int device) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + u8 tmp = ATA_DEVICE_OBS; + + if (device != 0) + tmp |= ATA_DEV1; + + ep93xx_pata_write_reg(drv_data, tmp, IDECTRL_ADDR_DEVICE); + ata_sff_pause(ap); /* needed; also flushes, for mmio */ +} + +/* Note: original code is ata_sff_set_devctl */ +static void ep93xx_pata_set_devctl(struct ata_port *ap, u8 ctl) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + + ep93xx_pata_write_reg(drv_data, ctl, IDECTRL_ADDR_CTL); +} + +/* Note: original code is ata_sff_data_xfer */ +static unsigned int ep93xx_pata_data_xfer(struct ata_device *adev, + unsigned char *buf, + unsigned int buflen, int rw) +{ + struct ata_port *ap = adev->link->ap; + struct ep93xx_pata_data *drv_data = ap->host->private_data; + u16 *data = (u16 *)buf; + unsigned int words = buflen >> 1; + + /* Transfer multiple of 2 bytes */ + while (words--) + if (rw == READ) + *data++ = cpu_to_le16( + ep93xx_pata_read_data( + drv_data, IDECTRL_ADDR_DATA)); + else + ep93xx_pata_write_data(drv_data, le16_to_cpu(*data++), + IDECTRL_ADDR_DATA); + + /* Transfer trailing 1 byte, if any. */ + if (unlikely(buflen & 0x01)) { + unsigned char pad[2] = { }; + + buf += buflen - 1; + + if (rw == READ) { + *pad = cpu_to_le16( + ep93xx_pata_read_data( + drv_data, IDECTRL_ADDR_DATA)); + *buf = pad[0]; + } else { + pad[0] = *buf; + ep93xx_pata_write_data(drv_data, le16_to_cpu(*pad), + IDECTRL_ADDR_DATA); + } + words++; + } + + return words << 1; +} + +/* Note: original code is ata_devchk */ +static bool ep93xx_pata_device_is_present(struct ata_port *ap, + unsigned int device) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + u8 nsect, lbal; + + ap->ops->sff_dev_select(ap, device); + + ep93xx_pata_write_reg(drv_data, 0x55, IDECTRL_ADDR_NSECT); + ep93xx_pata_write_reg(drv_data, 0xaa, IDECTRL_ADDR_LBAL); + + ep93xx_pata_write_reg(drv_data, 0xaa, IDECTRL_ADDR_NSECT); + ep93xx_pata_write_reg(drv_data, 0x55, IDECTRL_ADDR_LBAL); + + ep93xx_pata_write_reg(drv_data, 0x55, IDECTRL_ADDR_NSECT); + ep93xx_pata_write_reg(drv_data, 0xaa, IDECTRL_ADDR_LBAL); + + nsect = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_NSECT); + lbal = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_LBAL); + + if ((nsect == 0x55) && (lbal == 0xaa)) + return true; + + return false; +} + +/* Note: original code is ata_sff_wait_after_reset */ +static int ep93xx_pata_wait_after_reset(struct ata_link *link, + unsigned int devmask, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct ep93xx_pata_data *drv_data = ap->host->private_data; + unsigned int dev0 = devmask & (1 << 0); + unsigned int dev1 = devmask & (1 << 1); + int rc, ret = 0; + + ata_msleep(ap, ATA_WAIT_AFTER_RESET); + + /* always check readiness of the master device */ + rc = ata_sff_wait_ready(link, deadline); + /* + * -ENODEV means the odd clown forgot the D7 pulldown resistor + * and TF status is 0xff, bail out on it too. + */ + if (rc) + return rc; + + /* + * if device 1 was found in ata_devchk, wait for register + * access briefly, then wait for BSY to clear. + */ + if (dev1) { + int i; + + ap->ops->sff_dev_select(ap, 1); + + /* + * Wait for register access. Some ATAPI devices fail + * to set nsect/lbal after reset, so don't waste too + * much time on it. We're gonna wait for !BSY anyway. + */ + for (i = 0; i < 2; i++) { + u8 nsect, lbal; + + nsect = ep93xx_pata_read_reg(drv_data, + IDECTRL_ADDR_NSECT); + lbal = ep93xx_pata_read_reg(drv_data, + IDECTRL_ADDR_LBAL); + if (nsect == 1 && lbal == 1) + break; + msleep(50); /* give drive a breather */ + } + + rc = ata_sff_wait_ready(link, deadline); + if (rc) { + if (rc != -ENODEV) + return rc; + ret = rc; + } + } + /* is all this really necessary? */ + ap->ops->sff_dev_select(ap, 0); + if (dev1) + ap->ops->sff_dev_select(ap, 1); + if (dev0) + ap->ops->sff_dev_select(ap, 0); + + return ret; +} + +/* Note: original code is ata_bus_softreset */ +static int ep93xx_pata_bus_softreset(struct ata_port *ap, unsigned int devmask, + unsigned long deadline) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + + ep93xx_pata_write_reg(drv_data, ap->ctl, IDECTRL_ADDR_CTL); + udelay(20); /* FIXME: flush */ + ep93xx_pata_write_reg(drv_data, ap->ctl | ATA_SRST, IDECTRL_ADDR_CTL); + udelay(20); /* FIXME: flush */ + ep93xx_pata_write_reg(drv_data, ap->ctl, IDECTRL_ADDR_CTL); + ap->last_ctl = ap->ctl; + + return ep93xx_pata_wait_after_reset(&ap->link, devmask, deadline); +} + +static void ep93xx_pata_release_dma(struct ep93xx_pata_data *drv_data) +{ + if (drv_data->dma_rx_channel) { + dma_release_channel(drv_data->dma_rx_channel); + drv_data->dma_rx_channel = NULL; + } + if (drv_data->dma_tx_channel) { + dma_release_channel(drv_data->dma_tx_channel); + drv_data->dma_tx_channel = NULL; + } +} + +static bool ep93xx_pata_dma_filter(struct dma_chan *chan, void *filter_param) +{ + if (ep93xx_dma_chan_is_m2p(chan)) + return false; + + chan->private = filter_param; + return true; +} + +static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data) +{ + const struct platform_device *pdev = drv_data->pdev; + dma_cap_mask_t mask; + struct dma_slave_config conf; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* + * Request two channels for IDE. Another possibility would be + * to request only one channel, and reprogram it's direction at + * start of new transfer. + */ + drv_data->dma_rx_data.port = EP93XX_DMA_IDE; + drv_data->dma_rx_data.direction = DMA_FROM_DEVICE; + drv_data->dma_rx_data.name = "ep93xx-pata-rx"; + drv_data->dma_rx_channel = dma_request_channel(mask, + ep93xx_pata_dma_filter, &drv_data->dma_rx_data); + if (!drv_data->dma_rx_channel) + return; + + drv_data->dma_tx_data.port = EP93XX_DMA_IDE; + drv_data->dma_tx_data.direction = DMA_TO_DEVICE; + drv_data->dma_tx_data.name = "ep93xx-pata-tx"; + drv_data->dma_tx_channel = dma_request_channel(mask, + ep93xx_pata_dma_filter, &drv_data->dma_tx_data); + if (!drv_data->dma_tx_channel) { + dma_release_channel(drv_data->dma_rx_channel); + return; + } + + /* Configure receive channel direction and source address */ + memset(&conf, 0, sizeof(conf)); + conf.direction = DMA_FROM_DEVICE; + conf.src_addr = drv_data->udma_in_phys; + conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + if (dmaengine_slave_config(drv_data->dma_rx_channel, &conf)) { + dev_err(&pdev->dev, "failed to configure rx dma channel\n"); + ep93xx_pata_release_dma(drv_data); + return; + } + + /* Configure transmit channel direction and destination address */ + memset(&conf, 0, sizeof(conf)); + conf.direction = DMA_TO_DEVICE; + conf.dst_addr = drv_data->udma_out_phys; + conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + if (dmaengine_slave_config(drv_data->dma_tx_channel, &conf)) { + dev_err(&pdev->dev, "failed to configure tx dma channel\n"); + ep93xx_pata_release_dma(drv_data); + } +} + +static void ep93xx_pata_dma_start(struct ata_queued_cmd *qc) +{ + struct dma_async_tx_descriptor *txd; + struct ep93xx_pata_data *drv_data = qc->ap->host->private_data; + void __iomem *base = drv_data->ide_base; + struct ata_device *adev = qc->dev; + u32 v = qc->dma_dir == DMA_TO_DEVICE ? IDEUDMAOP_RWOP : 0; + struct dma_chan *channel = qc->dma_dir == DMA_TO_DEVICE + ? drv_data->dma_tx_channel : drv_data->dma_rx_channel; + + txd = dmaengine_prep_slave_sg(channel, qc->sg, qc->n_elem, qc->dma_dir, + DMA_CTRL_ACK); + if (!txd) { + dev_err(qc->ap->dev, "failed to prepare slave for sg dma\n"); + return; + } + txd->callback = NULL; + txd->callback_param = NULL; + + if (dmaengine_submit(txd) < 0) { + dev_err(qc->ap->dev, "failed to submit dma transfer\n"); + return; + } + dma_async_issue_pending(channel); + + /* + * When enabling UDMA operation, IDEUDMAOP register needs to be + * programmed in three step sequence: + * 1) set or clear the RWOP bit, + * 2) perform dummy read of the register, + * 3) set the UEN bit. + */ + writel(v, base + IDEUDMAOP); + readl(base + IDEUDMAOP); + writel(v | IDEUDMAOP_UEN, base + IDEUDMAOP); + + writel(IDECFG_IDEEN | IDECFG_UDMA | + ((adev->xfer_mode - XFER_UDMA_0) << IDECFG_MODE_SHIFT), + base + IDECFG); +} + +static void ep93xx_pata_dma_stop(struct ata_queued_cmd *qc) +{ + struct ep93xx_pata_data *drv_data = qc->ap->host->private_data; + void __iomem *base = drv_data->ide_base; + + /* terminate all dma transfers, if not yet finished */ + dmaengine_terminate_all(drv_data->dma_rx_channel); + dmaengine_terminate_all(drv_data->dma_tx_channel); + + /* + * To properly stop IDE-DMA, IDEUDMAOP register must to be cleared + * and IDECTRL register must be set to default value. + */ + writel(0, base + IDEUDMAOP); + writel(readl(base + IDECTRL) | IDECTRL_DIOWN | IDECTRL_DIORN | + IDECTRL_CS0N | IDECTRL_CS1N, base + IDECTRL); + + ep93xx_pata_enable_pio(drv_data->ide_base, + qc->dev->pio_mode - XFER_PIO_0); + + ata_sff_dma_pause(qc->ap); +} + +static void ep93xx_pata_dma_setup(struct ata_queued_cmd *qc) +{ + qc->ap->ops->sff_exec_command(qc->ap, &qc->tf); +} + +static u8 ep93xx_pata_dma_status(struct ata_port *ap) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + u32 val = readl(drv_data->ide_base + IDEUDMASTS); + + /* + * UDMA Status Register bits: + * + * DMAIDE - DMA request signal from UDMA state machine, + * INTIDE - INT line generated by UDMA because of errors in the + * state machine, + * SBUSY - UDMA state machine busy, not in idle state, + * NDO - error for data-out not completed, + * NDI - error for data-in not completed, + * N4X - error for data transferred not multiplies of four + * 32-bit words. + * (EP93xx UG p27-17) + */ + if (val & IDEUDMASTS_NDO || val & IDEUDMASTS_NDI || + val & IDEUDMASTS_N4X || val & IDEUDMASTS_INTIDE) + return ATA_DMA_ERR; + + /* read INTRQ (INT[3]) pin input state */ + if (readl(drv_data->ide_base + IDECTRL) & IDECTRL_INTRQ) + return ATA_DMA_INTR; + + if (val & IDEUDMASTS_SBUSY || val & IDEUDMASTS_DMAIDE) + return ATA_DMA_ACTIVE; + + return 0; +} + +/* Note: original code is ata_sff_softreset */ +static int ep93xx_pata_softreset(struct ata_link *al, unsigned int *classes, + unsigned long deadline) +{ + struct ata_port *ap = al->ap; + unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; + unsigned int devmask = 0; + int rc; + u8 err; + + /* determine if device 0/1 are present */ + if (ep93xx_pata_device_is_present(ap, 0)) + devmask |= (1 << 0); + if (slave_possible && ep93xx_pata_device_is_present(ap, 1)) + devmask |= (1 << 1); + + /* select device 0 again */ + ap->ops->sff_dev_select(al->ap, 0); + + /* issue bus reset */ + rc = ep93xx_pata_bus_softreset(ap, devmask, deadline); + /* if link is ocuppied, -ENODEV too is an error */ + if (rc && (rc != -ENODEV || sata_scr_valid(al))) { + ata_link_err(al, "SRST failed (errno=%d)\n", rc); + return rc; + } + + /* determine by signature whether we have ATA or ATAPI devices */ + classes[0] = ata_sff_dev_classify(&al->device[0], devmask & (1 << 0), + &err); + if (slave_possible && err != 0x81) + classes[1] = ata_sff_dev_classify(&al->device[1], + devmask & (1 << 1), &err); + + return 0; +} + +/* Note: original code is ata_sff_drain_fifo */ +static void ep93xx_pata_drain_fifo(struct ata_queued_cmd *qc) +{ + int count; + struct ata_port *ap; + struct ep93xx_pata_data *drv_data; + + /* We only need to flush incoming data when a command was running */ + if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE) + return; + + ap = qc->ap; + drv_data = ap->host->private_data; + /* Drain up to 64K of data before we give up this recovery method */ + for (count = 0; (ap->ops->sff_check_status(ap) & ATA_DRQ) + && count < 65536; count += 2) + ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_DATA); + + /* Can become DEBUG later */ + if (count) + ata_port_dbg(ap, "drained %d bytes to clear DRQ.\n", count); + +} + +static int ep93xx_pata_port_start(struct ata_port *ap) +{ + struct ep93xx_pata_data *drv_data = ap->host->private_data; + + /* + * Set timings to safe values at startup (= number of ns from ATA + * specification), we'll switch to properly calculated values later. + */ + drv_data->t = *ata_timing_find_mode(XFER_PIO_0); + return 0; +} + +static struct scsi_host_template ep93xx_pata_sht = { + ATA_BASE_SHT(DRV_NAME), + /* ep93xx dma implementation limit */ + .sg_tablesize = 32, + /* ep93xx dma can't transfer 65536 bytes at once */ + .dma_boundary = 0x7fff, +}; + +static struct ata_port_operations ep93xx_pata_port_ops = { + .inherits = &ata_bmdma_port_ops, + + .qc_prep = ata_noop_qc_prep, + + .softreset = ep93xx_pata_softreset, + .hardreset = ATA_OP_NULL, + + .sff_dev_select = ep93xx_pata_dev_select, + .sff_set_devctl = ep93xx_pata_set_devctl, + .sff_check_status = ep93xx_pata_check_status, + .sff_check_altstatus = ep93xx_pata_check_altstatus, + .sff_tf_load = ep93xx_pata_tf_load, + .sff_tf_read = ep93xx_pata_tf_read, + .sff_exec_command = ep93xx_pata_exec_command, + .sff_data_xfer = ep93xx_pata_data_xfer, + .sff_drain_fifo = ep93xx_pata_drain_fifo, + .sff_irq_clear = ATA_OP_NULL, + + .set_piomode = ep93xx_pata_set_piomode, + + .bmdma_setup = ep93xx_pata_dma_setup, + .bmdma_start = ep93xx_pata_dma_start, + .bmdma_stop = ep93xx_pata_dma_stop, + .bmdma_status = ep93xx_pata_dma_status, + + .cable_detect = ata_cable_unknown, + .port_start = ep93xx_pata_port_start, +}; + +static int ep93xx_pata_probe(struct platform_device *pdev) +{ + struct ep93xx_pata_data *drv_data; + struct ata_host *host; + struct ata_port *ap; + int irq; + struct resource *mem_res; + void __iomem *ide_base; + int err; + + err = ep93xx_ide_acquire_gpio(pdev); + if (err) + return err; + + /* INT[3] (IRQ_EP93XX_EXT3) line connected as pull down */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + err = -ENXIO; + goto err_rel_gpio; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ide_base = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(ide_base)) { + err = PTR_ERR(ide_base); + goto err_rel_gpio; + } + + drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) { + err = -ENXIO; + goto err_rel_gpio; + } + + platform_set_drvdata(pdev, drv_data); + drv_data->pdev = pdev; + drv_data->ide_base = ide_base; + drv_data->udma_in_phys = mem_res->start + IDEUDMADATAIN; + drv_data->udma_out_phys = mem_res->start + IDEUDMADATAOUT; + ep93xx_pata_dma_init(drv_data); + + /* allocate host */ + host = ata_host_alloc(&pdev->dev, 1); + if (!host) { + err = -ENXIO; + goto err_rel_dma; + } + + ep93xx_pata_clear_regs(ide_base); + + host->private_data = drv_data; + + ap = host->ports[0]; + ap->dev = &pdev->dev; + ap->ops = &ep93xx_pata_port_ops; + ap->flags |= ATA_FLAG_SLAVE_POSS; + ap->pio_mask = ATA_PIO4; + + /* + * Maximum UDMA modes: + * EP931x rev.E0 - UDMA2 + * EP931x rev.E1 - UDMA3 + * EP931x rev.E2 - UDMA4 + * + * MWDMA support was removed from EP931x rev.E2, + * so this driver supports only UDMA modes. + */ + if (drv_data->dma_rx_channel && drv_data->dma_tx_channel) { + int chip_rev = ep93xx_chip_revision(); + + if (chip_rev == EP93XX_CHIP_REV_E1) + ap->udma_mask = ATA_UDMA3; + else if (chip_rev == EP93XX_CHIP_REV_E2) + ap->udma_mask = ATA_UDMA4; + else + ap->udma_mask = ATA_UDMA2; + } + + /* defaults, pio 0 */ + ep93xx_pata_enable_pio(ide_base, 0); + + dev_info(&pdev->dev, "version " DRV_VERSION "\n"); + + /* activate host */ + err = ata_host_activate(host, irq, ata_bmdma_interrupt, 0, + &ep93xx_pata_sht); + if (err == 0) + return 0; + +err_rel_dma: + ep93xx_pata_release_dma(drv_data); +err_rel_gpio: + ep93xx_ide_release_gpio(pdev); + return err; +} + +static int ep93xx_pata_remove(struct platform_device *pdev) +{ + struct ata_host *host = platform_get_drvdata(pdev); + struct ep93xx_pata_data *drv_data = host->private_data; + + ata_host_detach(host); + ep93xx_pata_release_dma(drv_data); + ep93xx_pata_clear_regs(drv_data->ide_base); + ep93xx_ide_release_gpio(pdev); + return 0; +} + +static struct platform_driver ep93xx_pata_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ep93xx_pata_probe, + .remove = ep93xx_pata_remove, +}; + +module_platform_driver(ep93xx_pata_platform_driver); + +MODULE_AUTHOR("Alessandro Zummo, Lennert Buytenhek, Joao Ramos, " + "Bartlomiej Zolnierkiewicz, Rafal Prylowski"); +MODULE_DESCRIPTION("low-level driver for cirrus ep93xx IDE controller"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("platform:pata_ep93xx"); diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index 42cffd38910..cbc3de793d1 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -387,10 +386,10 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_bmdma_init_one(dev, ppi, &hpt36x_sht, hpriv, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int hpt36x_reinit_one(struct pci_dev *dev) { - struct ata_host *host = dev_get_drvdata(&dev->dev); + struct ata_host *host = pci_get_drvdata(dev); int rc; rc = ata_pci_device_do_resume(dev); @@ -412,27 +411,16 @@ static struct pci_driver hpt36x_pci_driver = { .id_table = hpt36x, .probe = hpt36x_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = hpt36x_reinit_one, #endif }; -static int __init hpt36x_init(void) -{ - return pci_register_driver(&hpt36x_pci_driver); -} - -static void __exit hpt36x_exit(void) -{ - pci_unregister_driver(&hpt36x_pci_driver); -} +module_pci_driver(hpt36x_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Highpoint HPT366/368"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, hpt36x); MODULE_VERSION(DRV_VERSION); - -module_init(hpt36x_init); -module_exit(hpt36x_exit); diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index 9620636aa40..3ba843f5cdc 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -1058,21 +1057,10 @@ static struct pci_driver hpt37x_pci_driver = { .remove = ata_pci_remove_one }; -static int __init hpt37x_init(void) -{ - return pci_register_driver(&hpt37x_pci_driver); -} - -static void __exit hpt37x_exit(void) -{ - pci_unregister_driver(&hpt37x_pci_driver); -} +module_pci_driver(hpt37x_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Highpoint HPT37x/30x"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, hpt37x); MODULE_VERSION(DRV_VERSION); - -module_init(hpt37x_init); -module_exit(hpt37x_exit); diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c index 765f136d8cd..b93c0f0729e 100644 --- a/drivers/ata/pata_hpt3x2n.c +++ b/drivers/ata/pata_hpt3x2n.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -621,21 +620,10 @@ static struct pci_driver hpt3x2n_pci_driver = { .remove = ata_pci_remove_one }; -static int __init hpt3x2n_init(void) -{ - return pci_register_driver(&hpt3x2n_pci_driver); -} - -static void __exit hpt3x2n_exit(void) -{ - pci_unregister_driver(&hpt3x2n_pci_driver); -} +module_pci_driver(hpt3x2n_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Highpoint HPT3xxN"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, hpt3x2n); MODULE_VERSION(DRV_VERSION); - -module_init(hpt3x2n_init); -module_exit(hpt3x2n_exit); diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c index b3042dab08b..d019cdd5bc9 100644 --- a/drivers/ata/pata_hpt3x3.c +++ b/drivers/ata/pata_hpt3x3.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -250,10 +249,10 @@ static int hpt3x3_init_one(struct pci_dev *pdev, const struct pci_device_id *id) IRQF_SHARED, &hpt3x3_sht); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int hpt3x3_reinit_one(struct pci_dev *dev) { - struct ata_host *host = dev_get_drvdata(&dev->dev); + struct ata_host *host = pci_get_drvdata(dev); int rc; rc = ata_pci_device_do_resume(dev); @@ -278,29 +277,16 @@ static struct pci_driver hpt3x3_pci_driver = { .id_table = hpt3x3, .probe = hpt3x3_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = hpt3x3_reinit_one, #endif }; -static int __init hpt3x3_init(void) -{ - return pci_register_driver(&hpt3x3_pci_driver); -} - - -static void __exit hpt3x3_exit(void) -{ - pci_unregister_driver(&hpt3x3_pci_driver); -} - +module_pci_driver(hpt3x3_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Highpoint HPT343/363"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, hpt3x3); MODULE_VERSION(DRV_VERSION); - -module_init(hpt3x3_init); -module_exit(hpt3x3_exit); diff --git a/drivers/ata/pata_icside.c b/drivers/ata/pata_icside.c index 52e7e7b8c74..d7c732042a4 100644 --- a/drivers/ata/pata_icside.c +++ b/drivers/ata/pata_icside.c @@ -337,10 +337,9 @@ static struct ata_port_operations pata_icside_port_ops = { .port_start = ATA_OP_NULL, /* don't need PRD table */ }; -static void __devinit -pata_icside_setup_ioaddr(struct ata_port *ap, void __iomem *base, - struct pata_icside_info *info, - const struct portinfo *port) +static void pata_icside_setup_ioaddr(struct ata_port *ap, void __iomem *base, + struct pata_icside_info *info, + const struct portinfo *port) { struct ata_ioports *ioaddr = &ap->ioaddr; void __iomem *cmd = base + port->dataoffset; @@ -368,7 +367,7 @@ pata_icside_setup_ioaddr(struct ata_port *ap, void __iomem *base, ata_port_desc(ap, "iocbase 0x%lx", info->raw_ioc_base); } -static int __devinit pata_icside_register_v5(struct pata_icside_info *info) +static int pata_icside_register_v5(struct pata_icside_info *info) { struct pata_icside_state *state = info->state; void __iomem *base; @@ -391,7 +390,7 @@ static int __devinit pata_icside_register_v5(struct pata_icside_info *info) return 0; } -static int __devinit pata_icside_register_v6(struct pata_icside_info *info) +static int pata_icside_register_v6(struct pata_icside_info *info) { struct pata_icside_state *state = info->state; struct expansion_card *ec = info->ec; @@ -434,7 +433,7 @@ static int __devinit pata_icside_register_v6(struct pata_icside_info *info) return icside_dma_init(info); } -static int __devinit pata_icside_add_ports(struct pata_icside_info *info) +static int pata_icside_add_ports(struct pata_icside_info *info) { struct expansion_card *ec = info->ec; struct ata_host *host; @@ -474,8 +473,8 @@ static int __devinit pata_icside_add_ports(struct pata_icside_info *info) &pata_icside_sht); } -static int __devinit -pata_icside_probe(struct expansion_card *ec, const struct ecard_id *id) +static int pata_icside_probe(struct expansion_card *ec, + const struct ecard_id *id) { struct pata_icside_state *state; struct pata_icside_info info; @@ -575,7 +574,7 @@ static void pata_icside_shutdown(struct expansion_card *ec) } } -static void __devexit pata_icside_remove(struct expansion_card *ec) +static void pata_icside_remove(struct expansion_card *ec) { struct ata_host *host = ecard_get_drvdata(ec); struct pata_icside_state *state = host->private_data; @@ -602,7 +601,7 @@ static const struct ecard_id pata_icside_ids[] = { static struct ecard_driver pata_icside_driver = { .probe = pata_icside_probe, - .remove = __devexit_p(pata_icside_remove), + .remove = pata_icside_remove, .shutdown = pata_icside_shutdown, .id_table = pata_icside_ids, .drv = { diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c index c5af97f5107..af424573c2f 100644 --- a/drivers/ata/pata_imx.c +++ b/drivers/ata/pata_imx.c @@ -15,7 +15,6 @@ */ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <scsi/scsi_host.h> #include <linux/ata.h> @@ -37,7 +36,7 @@ struct pata_imx_priv { struct clk *clk; /* timings/interrupt/control regs */ - u8 *host_regs; + void __iomem *host_regs; u32 ata_ctl; }; @@ -60,7 +59,7 @@ static int pata_imx_set_mode(struct ata_link *link, struct ata_device **unused) val &= ~PATA_IMX_ATA_CTRL_IORDY_EN; __raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL); - ata_dev_printk(dev, KERN_INFO, "configured for PIO\n"); + ata_dev_info(dev, "configured for PIO\n"); } return 0; } @@ -91,38 +90,39 @@ static void pata_imx_setup_port(struct ata_ioports *ioaddr) ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << 2); } -static int __devinit pata_imx_probe(struct platform_device *pdev) +static int pata_imx_probe(struct platform_device *pdev) { struct ata_host *host; struct ata_port *ap; struct pata_imx_priv *priv; int irq = 0; struct resource *io_res; - - io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (io_res == NULL) - return -EINVAL; + int ret; irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -EINVAL; + if (irq < 0) + return irq; priv = devm_kzalloc(&pdev->dev, sizeof(struct pata_imx_priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->clk = clk_get(&pdev->dev, NULL); + priv->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "Failed to get clock\n"); return PTR_ERR(priv->clk); } - clk_enable(priv->clk); + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; host = ata_host_alloc(&pdev->dev, 1); - if (!host) - goto free_priv; + if (!host) { + ret = -ENOMEM; + goto err; + } host->private_data = priv; ap = host->ports[0]; @@ -131,11 +131,11 @@ static int __devinit pata_imx_probe(struct platform_device *pdev) ap->pio_mask = ATA_PIO0; ap->flags |= ATA_FLAG_SLAVE_POSS; - priv->host_regs = devm_ioremap(&pdev->dev, io_res->start, - resource_size(io_res)); - if (!priv->host_regs) { - dev_err(&pdev->dev, "failed to map IO/CTL base\n"); - goto free_priv; + io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->host_regs = devm_ioremap_resource(&pdev->dev, io_res); + if (IS_ERR(priv->host_regs)) { + ret = PTR_ERR(priv->host_regs); + goto err; } ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA; @@ -158,31 +158,34 @@ static int __devinit pata_imx_probe(struct platform_device *pdev) priv->host_regs + PATA_IMX_ATA_INT_EN); /* activate */ - return ata_host_activate(host, irq, ata_sff_interrupt, 0, + ret = ata_host_activate(host, irq, ata_sff_interrupt, 0, &pata_imx_sht); -free_priv: - clk_disable(priv->clk); - clk_put(priv->clk); - return -ENOMEM; + if (ret) + goto err; + + return 0; +err: + clk_disable_unprepare(priv->clk); + + return ret; } -static int __devexit pata_imx_remove(struct platform_device *pdev) +static int pata_imx_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct pata_imx_priv *priv = host->private_data; ata_host_detach(host); __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN); - clk_disable(priv->clk); - clk_put(priv->clk); + clk_disable_unprepare(priv->clk); return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int pata_imx_suspend(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); @@ -194,7 +197,7 @@ static int pata_imx_suspend(struct device *dev) __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN); priv->ata_ctl = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL); - clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); } return ret; @@ -205,7 +208,9 @@ static int pata_imx_resume(struct device *dev) struct ata_host *host = dev_get_drvdata(dev); struct pata_imx_priv *priv = host->private_data; - clk_enable(priv->clk); + int ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; __raw_writel(priv->ata_ctl, priv->host_regs + PATA_IMX_ATA_CONTROL); @@ -223,13 +228,23 @@ static const struct dev_pm_ops pata_imx_pm_ops = { }; #endif +static const struct of_device_id imx_pata_dt_ids[] = { + { + .compatible = "fsl,imx27-pata", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx_pata_dt_ids); + static struct platform_driver pata_imx_driver = { .probe = pata_imx_probe, - .remove = __devexit_p(pata_imx_remove), + .remove = pata_imx_remove, .driver = { .name = DRV_NAME, + .of_match_table = imx_pata_dt_ids, .owner = THIS_MODULE, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .pm = &pata_imx_pm_ops, #endif }, diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c index 4bceb8803a1..b33d1f99b3a 100644 --- a/drivers/ata/pata_isapnp.c +++ b/drivers/ata/pata_isapnp.c @@ -78,7 +78,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev ap->ioaddr.cmd_addr = cmd_addr; - if (pnp_port_valid(idev, 1) == 0) { + if (pnp_port_valid(idev, 1)) { ctl_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 1), 1); ap->ioaddr.altstatus_addr = ctl_addr; diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c index cf9164d79f1..4f97d1e52f8 100644 --- a/drivers/ata/pata_it8213.c +++ b/drivers/ata/pata_it8213.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -284,24 +283,13 @@ static struct pci_driver it8213_pci_driver = { .id_table = it8213_pci_tbl, .probe = it8213_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init it8213_init(void) -{ - return pci_register_driver(&it8213_pci_driver); -} - -static void __exit it8213_exit(void) -{ - pci_unregister_driver(&it8213_pci_driver); -} - -module_init(it8213_init); -module_exit(it8213_exit); +module_pci_driver(it8213_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for the ITE 8213"); diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index 62c5d00abd2..a5088ecb349 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -72,7 +72,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/slab.h> @@ -936,10 +935,10 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_bmdma_init_one(pdev, ppi, &it821x_sht, NULL, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int it821x_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -966,21 +965,13 @@ static struct pci_driver it821x_pci_driver = { .id_table = it821x, .probe = it821x_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = it821x_reinit_one, #endif }; -static int __init it821x_init(void) -{ - return pci_register_driver(&it821x_pci_driver); -} - -static void __exit it821x_exit(void) -{ - pci_unregister_driver(&it821x_pci_driver); -} +module_pci_driver(it821x_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the IT8211/IT8212 IDE RAID controller"); @@ -988,9 +979,5 @@ MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, it821x); MODULE_VERSION(DRV_VERSION); - module_param_named(noraid, it8212_noraid, int, S_IRUGO); MODULE_PARM_DESC(noraid, "Force card into bypass mode"); - -module_init(it821x_init); -module_exit(it821x_exit); diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index badb1789a91..ddf470c2341 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -48,7 +48,7 @@ static unsigned int ixp4xx_mmio_data_xfer(struct ata_device *dev, u16 *buf16 = (u16 *) buf; struct ata_port *ap = dev->link->ap; void __iomem *mmio = ap->ioaddr.data_addr; - struct ixp4xx_pata_data *data = ap->host->dev->platform_data; + struct ixp4xx_pata_data *data = dev_get_platdata(ap->host->dev); /* set the expansion bus in 16bit mode and restore * 8 bit mode after the transaction. @@ -137,13 +137,14 @@ static void ixp4xx_setup_port(struct ata_port *ap, ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", raw_cmd, raw_ctl); } -static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) +static int ixp4xx_pata_probe(struct platform_device *pdev) { unsigned int irq; struct resource *cs0, *cs1; struct ata_host *host; struct ata_port *ap; - struct ixp4xx_pata_data *data = pdev->dev.platform_data; + struct ixp4xx_pata_data *data = dev_get_platdata(&pdev->dev); + int ret; cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -157,7 +158,9 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) return -ENOMEM; /* acquire resources and fill host */ - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000); data->cs1 = devm_ioremap(&pdev->dev, cs1->start, 0x1000); @@ -187,22 +190,13 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht); } -static __devexit int ixp4xx_pata_remove(struct platform_device *dev) -{ - struct ata_host *host = platform_get_drvdata(dev); - - ata_host_detach(host); - - return 0; -} - static struct platform_driver ixp4xx_pata_platform_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, .probe = ixp4xx_pata_probe, - .remove = __devexit_p(ixp4xx_pata_remove), + .remove = ata_platform_remove_one, }; module_platform_driver(ixp4xx_pata_platform_driver); diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c index cb3babbb703..4d1a5d2c428 100644 --- a/drivers/ata/pata_jmicron.c +++ b/drivers/ata/pata_jmicron.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -158,24 +157,13 @@ static struct pci_driver jmicron_pci_driver = { .id_table = jmicron_pci_tbl, .probe = jmicron_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init jmicron_init(void) -{ - return pci_register_driver(&jmicron_pci_driver); -} - -static void __exit jmicron_exit(void) -{ - pci_unregister_driver(&jmicron_pci_driver); -} - -module_init(jmicron_init); -module_exit(jmicron_exit); +module_pci_driver(jmicron_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for Jmicron PATA ports"); diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c index 35aca7d1a3e..bce2a8ca467 100644 --- a/drivers/ata/pata_legacy.c +++ b/drivers/ata/pata_legacy.c @@ -401,8 +401,7 @@ static void ht6560b_set_piomode(struct ata_port *ap, struct ata_device *adev) ata_timing_compute(adev, adev->pio_mode, &t, 20000, 1000); active = clamp_val(t.active, 2, 15); - recover = clamp_val(t.recover, 2, 16); - recover &= 0x15; + recover = clamp_val(t.recover, 2, 16) & 0x0F; inb(0x3E6); inb(0x3E6); @@ -543,7 +542,7 @@ static void opti82c46x_set_piomode(struct ata_port *ap, struct ata_device *adev) u8 sysclk; /* Get the clock */ - sysclk = opti_syscfg(0xAC) & 0xC0; /* BIOS set */ + sysclk = (opti_syscfg(0xAC) & 0xC0) >> 6; /* BIOS set */ /* Enter configuration mode */ ioread16(ap->ioaddr.error_addr); @@ -917,7 +916,6 @@ static __init int probe_chip_type(struct legacy_probe *probe) local_irq_restore(flags); return BIOS; } - local_irq_restore(flags); } if (ht6560a & mask) diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index b057e3fa44b..a02f76fdcfc 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -845,8 +845,7 @@ static int pata_macio_slave_config(struct scsi_device *sdev) return 0; } -#ifdef CONFIG_PM - +#ifdef CONFIG_PM_SLEEP static int pata_macio_do_suspend(struct pata_macio_priv *priv, pm_message_t mesg) { int rc; @@ -907,8 +906,7 @@ static int pata_macio_do_resume(struct pata_macio_priv *priv) return 0; } - -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static struct scsi_host_template pata_macio_sht = { ATA_BASE_SHT(DRV_NAME), @@ -935,7 +933,7 @@ static struct ata_port_operations pata_macio_ops = { .sff_irq_clear = pata_macio_irq_clear, }; -static void __devinit pata_macio_invariants(struct pata_macio_priv *priv) +static void pata_macio_invariants(struct pata_macio_priv *priv) { const int *bidp; @@ -976,9 +974,8 @@ static void __devinit pata_macio_invariants(struct pata_macio_priv *priv) priv->aapl_bus_id = 1; } -static void __devinit pata_macio_setup_ios(struct ata_ioports *ioaddr, - void __iomem * base, - void __iomem * dma) +static void pata_macio_setup_ios(struct ata_ioports *ioaddr, + void __iomem * base, void __iomem * dma) { /* cmd_addr is the base of regs for that port */ ioaddr->cmd_addr = base; @@ -999,8 +996,8 @@ static void __devinit pata_macio_setup_ios(struct ata_ioports *ioaddr, ioaddr->bmdma_addr = dma; } -static void __devinit pmac_macio_calc_timing_masks(struct pata_macio_priv *priv, - struct ata_port_info *pinfo) +static void pmac_macio_calc_timing_masks(struct pata_macio_priv *priv, + struct ata_port_info *pinfo) { int i = 0; @@ -1027,11 +1024,11 @@ static void __devinit pmac_macio_calc_timing_masks(struct pata_macio_priv *priv, pinfo->pio_mask, pinfo->mwdma_mask, pinfo->udma_mask); } -static int __devinit pata_macio_common_init(struct pata_macio_priv *priv, - resource_size_t tfregs, - resource_size_t dmaregs, - resource_size_t fcregs, - unsigned long irq) +static int pata_macio_common_init(struct pata_macio_priv *priv, + resource_size_t tfregs, + resource_size_t dmaregs, + resource_size_t fcregs, + unsigned long irq) { struct ata_port_info pinfo; const struct ata_port_info *ppi[] = { &pinfo, NULL }; @@ -1113,8 +1110,8 @@ static int __devinit pata_macio_common_init(struct pata_macio_priv *priv, &pata_macio_sht); } -static int __devinit pata_macio_attach(struct macio_dev *mdev, - const struct of_device_id *match) +static int pata_macio_attach(struct macio_dev *mdev, + const struct of_device_id *match) { struct pata_macio_priv *priv; resource_size_t tfregs, dmaregs = 0; @@ -1190,7 +1187,7 @@ static int __devinit pata_macio_attach(struct macio_dev *mdev, return rc; } -static int __devexit pata_macio_detach(struct macio_dev *mdev) +static int pata_macio_detach(struct macio_dev *mdev) { struct ata_host *host = macio_get_drvdata(mdev); struct pata_macio_priv *priv = host->private_data; @@ -1209,8 +1206,7 @@ static int __devexit pata_macio_detach(struct macio_dev *mdev) return 0; } -#ifdef CONFIG_PM - +#ifdef CONFIG_PM_SLEEP static int pata_macio_suspend(struct macio_dev *mdev, pm_message_t mesg) { struct ata_host *host = macio_get_drvdata(mdev); @@ -1224,8 +1220,7 @@ static int pata_macio_resume(struct macio_dev *mdev) return pata_macio_do_resume(host->private_data); } - -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PMAC_MEDIABAY static void pata_macio_mb_event(struct macio_dev* mdev, int mb_state) @@ -1257,8 +1252,8 @@ static void pata_macio_mb_event(struct macio_dev* mdev, int mb_state) #endif /* CONFIG_PMAC_MEDIABAY */ -static int __devinit pata_macio_pci_attach(struct pci_dev *pdev, - const struct pci_device_id *id) +static int pata_macio_pci_attach(struct pci_dev *pdev, + const struct pci_device_id *id) { struct pata_macio_priv *priv; struct device_node *np; @@ -1310,30 +1305,28 @@ static int __devinit pata_macio_pci_attach(struct pci_dev *pdev, return 0; } -static void __devexit pata_macio_pci_detach(struct pci_dev *pdev) +static void pata_macio_pci_detach(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); ata_host_detach(host); } -#ifdef CONFIG_PM - +#ifdef CONFIG_PM_SLEEP static int pata_macio_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); return pata_macio_do_suspend(host->private_data, mesg); } static int pata_macio_pci_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); return pata_macio_do_resume(host->private_data); } - -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static struct of_device_id pata_macio_match[] = { @@ -1361,7 +1354,7 @@ static struct macio_driver pata_macio_driver = }, .probe = pata_macio_attach, .remove = pata_macio_detach, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = pata_macio_suspend, .resume = pata_macio_resume, #endif @@ -1384,7 +1377,7 @@ static struct pci_driver pata_macio_pci_driver = { .id_table = pata_macio_pci_match, .probe = pata_macio_pci_attach, .remove = pata_macio_pci_detach, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = pata_macio_pci_suspend, .resume = pata_macio_pci_resume, #endif diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c index 5d7f58a7e34..ae9feb1ba8f 100644 --- a/drivers/ata/pata_marvell.c +++ b/drivers/ata/pata_marvell.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -172,28 +171,16 @@ static struct pci_driver marvell_pci_driver = { .id_table = marvell_pci_tbl, .probe = marvell_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init marvell_init(void) -{ - return pci_register_driver(&marvell_pci_driver); -} - -static void __exit marvell_exit(void) -{ - pci_unregister_driver(&marvell_pci_driver); -} - -module_init(marvell_init); -module_exit(marvell_exit); +module_pci_driver(marvell_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for Marvell ATA in legacy mode"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, marvell_pci_tbl); MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 00748ae1a01..ccd1c83a05c 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -26,9 +26,9 @@ #include <asm/prom.h> #include <asm/mpc52xx.h> -#include <sysdev/bestcomm/bestcomm.h> -#include <sysdev/bestcomm/bestcomm_priv.h> -#include <sysdev/bestcomm/ata.h> +#include <linux/fsl/bestcomm/bestcomm.h> +#include <linux/fsl/bestcomm/bestcomm_priv.h> +#include <linux/fsl/bestcomm/ata.h> #define DRV_NAME "mpc52xx_ata" @@ -621,9 +621,10 @@ static struct ata_port_operations mpc52xx_ata_port_ops = { .qc_prep = ata_noop_qc_prep, }; -static int __devinit -mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv, - unsigned long raw_ata_regs, int mwdma_mask, int udma_mask) +static int mpc52xx_ata_init_one(struct device *dev, + struct mpc52xx_ata_priv *priv, + unsigned long raw_ata_regs, + int mwdma_mask, int udma_mask) { struct ata_host *host; struct ata_port *ap; @@ -663,35 +664,22 @@ mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv, &mpc52xx_ata_sht); } -static struct mpc52xx_ata_priv * -mpc52xx_ata_remove_one(struct device *dev) -{ - struct ata_host *host = dev_get_drvdata(dev); - struct mpc52xx_ata_priv *priv = host->private_data; - - ata_host_detach(host); - - return priv; -} - - /* ======================================================================== */ /* OF Platform driver */ /* ======================================================================== */ -static int __devinit -mpc52xx_ata_probe(struct platform_device *op) +static int mpc52xx_ata_probe(struct platform_device *op) { unsigned int ipb_freq; struct resource res_mem; int ata_irq = 0; struct mpc52xx_ata __iomem *ata_regs; struct mpc52xx_ata_priv *priv = NULL; - int rv, ret, task_irq = 0; + int rv, task_irq; int mwdma_mask = 0, udma_mask = 0; const __be32 *prop; int proplen; - struct bcom_task *dmatsk = NULL; + struct bcom_task *dmatsk; /* Get ipb frequency */ ipb_freq = mpc5xxx_get_bus_frequency(op->dev.of_node); @@ -717,8 +705,7 @@ mpc52xx_ata_probe(struct platform_device *op) ata_regs = devm_ioremap(&op->dev, res_mem.start, sizeof(*ata_regs)); if (!ata_regs) { dev_err(&op->dev, "error mapping device registers\n"); - rv = -ENOMEM; - goto err; + return -ENOMEM; } /* @@ -753,7 +740,7 @@ mpc52xx_ata_probe(struct platform_device *op) if (!priv) { dev_err(&op->dev, "error allocating private structure\n"); rv = -ENOMEM; - goto err; + goto err1; } priv->ipb_period = 1000000000 / (ipb_freq / 1000); @@ -776,15 +763,15 @@ mpc52xx_ata_probe(struct platform_device *op) if (!dmatsk) { dev_err(&op->dev, "bestcomm initialization failed\n"); rv = -ENOMEM; - goto err; + goto err1; } task_irq = bcom_get_task_irq(dmatsk); - ret = request_irq(task_irq, &mpc52xx_ata_task_irq, 0, + rv = devm_request_irq(&op->dev, task_irq, &mpc52xx_ata_task_irq, 0, "ATA task", priv); - if (ret) { + if (rv) { dev_err(&op->dev, "error requesting DMA IRQ\n"); - goto err; + goto err2; } priv->dmatsk = dmatsk; @@ -792,7 +779,7 @@ mpc52xx_ata_probe(struct platform_device *op) rv = mpc52xx_ata_hw_init(priv); if (rv) { dev_err(&op->dev, "error initializing hardware\n"); - goto err; + goto err2; } /* Register ourselves to libata */ @@ -800,34 +787,28 @@ mpc52xx_ata_probe(struct platform_device *op) mwdma_mask, udma_mask); if (rv) { dev_err(&op->dev, "error registering with ATA layer\n"); - goto err; + goto err2; } return 0; - err: - devm_release_mem_region(&op->dev, res_mem.start, sizeof(*ata_regs)); - if (ata_irq) - irq_dispose_mapping(ata_irq); - if (task_irq) - irq_dispose_mapping(task_irq); - if (dmatsk) - bcom_ata_release(dmatsk); - if (ata_regs) - devm_iounmap(&op->dev, ata_regs); - if (priv) - devm_kfree(&op->dev, priv); + err2: + irq_dispose_mapping(task_irq); + bcom_ata_release(dmatsk); + err1: + irq_dispose_mapping(ata_irq); return rv; } static int mpc52xx_ata_remove(struct platform_device *op) { - struct mpc52xx_ata_priv *priv; + struct ata_host *host = platform_get_drvdata(op); + struct mpc52xx_ata_priv *priv = host->private_data; int task_irq; /* Deregister the ATA interface */ - priv = mpc52xx_ata_remove_one(&op->dev); + ata_platform_remove_one(op); /* Clean up DMA */ task_irq = bcom_get_task_irq(priv->dmatsk); @@ -835,22 +816,14 @@ mpc52xx_ata_remove(struct platform_device *op) bcom_ata_release(priv->dmatsk); irq_dispose_mapping(priv->ata_irq); - /* Clear up IO allocations */ - devm_iounmap(&op->dev, priv->ata_regs); - devm_release_mem_region(&op->dev, priv->ata_regs_pa, - sizeof(*priv->ata_regs)); - devm_kfree(&op->dev, priv); - return 0; } - -#ifdef CONFIG_PM - +#ifdef CONFIG_PM_SLEEP static int mpc52xx_ata_suspend(struct platform_device *op, pm_message_t state) { - struct ata_host *host = dev_get_drvdata(&op->dev); + struct ata_host *host = platform_get_drvdata(op); return ata_host_suspend(host, state); } @@ -858,7 +831,7 @@ mpc52xx_ata_suspend(struct platform_device *op, pm_message_t state) static int mpc52xx_ata_resume(struct platform_device *op) { - struct ata_host *host = dev_get_drvdata(&op->dev); + struct ata_host *host = platform_get_drvdata(op); struct mpc52xx_ata_priv *priv = host->private_data; int rv; @@ -872,10 +845,8 @@ mpc52xx_ata_resume(struct platform_device *op) return 0; } - #endif - static struct of_device_id mpc52xx_ata_of_match[] = { { .compatible = "fsl,mpc5200-ata", }, { .compatible = "mpc5200-ata", }, @@ -886,7 +857,7 @@ static struct of_device_id mpc52xx_ata_of_match[] = { static struct platform_driver mpc52xx_ata_of_platform_driver = { .probe = mpc52xx_ata_probe, .remove = mpc52xx_ata_remove, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = mpc52xx_ata_suspend, .resume = mpc52xx_ata_resume, #endif diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c index 9dc16df8419..202b4d60139 100644 --- a/drivers/ata/pata_mpiix.c +++ b/drivers/ata/pata_mpiix.c @@ -28,7 +28,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -224,27 +223,16 @@ static struct pci_driver mpiix_pci_driver = { .id_table = mpiix, .probe = mpiix_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init mpiix_init(void) -{ - return pci_register_driver(&mpiix_pci_driver); -} - -static void __exit mpiix_exit(void) -{ - pci_unregister_driver(&mpiix_pci_driver); -} +module_pci_driver(mpiix_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Intel MPIIX"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, mpiix); MODULE_VERSION(DRV_VERSION); - -module_init(mpiix_init); -module_exit(mpiix_exit); diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c index 9979a43bc59..0ea18331d46 100644 --- a/drivers/ata/pata_netcell.c +++ b/drivers/ata/pata_netcell.c @@ -7,7 +7,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -93,28 +92,16 @@ static struct pci_driver netcell_pci_driver = { .id_table = netcell_pci_tbl, .probe = netcell_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init netcell_init(void) -{ - return pci_register_driver(&netcell_pci_driver); -} - -static void __exit netcell_exit(void) -{ - pci_unregister_driver(&netcell_pci_driver); -} - -module_init(netcell_init); -module_exit(netcell_exit); +module_pci_driver(netcell_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for Netcell PATA RAID"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, netcell_pci_tbl); MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/ata/pata_ninja32.c b/drivers/ata/pata_ninja32.c index e277a142138..efb272da856 100644 --- a/drivers/ata/pata_ninja32.c +++ b/drivers/ata/pata_ninja32.c @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -153,11 +152,10 @@ static int ninja32_init_one(struct pci_dev *dev, const struct pci_device_id *id) IRQF_SHARED, &ninja32_sht); } -#ifdef CONFIG_PM - +#ifdef CONFIG_PM_SLEEP static int ninja32_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -184,27 +182,16 @@ static struct pci_driver ninja32_pci_driver = { .id_table = ninja32, .probe = ninja32_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ninja32_reinit_one, #endif }; -static int __init ninja32_init(void) -{ - return pci_register_driver(&ninja32_pci_driver); -} - -static void __exit ninja32_exit(void) -{ - pci_unregister_driver(&ninja32_pci_driver); -} +module_pci_driver(ninja32_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Ninja32 ATA"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, ninja32); MODULE_VERSION(DRV_VERSION); - -module_init(ninja32_init); -module_exit(ninja32_exit); diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c index 31d5986537a..200e1eb23a2 100644 --- a/drivers/ata/pata_ns87410.c +++ b/drivers/ata/pata_ns87410.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -162,27 +161,16 @@ static struct pci_driver ns87410_pci_driver = { .id_table = ns87410, .probe = ns87410_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init ns87410_init(void) -{ - return pci_register_driver(&ns87410_pci_driver); -} - -static void __exit ns87410_exit(void) -{ - pci_unregister_driver(&ns87410_pci_driver); -} +module_pci_driver(ns87410_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Nat Semi 87410"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, ns87410); MODULE_VERSION(DRV_VERSION); - -module_init(ns87410_init); -module_exit(ns87410_exit); diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c index f1d517bc5b4..84c6b225b56 100644 --- a/drivers/ata/pata_ns87415.c +++ b/drivers/ata/pata_ns87415.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -386,10 +385,10 @@ static const struct pci_device_id ns87415_pci_tbl[] = { { } /* terminate list */ }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ns87415_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -408,24 +407,13 @@ static struct pci_driver ns87415_pci_driver = { .id_table = ns87415_pci_tbl, .probe = ns87415_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ns87415_reinit_one, #endif }; -static int __init ns87415_init(void) -{ - return pci_register_driver(&ns87415_pci_driver); -} - -static void __exit ns87415_exit(void) -{ - pci_unregister_driver(&ns87415_pci_driver); -} - -module_init(ns87415_init); -module_exit(ns87415_exit); +module_pci_driver(ns87415_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("ATA low-level driver for NS87415 controllers"); diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c index 1d61d5d278f..2a97d3a531e 100644 --- a/drivers/ata/pata_octeon_cf.c +++ b/drivers/ata/pata_octeon_cf.c @@ -5,19 +5,22 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2005 - 2009 Cavium Networks + * Copyright (C) 2005 - 2012 Cavium Inc. * Copyright (C) 2008 Wind River Systems */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/libata.h> -#include <linux/irq.h> +#include <linux/hrtimer.h> #include <linux/slab.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> -#include <linux/workqueue.h> #include <scsi/scsi_host.h> +#include <asm/byteorder.h> #include <asm/octeon/octeon.h> /* @@ -34,20 +37,36 @@ */ #define DRV_NAME "pata_octeon_cf" -#define DRV_VERSION "2.1" +#define DRV_VERSION "2.2" + +/* Poll interval in nS. */ +#define OCTEON_CF_BUSY_POLL_INTERVAL 500000 +#define DMA_CFG 0 +#define DMA_TIM 0x20 +#define DMA_INT 0x38 +#define DMA_INT_EN 0x50 struct octeon_cf_port { - struct workqueue_struct *wq; - struct delayed_work delayed_finish; + struct hrtimer delayed_finish; struct ata_port *ap; int dma_finished; + void *c0; + unsigned int cs0; + unsigned int cs1; + bool is_true_ide; + u64 dma_base; }; static struct scsi_host_template octeon_cf_sht = { ATA_PIO_SHT(DRV_NAME), }; +static int enable_dma; +module_param(enable_dma, int, 0444); +MODULE_PARM_DESC(enable_dma, + "Enable use of DMA on interfaces that support it (0=no dma [default], 1=use dma)"); + /** * Convert nanosecond based time to setting used in the * boot bus timing register, based on timing multiple @@ -66,12 +85,29 @@ static unsigned int ns_to_tim_reg(unsigned int tim_mult, unsigned int nsecs) return val; } -static void octeon_cf_set_boot_reg_cfg(int cs) +static void octeon_cf_set_boot_reg_cfg(int cs, unsigned int multiplier) { union cvmx_mio_boot_reg_cfgx reg_cfg; + unsigned int tim_mult; + + switch (multiplier) { + case 8: + tim_mult = 3; + break; + case 4: + tim_mult = 0; + break; + case 2: + tim_mult = 2; + break; + default: + tim_mult = 1; + break; + } + reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs)); reg_cfg.s.dmack = 0; /* Don't assert DMACK on access */ - reg_cfg.s.tim_mult = 2; /* Timing mutiplier 2x */ + reg_cfg.s.tim_mult = tim_mult; /* Timing mutiplier */ reg_cfg.s.rd_dly = 0; /* Sample on falling edge of BOOT_OE */ reg_cfg.s.sam = 0; /* Don't combine write and output enable */ reg_cfg.s.we_ext = 0; /* No write enable extension */ @@ -92,12 +128,12 @@ static void octeon_cf_set_boot_reg_cfg(int cs) */ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev) { - struct octeon_cf_data *ocd = ap->dev->platform_data; + struct octeon_cf_port *cf_port = ap->private_data; union cvmx_mio_boot_reg_timx reg_tim; - int cs = ocd->base_region; int T; struct ata_timing timing; + unsigned int div; int use_iordy; int trh; int pause; @@ -106,7 +142,15 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev) int t2; int t2i; - T = (int)(2000000000000LL / octeon_get_clock_rate()); + /* + * A divisor value of four will overflow the timing fields at + * clock rates greater than 800MHz + */ + if (octeon_get_io_clock_rate() <= 800000000) + div = 4; + else + div = 8; + T = (int)((1000000000000LL * div) / octeon_get_io_clock_rate()); if (ata_timing_compute(dev, dev->pio_mode, &timing, T, T)) BUG(); @@ -121,23 +165,26 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev) if (t2i) t2i--; - trh = ns_to_tim_reg(2, 20); + trh = ns_to_tim_reg(div, 20); if (trh) trh--; - pause = timing.cycle - timing.active - timing.setup - trh; + pause = (int)timing.cycle - (int)timing.active - + (int)timing.setup - trh; + if (pause < 0) + pause = 0; if (pause) pause--; - octeon_cf_set_boot_reg_cfg(cs); - if (ocd->dma_engine >= 0) + octeon_cf_set_boot_reg_cfg(cf_port->cs0, div); + if (cf_port->is_true_ide) /* True IDE mode, program both chip selects. */ - octeon_cf_set_boot_reg_cfg(cs + 1); + octeon_cf_set_boot_reg_cfg(cf_port->cs1, div); use_iordy = ata_pio_need_iordy(dev); - reg_tim.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_TIMX(cs)); + reg_tim.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs0)); /* Disable page mode */ reg_tim.s.pagem = 0; /* Enable dynamic timing */ @@ -161,20 +208,22 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev) /* How long read enable is asserted */ reg_tim.s.oe = t2; /* Time after CE that read/write starts */ - reg_tim.s.ce = ns_to_tim_reg(2, 5); + reg_tim.s.ce = ns_to_tim_reg(div, 5); /* Time before CE that address is valid */ reg_tim.s.adr = 0; /* Program the bootbus region timing for the data port chip select. */ - cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cs), reg_tim.u64); - if (ocd->dma_engine >= 0) + cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs0), reg_tim.u64); + if (cf_port->is_true_ide) /* True IDE mode, program both chip selects. */ - cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cs + 1), reg_tim.u64); + cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs1), + reg_tim.u64); } static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev) { - struct octeon_cf_data *ocd = dev->link->ap->dev->platform_data; + struct octeon_cf_port *cf_port = ap->private_data; + union cvmx_mio_boot_pin_defs pin_defs; union cvmx_mio_boot_dma_timx dma_tim; unsigned int oe_a; unsigned int oe_n; @@ -183,6 +232,7 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev) unsigned int pause; unsigned int T0, Tkr, Td; unsigned int tim_mult; + int c; const struct ata_timing *timing; @@ -199,13 +249,19 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev) /* not spec'ed, value in eclocks, not affected by tim_mult */ dma_arq = 8; pause = 25 - dma_arq * 1000 / - (octeon_get_clock_rate() / 1000000); /* Tz */ + (octeon_get_io_clock_rate() / 1000000); /* Tz */ oe_a = Td; /* Tkr from cf spec, lengthened to meet T0 */ oe_n = max(T0 - oe_a, Tkr); - dma_tim.s.dmack_pi = 1; + pin_defs.u64 = cvmx_read_csr(CVMX_MIO_BOOT_PIN_DEFS); + + /* DMA channel number. */ + c = (cf_port->dma_base & 8) >> 3; + + /* Invert the polarity if the default is 0*/ + dma_tim.s.dmack_pi = (pin_defs.u64 & (1ull << (11 + c))) ? 0 : 1; dma_tim.s.oe_n = ns_to_tim_reg(tim_mult, oe_n); dma_tim.s.oe_a = ns_to_tim_reg(tim_mult, oe_a); @@ -228,14 +284,11 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev) pr_debug("ns to ticks (mult %d) of %d is: %d\n", tim_mult, 60, ns_to_tim_reg(tim_mult, 60)); - pr_debug("oe_n: %d, oe_a: %d, dmack_s: %d, dmack_h: " - "%d, dmarq: %d, pause: %d\n", + pr_debug("oe_n: %d, oe_a: %d, dmack_s: %d, dmack_h: %d, dmarq: %d, pause: %d\n", dma_tim.s.oe_n, dma_tim.s.oe_a, dma_tim.s.dmack_s, dma_tim.s.dmack_h, dma_tim.s.dmarq, dma_tim.s.pause); - cvmx_write_csr(CVMX_MIO_BOOT_DMA_TIMX(ocd->dma_engine), - dma_tim.u64); - + cvmx_write_csr(cf_port->dma_base + DMA_TIM, dma_tim.u64); } /** @@ -489,15 +542,10 @@ static void octeon_cf_exec_command16(struct ata_port *ap, ata_wait_idle(ap); } -static void octeon_cf_irq_on(struct ata_port *ap) +static void octeon_cf_ata_port_noaction(struct ata_port *ap) { } -static void octeon_cf_irq_clear(struct ata_port *ap) -{ - return; -} - static void octeon_cf_dma_setup(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; @@ -519,7 +567,7 @@ static void octeon_cf_dma_setup(struct ata_queued_cmd *qc) */ static void octeon_cf_dma_start(struct ata_queued_cmd *qc) { - struct octeon_cf_data *ocd = qc->ap->dev->platform_data; + struct octeon_cf_port *cf_port = qc->ap->private_data; union cvmx_mio_boot_dma_cfgx mio_boot_dma_cfg; union cvmx_mio_boot_dma_intx mio_boot_dma_int; struct scatterlist *sg; @@ -535,15 +583,16 @@ static void octeon_cf_dma_start(struct ata_queued_cmd *qc) */ mio_boot_dma_int.u64 = 0; mio_boot_dma_int.s.done = 1; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine), - mio_boot_dma_int.u64); + cvmx_write_csr(cf_port->dma_base + DMA_INT, mio_boot_dma_int.u64); /* Enable the interrupt. */ - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INT_ENX(ocd->dma_engine), - mio_boot_dma_int.u64); + cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, mio_boot_dma_int.u64); /* Set the direction of the DMA */ mio_boot_dma_cfg.u64 = 0; +#ifdef __LITTLE_ENDIAN + mio_boot_dma_cfg.s.endian = 1; +#endif mio_boot_dma_cfg.s.en = 1; mio_boot_dma_cfg.s.rw = ((qc->tf.flags & ATA_TFLAG_WRITE) != 0); @@ -569,8 +618,7 @@ static void octeon_cf_dma_start(struct ata_queued_cmd *qc) (mio_boot_dma_cfg.s.rw) ? "write" : "read", sg->length, (void *)(unsigned long)mio_boot_dma_cfg.s.adr); - cvmx_write_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine), - mio_boot_dma_cfg.u64); + cvmx_write_csr(cf_port->dma_base + DMA_CFG, mio_boot_dma_cfg.u64); } /** @@ -583,10 +631,9 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap, struct ata_queued_cmd *qc) { struct ata_eh_info *ehi = &ap->link.eh_info; - struct octeon_cf_data *ocd = ap->dev->platform_data; + struct octeon_cf_port *cf_port = ap->private_data; union cvmx_mio_boot_dma_cfgx dma_cfg; union cvmx_mio_boot_dma_intx dma_int; - struct octeon_cf_port *cf_port; u8 status; VPRINTK("ata%u: protocol %d task_state %d\n", @@ -596,9 +643,7 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap, if (ap->hsm_task_state != HSM_ST_LAST) return 0; - cf_port = ap->private_data; - - dma_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine)); + dma_cfg.u64 = cvmx_read_csr(cf_port->dma_base + DMA_CFG); if (dma_cfg.s.size != 0xfffff) { /* Error, the transfer was not complete. */ qc->err_mask |= AC_ERR_HOST_BUS; @@ -608,15 +653,15 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap, /* Stop and clear the dma engine. */ dma_cfg.u64 = 0; dma_cfg.s.size = -1; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine), dma_cfg.u64); + cvmx_write_csr(cf_port->dma_base + DMA_CFG, dma_cfg.u64); /* Disable the interrupt. */ dma_int.u64 = 0; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INT_ENX(ocd->dma_engine), dma_int.u64); + cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, dma_int.u64); /* Clear the DMA complete status */ dma_int.s.done = 1; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine), dma_int.u64); + cvmx_write_csr(cf_port->dma_base + DMA_INT, dma_int.u64); status = ap->ops->sff_check_status(ap); @@ -649,69 +694,68 @@ static irqreturn_t octeon_cf_interrupt(int irq, void *dev_instance) struct ata_queued_cmd *qc; union cvmx_mio_boot_dma_intx dma_int; union cvmx_mio_boot_dma_cfgx dma_cfg; - struct octeon_cf_data *ocd; ap = host->ports[i]; - ocd = ap->dev->platform_data; cf_port = ap->private_data; - dma_int.u64 = - cvmx_read_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine)); - dma_cfg.u64 = - cvmx_read_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine)); + + dma_int.u64 = cvmx_read_csr(cf_port->dma_base + DMA_INT); + dma_cfg.u64 = cvmx_read_csr(cf_port->dma_base + DMA_CFG); qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) { - if (dma_int.s.done && !dma_cfg.s.en) { - if (!sg_is_last(qc->cursg)) { - qc->cursg = sg_next(qc->cursg); - handled = 1; - octeon_cf_dma_start(qc); - continue; - } else { - cf_port->dma_finished = 1; - } - } - if (!cf_port->dma_finished) - continue; - status = ioread8(ap->ioaddr.altstatus_addr); - if (status & (ATA_BUSY | ATA_DRQ)) { - /* - * We are busy, try to handle it - * later. This is the DMA finished - * interrupt, and it could take a - * little while for the card to be - * ready for more commands. - */ - /* Clear DMA irq. */ - dma_int.u64 = 0; - dma_int.s.done = 1; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine), - dma_int.u64); - - queue_delayed_work(cf_port->wq, - &cf_port->delayed_finish, 1); + if (!qc || (qc->tf.flags & ATA_TFLAG_POLLING)) + continue; + + if (dma_int.s.done && !dma_cfg.s.en) { + if (!sg_is_last(qc->cursg)) { + qc->cursg = sg_next(qc->cursg); handled = 1; + octeon_cf_dma_start(qc); + continue; } else { - handled |= octeon_cf_dma_finished(ap, qc); + cf_port->dma_finished = 1; } } + if (!cf_port->dma_finished) + continue; + status = ioread8(ap->ioaddr.altstatus_addr); + if (status & (ATA_BUSY | ATA_DRQ)) { + /* + * We are busy, try to handle it later. This + * is the DMA finished interrupt, and it could + * take a little while for the card to be + * ready for more commands. + */ + /* Clear DMA irq. */ + dma_int.u64 = 0; + dma_int.s.done = 1; + cvmx_write_csr(cf_port->dma_base + DMA_INT, + dma_int.u64); + hrtimer_start_range_ns(&cf_port->delayed_finish, + ns_to_ktime(OCTEON_CF_BUSY_POLL_INTERVAL), + OCTEON_CF_BUSY_POLL_INTERVAL / 5, + HRTIMER_MODE_REL); + handled = 1; + } else { + handled |= octeon_cf_dma_finished(ap, qc); + } } spin_unlock_irqrestore(&host->lock, flags); DPRINTK("EXIT\n"); return IRQ_RETVAL(handled); } -static void octeon_cf_delayed_finish(struct work_struct *work) +static enum hrtimer_restart octeon_cf_delayed_finish(struct hrtimer *hrt) { - struct octeon_cf_port *cf_port = container_of(work, + struct octeon_cf_port *cf_port = container_of(hrt, struct octeon_cf_port, - delayed_finish.work); + delayed_finish); struct ata_port *ap = cf_port->ap; struct ata_host *host = ap->host; struct ata_queued_cmd *qc; unsigned long flags; u8 status; + enum hrtimer_restart rv = HRTIMER_NORESTART; spin_lock_irqsave(&host->lock, flags); @@ -726,15 +770,17 @@ static void octeon_cf_delayed_finish(struct work_struct *work) status = ioread8(ap->ioaddr.altstatus_addr); if (status & (ATA_BUSY | ATA_DRQ)) { /* Still busy, try again. */ - queue_delayed_work(cf_port->wq, - &cf_port->delayed_finish, 1); + hrtimer_forward_now(hrt, + ns_to_ktime(OCTEON_CF_BUSY_POLL_INTERVAL)); + rv = HRTIMER_RESTART; goto out; } qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) + if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) octeon_cf_dma_finished(ap, qc); out: spin_unlock_irqrestore(&host->lock, flags); + return rv; } static void octeon_cf_dev_config(struct ata_device *dev) @@ -786,81 +832,138 @@ static struct ata_port_operations octeon_cf_ops = { .qc_prep = ata_noop_qc_prep, .qc_issue = octeon_cf_qc_issue, .sff_dev_select = octeon_cf_dev_select, - .sff_irq_on = octeon_cf_irq_on, - .sff_irq_clear = octeon_cf_irq_clear, + .sff_irq_on = octeon_cf_ata_port_noaction, + .sff_irq_clear = octeon_cf_ata_port_noaction, .cable_detect = ata_cable_40wire, .set_piomode = octeon_cf_set_piomode, .set_dmamode = octeon_cf_set_dmamode, .dev_config = octeon_cf_dev_config, }; -static int __devinit octeon_cf_probe(struct platform_device *pdev) +static int octeon_cf_probe(struct platform_device *pdev) { struct resource *res_cs0, *res_cs1; + bool is_16bit; + const __be32 *cs_num; + struct property *reg_prop; + int n_addr, n_size, reg_len; + struct device_node *node; + const void *prop; void __iomem *cs0; void __iomem *cs1 = NULL; struct ata_host *host; struct ata_port *ap; - struct octeon_cf_data *ocd; int irq = 0; irq_handler_t irq_handler = NULL; void __iomem *base; struct octeon_cf_port *cf_port; - char version[32]; + int rv = -ENOMEM; - res_cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res_cs0) + node = pdev->dev.of_node; + if (node == NULL) return -EINVAL; - ocd = pdev->dev.platform_data; + cf_port = devm_kzalloc(&pdev->dev, sizeof(*cf_port), GFP_KERNEL); + if (!cf_port) + return -ENOMEM; + + cf_port->is_true_ide = (of_find_property(node, "cavium,true-ide", NULL) != NULL); - cs0 = devm_ioremap_nocache(&pdev->dev, res_cs0->start, - resource_size(res_cs0)); + prop = of_get_property(node, "cavium,bus-width", NULL); + if (prop) + is_16bit = (be32_to_cpup(prop) == 16); + else + is_16bit = false; - if (!cs0) - return -ENOMEM; + n_addr = of_n_addr_cells(node); + n_size = of_n_size_cells(node); + + reg_prop = of_find_property(node, "reg", ®_len); + if (!reg_prop || reg_len < sizeof(__be32)) + return -EINVAL; + + cs_num = reg_prop->value; + cf_port->cs0 = be32_to_cpup(cs_num); + + if (cf_port->is_true_ide) { + struct device_node *dma_node; + dma_node = of_parse_phandle(node, + "cavium,dma-engine-handle", 0); + if (dma_node) { + struct platform_device *dma_dev; + dma_dev = of_find_device_by_node(dma_node); + if (dma_dev) { + struct resource *res_dma; + int i; + res_dma = platform_get_resource(dma_dev, IORESOURCE_MEM, 0); + if (!res_dma) { + of_node_put(dma_node); + return -EINVAL; + } + cf_port->dma_base = (u64)devm_ioremap_nocache(&pdev->dev, res_dma->start, + resource_size(res_dma)); + if (!cf_port->dma_base) { + of_node_put(dma_node); + return -EINVAL; + } - /* Determine from availability of DMA if True IDE mode or not */ - if (ocd->dma_engine >= 0) { + irq_handler = octeon_cf_interrupt; + i = platform_get_irq(dma_dev, 0); + if (i > 0) + irq = i; + } + of_node_put(dma_node); + } res_cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res_cs1) return -EINVAL; cs1 = devm_ioremap_nocache(&pdev->dev, res_cs1->start, resource_size(res_cs1)); - if (!cs1) - return -ENOMEM; + return rv; + + if (reg_len < (n_addr + n_size + 1) * sizeof(__be32)) + return -EINVAL; + + cs_num += n_addr + n_size; + cf_port->cs1 = be32_to_cpup(cs_num); } - cf_port = kzalloc(sizeof(*cf_port), GFP_KERNEL); - if (!cf_port) - return -ENOMEM; + res_cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_cs0) + return -EINVAL; + + cs0 = devm_ioremap_nocache(&pdev->dev, res_cs0->start, + resource_size(res_cs0)); + if (!cs0) + return rv; /* allocate host */ host = ata_host_alloc(&pdev->dev, 1); if (!host) - goto free_cf_port; + return rv; ap = host->ports[0]; ap->private_data = cf_port; + pdev->dev.platform_data = cf_port; cf_port->ap = ap; ap->ops = &octeon_cf_ops; ap->pio_mask = ATA_PIO6; ap->flags |= ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING; - base = cs0 + ocd->base_region_bias; - if (!ocd->is16bit) { + if (!is_16bit) { + base = cs0 + 0x800; ap->ioaddr.cmd_addr = base; ata_sff_std_ports(&ap->ioaddr); ap->ioaddr.altstatus_addr = base + 0xe; ap->ioaddr.ctl_addr = base + 0xe; octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer8; - } else if (cs1) { - /* Presence of cs1 indicates True IDE mode. */ + } else if (cf_port->is_true_ide) { + base = cs0; ap->ioaddr.cmd_addr = base + (ATA_REG_CMD << 1) + 1; ap->ioaddr.data_addr = base + (ATA_REG_DATA << 1); ap->ioaddr.error_addr = base + (ATA_REG_ERR << 1) + 1; @@ -876,19 +979,15 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev) ap->ioaddr.ctl_addr = cs1 + (6 << 1) + 1; octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer16; - ap->mwdma_mask = ATA_MWDMA4; - irq = platform_get_irq(pdev, 0); - irq_handler = octeon_cf_interrupt; - - /* True IDE mode needs delayed work to poll for not-busy. */ - cf_port->wq = create_singlethread_workqueue(DRV_NAME); - if (!cf_port->wq) - goto free_cf_port; - INIT_DELAYED_WORK(&cf_port->delayed_finish, - octeon_cf_delayed_finish); + ap->mwdma_mask = enable_dma ? ATA_MWDMA4 : 0; + /* True IDE mode needs a timer to poll for not-busy. */ + hrtimer_init(&cf_port->delayed_finish, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + cf_port->delayed_finish.function = octeon_cf_delayed_finish; } else { /* 16 bit but not True IDE */ + base = cs0 + 0x800; octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer16; octeon_cf_ops.softreset = octeon_cf_softreset16; octeon_cf_ops.sff_check_status = octeon_cf_check_status16; @@ -902,28 +1001,67 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev) ap->ioaddr.ctl_addr = base + 0xe; ap->ioaddr.altstatus_addr = base + 0xe; } + cf_port->c0 = ap->ioaddr.ctl_addr; + + rv = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (rv) + return rv; ata_port_desc(ap, "cmd %p ctl %p", base, ap->ioaddr.ctl_addr); + dev_info(&pdev->dev, "version " DRV_VERSION" %d bit%s.\n", + is_16bit ? 16 : 8, + cf_port->is_true_ide ? ", True IDE" : ""); - snprintf(version, sizeof(version), "%s %d bit%s", - DRV_VERSION, - (ocd->is16bit) ? 16 : 8, - (cs1) ? ", True IDE" : ""); - ata_print_version_once(&pdev->dev, version); + return ata_host_activate(host, irq, irq_handler, + IRQF_SHARED, &octeon_cf_sht); +} - return ata_host_activate(host, irq, irq_handler, 0, &octeon_cf_sht); +static void octeon_cf_shutdown(struct device *dev) +{ + union cvmx_mio_boot_dma_cfgx dma_cfg; + union cvmx_mio_boot_dma_intx dma_int; + + struct octeon_cf_port *cf_port = dev_get_platdata(dev); + + if (cf_port->dma_base) { + /* Stop and clear the dma engine. */ + dma_cfg.u64 = 0; + dma_cfg.s.size = -1; + cvmx_write_csr(cf_port->dma_base + DMA_CFG, dma_cfg.u64); + + /* Disable the interrupt. */ + dma_int.u64 = 0; + cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, dma_int.u64); + + /* Clear the DMA complete status */ + dma_int.s.done = 1; + cvmx_write_csr(cf_port->dma_base + DMA_INT, dma_int.u64); -free_cf_port: - kfree(cf_port); - return -ENOMEM; + __raw_writeb(0, cf_port->c0); + udelay(20); + __raw_writeb(ATA_SRST, cf_port->c0); + udelay(20); + __raw_writeb(0, cf_port->c0); + mdelay(100); + } } +static struct of_device_id octeon_cf_match[] = { + { + .compatible = "cavium,ebt3000-compact-flash", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_i2c_match); + static struct platform_driver octeon_cf_driver = { .probe = octeon_cf_probe, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = octeon_cf_match, + .shutdown = octeon_cf_shutdown }, }; diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c index 1654dc27e7f..a7e95a54c78 100644 --- a/drivers/ata/pata_of_platform.c +++ b/drivers/ata/pata_of_platform.c @@ -14,8 +14,9 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/ata_platform.h> +#include <linux/libata.h> -static int __devinit pata_of_platform_probe(struct platform_device *ofdev) +static int pata_of_platform_probe(struct platform_device *ofdev) { int ret; struct device_node *dn = ofdev->dev.of_node; @@ -76,11 +77,6 @@ static int __devinit pata_of_platform_probe(struct platform_device *ofdev) reg_shift, pio_mask); } -static int __devexit pata_of_platform_remove(struct platform_device *ofdev) -{ - return __pata_platform_remove(&ofdev->dev); -} - static struct of_device_id pata_of_platform_match[] = { { .compatible = "ata-generic", }, { .compatible = "electra-ide", }, @@ -95,7 +91,7 @@ static struct platform_driver pata_of_platform_driver = { .of_match_table = pata_of_platform_match, }, .probe = pata_of_platform_probe, - .remove = __devexit_p(pata_of_platform_remove), + .remove = ata_platform_remove_one, }; module_platform_driver(pata_of_platform_driver); diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index 98cdf50e406..b9bf78b7d48 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -259,28 +258,16 @@ static struct pci_driver oldpiix_pci_driver = { .id_table = oldpiix_pci_tbl, .probe = oldpiix_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init oldpiix_init(void) -{ - return pci_register_driver(&oldpiix_pci_driver); -} - -static void __exit oldpiix_exit(void) -{ - pci_unregister_driver(&oldpiix_pci_driver); -} - -module_init(oldpiix_init); -module_exit(oldpiix_exit); +module_pci_driver(oldpiix_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for early PIIX series controllers"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, oldpiix_pci_tbl); MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c index accc033faf7..3a944a02926 100644 --- a/drivers/ata/pata_opti.c +++ b/drivers/ata/pata_opti.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -185,28 +184,16 @@ static struct pci_driver opti_pci_driver = { .id_table = opti, .probe = opti_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init opti_init(void) -{ - return pci_register_driver(&opti_pci_driver); -} - -static void __exit opti_exit(void) -{ - pci_unregister_driver(&opti_pci_driver); -} - +module_pci_driver(opti_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Opti 621/621X"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, opti); MODULE_VERSION(DRV_VERSION); - -module_init(opti_init); -module_exit(opti_exit); diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c index 77cb9140863..bdec7efa464 100644 --- a/drivers/ata/pata_optidma.c +++ b/drivers/ata/pata_optidma.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -441,27 +440,16 @@ static struct pci_driver optidma_pci_driver = { .id_table = optidma, .probe = optidma_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init optidma_init(void) -{ - return pci_register_driver(&optidma_pci_driver); -} - -static void __exit optidma_exit(void) -{ - pci_unregister_driver(&optidma_pci_driver); -} +module_pci_driver(optidma_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Opti Firestar/Firestar Plus"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, optidma); MODULE_VERSION(DRV_VERSION); - -module_init(optidma_init); -module_exit(optidma_exit); diff --git a/drivers/ata/pata_palmld.c b/drivers/ata/pata_palmld.c index 5ff31b68135..df2bb7504fc 100644 --- a/drivers/ata/pata_palmld.c +++ b/drivers/ata/pata_palmld.c @@ -48,7 +48,7 @@ static struct ata_port_operations palmld_port_ops = { .cable_detect = ata_cable_40wire, }; -static __devinit int palmld_pata_probe(struct platform_device *pdev) +static int palmld_pata_probe(struct platform_device *pdev) { struct ata_host *host; struct ata_port *ap; @@ -109,11 +109,9 @@ err1: return ret; } -static __devexit int palmld_pata_remove(struct platform_device *dev) +static int palmld_pata_remove(struct platform_device *dev) { - struct ata_host *host = platform_get_drvdata(dev); - - ata_host_detach(host); + ata_platform_remove_one(dev); /* power down the HDD */ gpio_set_value(GPIO_NR_PALMLD_IDE_PWEN, 0); @@ -129,7 +127,7 @@ static struct platform_driver palmld_pata_platform_driver = { .owner = THIS_MODULE, }, .probe = palmld_pata_probe, - .remove = __devexit_p(palmld_pata_remove), + .remove = palmld_pata_remove, }; module_platform_driver(palmld_pata_platform_driver); diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index a808ba03bd7..bcc4b968c04 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/slab.h> @@ -170,7 +169,8 @@ static int pcmcia_check_one_config(struct pcmcia_device *pdev, void *priv_data) { int *is_kme = priv_data; - if (!(pdev->resource[0]->flags & IO_DATA_PATH_WIDTH_8)) { + if ((pdev->resource[0]->flags & IO_DATA_PATH_WIDTH) + != IO_DATA_PATH_WIDTH_8) { pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; } @@ -386,21 +386,9 @@ static struct pcmcia_driver pcmcia_driver = { .probe = pcmcia_init_one, .remove = pcmcia_remove_one, }; - -static int __init pcmcia_init(void) -{ - return pcmcia_register_driver(&pcmcia_driver); -} - -static void __exit pcmcia_exit(void) -{ - pcmcia_unregister_driver(&pcmcia_driver); -} +module_pcmcia_driver(pcmcia_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for PCMCIA ATA"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(pcmcia_init); -module_exit(pcmcia_exit); diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index 7d63f24179c..4d06a5cda98 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -63,7 +62,9 @@ enum { }; static int pdc2027x_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); +#ifdef CONFIG_PM_SLEEP static int pdc2027x_reinit_one(struct pci_dev *pdev); +#endif static int pdc2027x_prereset(struct ata_link *link, unsigned long deadline); static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev); static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev); @@ -127,7 +128,7 @@ static struct pci_driver pdc2027x_pci_driver = { .id_table = pdc2027x_pci_tbl, .probe = pdc2027x_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = pdc2027x_reinit_one, #endif @@ -700,7 +701,8 @@ static void pdc_ata_setup_port(struct ata_ioports *port, void __iomem *base) * @pdev: instance of pci_dev found * @ent: matching entry in the id_tbl[] */ -static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +static int pdc2027x_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static const unsigned long cmd_offset[] = { 0x17c0, 0x15c0 }; static const unsigned long bmdma_offset[] = { 0x1000, 0x1008 }; @@ -759,10 +761,10 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de IRQF_SHARED, &pdc2027x_sht); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int pdc2027x_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); unsigned int board_idx; int rc; @@ -784,21 +786,4 @@ static int pdc2027x_reinit_one(struct pci_dev *pdev) } #endif -/** - * pdc2027x_init - Called after this module is loaded into the kernel. - */ -static int __init pdc2027x_init(void) -{ - return pci_register_driver(&pdc2027x_pci_driver); -} - -/** - * pdc2027x_exit - Called before this module unloaded from the kernel - */ -static void __exit pdc2027x_exit(void) -{ - pci_unregister_driver(&pdc2027x_pci_driver); -} - -module_init(pdc2027x_init); -module_exit(pdc2027x_exit); +module_pci_driver(pdc2027x_pci_driver); diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c index c2ed5868dda..9001991d283 100644 --- a/drivers/ata/pata_pdc202xx_old.c +++ b/drivers/ata/pata_pdc202xx_old.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -378,27 +377,16 @@ static struct pci_driver pdc202xx_pci_driver = { .id_table = pdc202xx, .probe = pdc202xx_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init pdc202xx_init(void) -{ - return pci_register_driver(&pdc202xx_pci_driver); -} - -static void __exit pdc202xx_exit(void) -{ - pci_unregister_driver(&pdc202xx_pci_driver); -} +module_pci_driver(pdc202xx_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Promise 2024x and 20262-20267"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pdc202xx); MODULE_VERSION(DRV_VERSION); - -module_init(pdc202xx_init); -module_exit(pdc202xx_exit); diff --git a/drivers/ata/pata_piccolo.c b/drivers/ata/pata_piccolo.c index cb01bf9496f..35cb0e26323 100644 --- a/drivers/ata/pata_piccolo.c +++ b/drivers/ata/pata_piccolo.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -111,30 +110,16 @@ static struct pci_driver ata_tosh_pci_driver = { .id_table = ata_tosh, .probe = ata_tosh_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init ata_tosh_init(void) -{ - return pci_register_driver(&ata_tosh_pci_driver); -} - - -static void __exit ata_tosh_exit(void) -{ - pci_unregister_driver(&ata_tosh_pci_driver); -} - +module_pci_driver(ata_tosh_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("Low level driver for Toshiba Piccolo ATA"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, ata_tosh); MODULE_VERSION(DRV_VERSION); - -module_init(ata_tosh_init); -module_exit(ata_tosh_exit); - diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index f1848aeda78..a5579b55e33 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -13,7 +13,6 @@ */ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <scsi/scsi_host.h> #include <linux/ata.h> @@ -98,12 +97,9 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr, * * If no IRQ resource is present, PIO polling mode is used instead. */ -int __devinit __pata_platform_probe(struct device *dev, - struct resource *io_res, - struct resource *ctl_res, - struct resource *irq_res, - unsigned int ioport_shift, - int __pio_mask) +int __pata_platform_probe(struct device *dev, struct resource *io_res, + struct resource *ctl_res, struct resource *irq_res, + unsigned int ioport_shift, int __pio_mask) { struct ata_host *host; struct ata_port *ap; @@ -178,29 +174,12 @@ int __devinit __pata_platform_probe(struct device *dev, } EXPORT_SYMBOL_GPL(__pata_platform_probe); -/** - * __pata_platform_remove - unplug a platform interface - * @dev: device - * - * A platform bus ATA device has been unplugged. Perform the needed - * cleanup. Also called on module unload for any active devices. - */ -int __pata_platform_remove(struct device *dev) -{ - struct ata_host *host = dev_get_drvdata(dev); - - ata_host_detach(host); - - return 0; -} -EXPORT_SYMBOL_GPL(__pata_platform_remove); - -static int __devinit pata_platform_probe(struct platform_device *pdev) +static int pata_platform_probe(struct platform_device *pdev) { struct resource *io_res; struct resource *ctl_res; struct resource *irq_res; - struct pata_platform_info *pp_info = pdev->dev.platform_data; + struct pata_platform_info *pp_info = dev_get_platdata(&pdev->dev); /* * Simple resource validation .. @@ -242,14 +221,9 @@ static int __devinit pata_platform_probe(struct platform_device *pdev) pio_mask); } -static int __devexit pata_platform_remove(struct platform_device *pdev) -{ - return __pata_platform_remove(&pdev->dev); -} - static struct platform_driver pata_platform_driver = { .probe = pata_platform_probe, - .remove = __devexit_p(pata_platform_remove), + .remove = ata_platform_remove_one, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index 0bb0fb7b26b..73259bfda1e 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/ata.h> #include <linux/libata.h> @@ -32,7 +31,7 @@ #include <scsi/scsi_host.h> #include <mach/pxa2xx-regs.h> -#include <mach/pata_pxa.h> +#include <linux/platform_data/ata-pxa.h> #include <mach/dma.h> #define DRV_NAME "pata_pxa" @@ -229,7 +228,7 @@ static void pxa_ata_dma_irq(int dma, void *port) complete(&pd->dma_done); } -static int __devinit pxa_ata_probe(struct platform_device *pdev) +static int pxa_ata_probe(struct platform_device *pdev) { struct ata_host *host; struct ata_port *ap; @@ -238,7 +237,7 @@ static int __devinit pxa_ata_probe(struct platform_device *pdev) struct resource *ctl_res; struct resource *dma_res; struct resource *irq_res; - struct pata_pxa_pdata *pdata = pdev->dev.platform_data; + struct pata_pxa_pdata *pdata = dev_get_platdata(&pdev->dev); int ret = 0; /* @@ -369,9 +368,9 @@ static int __devinit pxa_ata_probe(struct platform_device *pdev) return ret; } -static int __devexit pxa_ata_remove(struct platform_device *pdev) +static int pxa_ata_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct pata_pxa_data *data = host->ports[0]->private_data; pxa_free_dma(data->dma_channel); @@ -383,7 +382,7 @@ static int __devexit pxa_ata_remove(struct platform_device *pdev) static struct platform_driver pxa_ata_driver = { .probe = pxa_ata_probe, - .remove = __devexit_p(pxa_ata_remove), + .remove = pxa_ata_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c index b2d3a2bb4e6..a3f1123d17a 100644 --- a/drivers/ata/pata_radisys.c +++ b/drivers/ata/pata_radisys.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -238,28 +237,16 @@ static struct pci_driver radisys_pci_driver = { .id_table = radisys_pci_tbl, .probe = radisys_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init radisys_init(void) -{ - return pci_register_driver(&radisys_pci_driver); -} - -static void __exit radisys_exit(void) -{ - pci_unregister_driver(&radisys_pci_driver); -} - -module_init(radisys_init); -module_exit(radisys_exit); +module_pci_driver(radisys_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for Radisys R82600 controllers"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, radisys_pci_tbl); MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c index 9417101bd5c..3c5eb8fa6bd 100644 --- a/drivers/ata/pata_rb532_cf.c +++ b/drivers/ata/pata_rb532_cf.c @@ -102,7 +102,7 @@ static void rb532_pata_setup_ports(struct ata_host *ah) ap->ioaddr.error_addr = info->iobase + RB500_CF_REG_ERR; } -static __devinit int rb532_pata_driver_probe(struct platform_device *pdev) +static int rb532_pata_driver_probe(struct platform_device *pdev) { int irq; int gpio; @@ -177,7 +177,7 @@ err_free_gpio: return ret; } -static __devexit int rb532_pata_driver_remove(struct platform_device *pdev) +static int rb532_pata_driver_remove(struct platform_device *pdev) { struct ata_host *ah = platform_get_drvdata(pdev); struct rb532_cf_info *info = ah->private_data; @@ -190,7 +190,7 @@ static __devexit int rb532_pata_driver_remove(struct platform_device *pdev) static struct platform_driver rb532_pata_platform_driver = { .probe = rb532_pata_driver_probe, - .remove = __devexit_p(rb532_pata_driver_remove), + .remove = rb532_pata_driver_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c index e6a2dd7809c..9ce5952216b 100644 --- a/drivers/ata/pata_rdc.c +++ b/drivers/ata/pata_rdc.c @@ -24,7 +24,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -321,13 +320,11 @@ static struct scsi_host_template rdc_sht = { * Zero on success, or -ERRNO value. */ -static int __devinit rdc_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int rdc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct device *dev = &pdev->dev; struct ata_port_info port_info[2]; const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] }; - unsigned long port_flags; struct ata_host *host; struct rdc_host_priv *hpriv; int rc; @@ -337,8 +334,6 @@ static int __devinit rdc_init_one(struct pci_dev *pdev, port_info[0] = rdc_port_info; port_info[1] = rdc_port_info; - port_flags = port_info[0].flags; - /* enable device and prepare host */ rc = pcim_enable_device(pdev); if (rc) @@ -368,7 +363,7 @@ static int __devinit rdc_init_one(struct pci_dev *pdev, static void rdc_remove_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); struct rdc_host_priv *hpriv = host->private_data; pci_write_config_dword(pdev, 0x54, hpriv->saved_iocfg); @@ -387,25 +382,14 @@ static struct pci_driver rdc_pci_driver = { .id_table = rdc_pci_tbl, .probe = rdc_init_one, .remove = rdc_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init rdc_init(void) -{ - return pci_register_driver(&rdc_pci_driver); -} - -static void __exit rdc_exit(void) -{ - pci_unregister_driver(&rdc_pci_driver); -} - -module_init(rdc_init); -module_exit(rdc_exit); +module_pci_driver(rdc_pci_driver); MODULE_AUTHOR("Alan Cox (based on ata_piix)"); MODULE_DESCRIPTION("SCSI low-level driver for RDC PATA controllers"); diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c index aca321e1e6a..b3ec18c6bcc 100644 --- a/drivers/ata/pata_rz1000.c +++ b/drivers/ata/pata_rz1000.c @@ -14,7 +14,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -102,10 +101,10 @@ static int rz1000_init_one (struct pci_dev *pdev, const struct pci_device_id *en return -ENODEV; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int rz1000_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -134,28 +133,16 @@ static struct pci_driver rz1000_pci_driver = { .id_table = pata_rz1000, .probe = rz1000_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = rz1000_reinit_one, #endif }; -static int __init rz1000_init(void) -{ - return pci_register_driver(&rz1000_pci_driver); -} - -static void __exit rz1000_exit(void) -{ - pci_unregister_driver(&rz1000_pci_driver); -} +module_pci_driver(rz1000_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for RZ1000 PCI ATA"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pata_rz1000); MODULE_VERSION(DRV_VERSION); - -module_init(rz1000_init); -module_exit(rz1000_exit); - diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index 1b372c29719..fb528831fb9 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -23,12 +23,35 @@ #include <linux/platform_device.h> #include <linux/slab.h> -#include <plat/ata.h> -#include <plat/regs-ata.h> +#include <linux/platform_data/ata-samsung_cf.h> #define DRV_NAME "pata_samsung_cf" #define DRV_VERSION "0.1" +#define S3C_CFATA_REG(x) (x) +#define S3C_CFATA_MUX S3C_CFATA_REG(0x0) +#define S3C_ATA_CTRL S3C_CFATA_REG(0x0) +#define S3C_ATA_CMD S3C_CFATA_REG(0x8) +#define S3C_ATA_IRQ S3C_CFATA_REG(0x10) +#define S3C_ATA_IRQ_MSK S3C_CFATA_REG(0x14) +#define S3C_ATA_CFG S3C_CFATA_REG(0x18) + +#define S3C_ATA_PIO_TIME S3C_CFATA_REG(0x2c) +#define S3C_ATA_PIO_DTR S3C_CFATA_REG(0x54) +#define S3C_ATA_PIO_FED S3C_CFATA_REG(0x58) +#define S3C_ATA_PIO_SCR S3C_CFATA_REG(0x5c) +#define S3C_ATA_PIO_LLR S3C_CFATA_REG(0x60) +#define S3C_ATA_PIO_LMR S3C_CFATA_REG(0x64) +#define S3C_ATA_PIO_LHR S3C_CFATA_REG(0x68) +#define S3C_ATA_PIO_DVR S3C_CFATA_REG(0x6c) +#define S3C_ATA_PIO_CSD S3C_CFATA_REG(0x70) +#define S3C_ATA_PIO_DAD S3C_CFATA_REG(0x74) +#define S3C_ATA_PIO_RDATA S3C_CFATA_REG(0x7c) + +#define S3C_CFATA_MUX_TRUEIDE 0x01 +#define S3C_ATA_CFG_SWAP 0x40 +#define S3C_ATA_CFG_IORDYEN 0x02 + enum s3c_cpu_type { TYPE_S3C64XX, TYPE_S5PC100, @@ -241,8 +264,8 @@ static u8 pata_s3c_check_altstatus(struct ata_port *ap) /* * pata_s3c_data_xfer - Transfer data by PIO */ -unsigned int pata_s3c_data_xfer(struct ata_device *dev, unsigned char *buf, - unsigned int buflen, int rw) +static unsigned int pata_s3c_data_xfer(struct ata_device *dev, + unsigned char *buf, unsigned int buflen, int rw) { struct ata_port *ap = dev->link->ap; struct s3c_ide_info *info = ap->host->private_data; @@ -418,7 +441,7 @@ static struct ata_port_operations pata_s5p_port_ops = { .set_piomode = pata_s3c_set_piomode, }; -static void pata_s3c_enable(void *s3c_ide_regbase, bool state) +static void pata_s3c_enable(void __iomem *s3c_ide_regbase, bool state) { u32 temp = readl(s3c_ide_regbase + S3C_ATA_CTRL); temp = state ? (temp | 1) : (temp & ~1); @@ -475,7 +498,7 @@ static void pata_s3c_hwinit(struct s3c_ide_info *info, static int __init pata_s3c_probe(struct platform_device *pdev) { - struct s3c_ide_platdata *pdata = pdev->dev.platform_data; + struct s3c_ide_platdata *pdata = dev_get_platdata(&pdev->dev); struct device *dev = &pdev->dev; struct s3c_ide_info *info; struct resource *res; @@ -495,24 +518,12 @@ static int __init pata_s3c_probe(struct platform_device *pdev) info->irq = platform_get_irq(pdev, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(dev, "failed to get mem resource\n"); - return -EINVAL; - } - - if (!devm_request_mem_region(dev, res->start, - resource_size(res), DRV_NAME)) { - dev_err(dev, "error requesting register region\n"); - return -EBUSY; - } - info->ide_addr = devm_ioremap(dev, res->start, resource_size(res)); - if (!info->ide_addr) { - dev_err(dev, "failed to map IO base address\n"); - return -ENOMEM; - } + info->ide_addr = devm_ioremap_resource(dev, res); + if (IS_ERR(info->ide_addr)) + return PTR_ERR(info->ide_addr); - info->clk = clk_get(&pdev->dev, "cfcon"); + info->clk = devm_clk_get(&pdev->dev, "cfcon"); if (IS_ERR(info->clk)) { dev_err(dev, "failed to get access to cf controller clock\n"); ret = PTR_ERR(info->clk); @@ -583,13 +594,16 @@ static int __init pata_s3c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); - return ata_host_activate(host, info->irq, - info->irq ? pata_s3c_irq : NULL, - 0, &pata_s3c_sht); + ret = ata_host_activate(host, info->irq, + info->irq ? pata_s3c_irq : NULL, + 0, &pata_s3c_sht); + if (ret) + goto stop_clk; + + return 0; stop_clk: clk_disable(info->clk); - clk_put(info->clk); return ret; } @@ -601,12 +615,11 @@ static int __exit pata_s3c_remove(struct platform_device *pdev) ata_host_detach(host); clk_disable(info->clk); - clk_put(info->clk); return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int pata_s3c_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -619,7 +632,7 @@ static int pata_s3c_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct ata_host *host = platform_get_drvdata(pdev); - struct s3c_ide_platdata *pdata = pdev->dev.platform_data; + struct s3c_ide_platdata *pdata = dev_get_platdata(&pdev->dev); struct s3c_ide_info *info = host->private_data; pata_s3c_hwinit(info, pdata); @@ -657,24 +670,13 @@ static struct platform_driver pata_s3c_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .pm = &pata_s3c_pm_ops, #endif }, }; -static int __init pata_s3c_init(void) -{ - return platform_driver_probe(&pata_s3c_driver, pata_s3c_probe); -} - -static void __exit pata_s3c_exit(void) -{ - platform_driver_unregister(&pata_s3c_driver); -} - -module_init(pata_s3c_init); -module_exit(pata_s3c_exit); +module_platform_driver_probe(pata_s3c_driver, pata_s3c_probe); MODULE_AUTHOR("Abhilash Kesavan, <a.kesavan@samsung.com>"); MODULE_DESCRIPTION("low-level driver for Samsung PATA controller"); diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c index c0e603a84f7..c71de5d680d 100644 --- a/drivers/ata/pata_sc1200.c +++ b/drivers/ata/pata_sc1200.c @@ -32,7 +32,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -255,27 +254,16 @@ static struct pci_driver sc1200_pci_driver = { .id_table = sc1200, .probe = sc1200_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init sc1200_init(void) -{ - return pci_register_driver(&sc1200_pci_driver); -} - -static void __exit sc1200_exit(void) -{ - pci_unregister_driver(&sc1200_pci_driver); -} +module_pci_driver(sc1200_pci_driver); MODULE_AUTHOR("Alan Cox, Mark Lord"); MODULE_DESCRIPTION("low-level driver for the NS/AMD SC1200"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, sc1200); MODULE_VERSION(DRV_VERSION); - -module_init(sc1200_init); -module_exit(sc1200_exit); diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index e265f835c95..4e006d74bef 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -35,7 +35,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -1097,32 +1096,13 @@ static struct pci_driver scc_pci_driver = { .id_table = scc_pci_tbl, .probe = scc_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init scc_init (void) -{ - int rc; - - DPRINTK("pci_register_driver\n"); - rc = pci_register_driver(&scc_pci_driver); - if (rc) - return rc; - - DPRINTK("done\n"); - return 0; -} - -static void __exit scc_exit (void) -{ - pci_unregister_driver(&scc_pci_driver); -} - -module_init(scc_init); -module_exit(scc_exit); +module_pci_driver(scc_pci_driver); MODULE_AUTHOR("Toshiba corp"); MODULE_DESCRIPTION("SCSI low-level driver for Toshiba SCC PATA controller"); diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c index 7c78b999362..b920c3407f8 100644 --- a/drivers/ata/pata_sch.c +++ b/drivers/ata/pata_sch.c @@ -27,7 +27,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -65,7 +64,7 @@ static struct pci_driver sch_pci_driver = { .id_table = sch_pci_tbl, .probe = sch_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif @@ -169,8 +168,7 @@ static void sch_set_dmamode(struct ata_port *ap, struct ata_device *adev) * Zero on success, or -ERRNO value. */ -static int __devinit sch_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int sch_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { const struct ata_port_info *ppi[] = { &sch_port_info, NULL }; @@ -179,15 +177,4 @@ static int __devinit sch_init_one(struct pci_dev *pdev, return ata_pci_bmdma_init_one(pdev, ppi, &sch_sht, NULL, 0); } -static int __init sch_init(void) -{ - return pci_register_driver(&sch_pci_driver); -} - -static void __exit sch_exit(void) -{ - pci_unregister_driver(&sch_pci_driver); -} - -module_init(sch_init); -module_exit(sch_exit); +module_pci_driver(sch_pci_driver); diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c index 71eaf385e97..fc5f31d4828 100644 --- a/drivers/ata/pata_serverworks.c +++ b/drivers/ata/pata_serverworks.c @@ -34,7 +34,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -437,10 +436,10 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id return ata_pci_bmdma_init_one(pdev, ppi, &serverworks_sht, NULL, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int serverworks_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -469,27 +468,16 @@ static struct pci_driver serverworks_pci_driver = { .id_table = serverworks, .probe = serverworks_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = serverworks_reinit_one, #endif }; -static int __init serverworks_init(void) -{ - return pci_register_driver(&serverworks_pci_driver); -} - -static void __exit serverworks_exit(void) -{ - pci_unregister_driver(&serverworks_pci_driver); -} +module_pci_driver(serverworks_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, serverworks); MODULE_VERSION(DRV_VERSION); - -module_init(serverworks_init); -module_exit(serverworks_exit); diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c index b92eacf8dd3..f597edccede 100644 --- a/drivers/ata/pata_sil680.c +++ b/drivers/ata/pata_sil680.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -323,8 +322,7 @@ static u8 sil680_init_chip(struct pci_dev *pdev, int *try_mmio) return tmpbyte & 0x30; } -static int __devinit sil680_init_one(struct pci_dev *pdev, - const struct pci_device_id *id) +static int sil680_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { static const struct ata_port_info info = { .flags = ATA_FLAG_SLAVE_POSS, @@ -405,10 +403,10 @@ use_ioports: return ata_pci_bmdma_init_one(pdev, ppi, &sil680_sht, NULL, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sil680_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int try_mmio, rc; rc = ata_pci_device_do_resume(pdev); @@ -431,27 +429,16 @@ static struct pci_driver sil680_pci_driver = { .id_table = sil680, .probe = sil680_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = sil680_reinit_one, #endif }; -static int __init sil680_init(void) -{ - return pci_register_driver(&sil680_pci_driver); -} - -static void __exit sil680_exit(void) -{ - pci_unregister_driver(&sil680_pci_driver); -} +module_pci_driver(sil680_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for SI680 PATA"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, sil680); MODULE_VERSION(DRV_VERSION); - -module_init(sil680_init); -module_exit(sil680_exit); diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c index b0edc7de7b2..626f989d5c6 100644 --- a/drivers/ata/pata_sis.c +++ b/drivers/ata/pata_sis.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -870,10 +869,10 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) return ata_pci_bmdma_init_one(pdev, ppi, &sis_sht, chipset, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sis_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -900,28 +899,16 @@ static struct pci_driver sis_pci_driver = { .id_table = sis_pci_tbl, .probe = sis_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = sis_reinit_one, #endif }; -static int __init sis_init(void) -{ - return pci_register_driver(&sis_pci_driver); -} - -static void __exit sis_exit(void) -{ - pci_unregister_driver(&sis_pci_driver); -} - -module_init(sis_init); -module_exit(sis_exit); +module_pci_driver(sis_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("SCSI low-level driver for SiS ATA"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, sis_pci_tbl); MODULE_VERSION(DRV_VERSION); - diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c index 24cf200dd1c..4935f61f629 100644 --- a/drivers/ata/pata_sl82c105.c +++ b/drivers/ata/pata_sl82c105.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -338,10 +337,10 @@ static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id return ata_pci_bmdma_init_one(dev, ppi, &sl82c105_sht, NULL, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sl82c105_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -366,27 +365,16 @@ static struct pci_driver sl82c105_pci_driver = { .id_table = sl82c105, .probe = sl82c105_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = sl82c105_reinit_one, #endif }; -static int __init sl82c105_init(void) -{ - return pci_register_driver(&sl82c105_pci_driver); -} - -static void __exit sl82c105_exit(void) -{ - pci_unregister_driver(&sl82c105_pci_driver); -} +module_pci_driver(sl82c105_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Sl82c105"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, sl82c105); MODULE_VERSION(DRV_VERSION); - -module_init(sl82c105_init); -module_exit(sl82c105_exit); diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c index 28da1c6becf..d9364af8eb6 100644 --- a/drivers/ata/pata_triflex.c +++ b/drivers/ata/pata_triflex.c @@ -36,7 +36,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> @@ -208,10 +207,10 @@ static const struct pci_device_id triflex[] = { { }, }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int triflex_ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc = 0; rc = ata_host_suspend(host, mesg); @@ -234,27 +233,16 @@ static struct pci_driver triflex_pci_driver = { .id_table = triflex, .probe = triflex_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = triflex_ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif }; -static int __init triflex_init(void) -{ - return pci_register_driver(&triflex_pci_driver); -} - -static void __exit triflex_exit(void) -{ - pci_unregister_driver(&triflex_pci_driver); -} +module_pci_driver(triflex_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Compaq Triflex"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, triflex); MODULE_VERSION(DRV_VERSION); - -module_init(triflex_init); -module_exit(triflex_exit); diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 255f336cd7e..1ca6bcab369 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -55,7 +55,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/gfp.h> @@ -660,7 +659,7 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_bmdma_init_one(pdev, ppi, &via_sht, (void *)config, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /** * via_reinit_one - reinit after resume * @pdev; PCI device @@ -673,7 +672,7 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) static int via_reinit_one(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -705,27 +704,16 @@ static struct pci_driver via_pci_driver = { .id_table = via, .probe = via_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = via_reinit_one, #endif }; -static int __init via_init(void) -{ - return pci_register_driver(&via_pci_driver); -} - -static void __exit via_exit(void) -{ - pci_unregister_driver(&via_pci_driver); -} +module_pci_driver(via_pci_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for VIA PATA"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, via); MODULE_VERSION(DRV_VERSION); - -module_init(via_init); -module_exit(via_exit); diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 04911d52f59..f10631beffa 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -1,7 +1,7 @@ /* * pdc_adma.c - Pacific Digital Corporation ADMA * - * Maintained by: Mark Lord <mlord@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * * Copyright 2005 Mark Lord * @@ -36,7 +36,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -660,21 +659,10 @@ static int adma_ata_init_one(struct pci_dev *pdev, &adma_ata_sht); } -static int __init adma_ata_init(void) -{ - return pci_register_driver(&adma_ata_pci_driver); -} - -static void __exit adma_ata_exit(void) -{ - pci_unregister_driver(&adma_ata_pci_driver); -} +module_pci_driver(adma_ata_pci_driver); MODULE_AUTHOR("Mark Lord"); MODULE_DESCRIPTION("Pacific Digital Corporation ADMA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, adma_ata_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(adma_ata_init); -module_exit(adma_ata_exit); diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 69f7cde49c6..0bb2cabd219 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -29,8 +29,9 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/libata.h> @@ -43,6 +44,7 @@ /* These two are defined in "libata.h" */ #undef DRV_NAME #undef DRV_VERSION + #define DRV_NAME "sata-dwc" #define DRV_VERSION "1.3" @@ -158,6 +160,7 @@ enum { /* Assign HW handshaking interface (x) to destination / source peripheral */ #define DMA_CFG_HW_HS_DEST(int_num) (((int_num) & 0xF) << 11) #define DMA_CFG_HW_HS_SRC(int_num) (((int_num) & 0xF) << 7) +#define DMA_CFG_HW_CH_PRIOR(int_num) (((int_num) & 0xF) << 5) #define DMA_LLP_LMS(addr, master) (((addr) & 0xfffffffc) | (master)) /* @@ -318,6 +321,7 @@ struct sata_dwc_host_priv { u32 dma_interrupt_count; struct ahb_dma_regs *sata_dma_regs; struct device *dwc_dev; + int dma_channel; }; struct sata_dwc_host_priv host_pvt; /* @@ -437,15 +441,12 @@ static void clear_chan_interrupts(int c) */ static int dma_request_channel(void) { - int i; - - for (i = 0; i < DMA_NUM_CHANS; i++) { - if (!(in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)) &\ - DMA_CHANNEL(i))) - return i; - } - dev_err(host_pvt.dwc_dev, "%s NO channel chan_en: 0x%08x\n", __func__, - in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low))); + /* Check if the channel is not currently in use */ + if (!(in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)) & + DMA_CHANNEL(host_pvt.dma_channel))) + return host_pvt.dma_channel; + dev_err(host_pvt.dwc_dev, "%s Channel %d is currently in use\n", + __func__, host_pvt.dma_channel); return -1; } @@ -460,8 +461,7 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance) int chan; u32 tfr_reg, err_reg; unsigned long flags; - struct sata_dwc_device *hsdev = - (struct sata_dwc_device *)hsdev_instance; + struct sata_dwc_device *hsdev = hsdev_instance; struct ata_host *host = (struct ata_host *)hsdev->host; struct ata_port *ap; struct sata_dwc_device_port *hsdevp; @@ -481,7 +481,8 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance) dev_dbg(ap->dev, "eot=0x%08x err=0x%08x pending=%d active port=%d\n", tfr_reg, err_reg, hsdevp->dma_pending[tag], port); - for (chan = 0; chan < DMA_NUM_CHANS; chan++) { + chan = host_pvt.dma_channel; + if (chan >= 0) { /* Check for end-of-transfer interrupt. */ if (tfr_reg & DMA_CHANNEL(chan)) { /* @@ -534,9 +535,9 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance) static int dma_request_interrupts(struct sata_dwc_device *hsdev, int irq) { int retval = 0; - int chan; + int chan = host_pvt.dma_channel; - for (chan = 0; chan < DMA_NUM_CHANS; chan++) { + if (chan >= 0) { /* Unmask error interrupt */ out_le32(&(host_pvt.sata_dma_regs)->interrupt_mask.error.low, DMA_ENABLE_CHAN(chan)); @@ -575,7 +576,10 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems, int fis_len = 0; dma_addr_t next_llp; int bl; + int sms_val, dms_val; + sms_val = 0; + dms_val = 1 + host_pvt.dma_channel; dev_dbg(host_pvt.dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x" " dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli, (u32)dmadr_addr); @@ -635,8 +639,8 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems, lli[idx].ctl.low = cpu_to_le32( DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) | - DMA_CTL_SMS(0) | - DMA_CTL_DMS(1) | + DMA_CTL_SMS(sms_val) | + DMA_CTL_DMS(dms_val) | DMA_CTL_SRC_MSIZE(bl) | DMA_CTL_DST_MSIZE(bl) | DMA_CTL_SINC_NOCHANGE | @@ -651,8 +655,8 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems, lli[idx].ctl.low = cpu_to_le32( DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) | - DMA_CTL_SMS(1) | - DMA_CTL_DMS(0) | + DMA_CTL_SMS(dms_val) | + DMA_CTL_DMS(sms_val) | DMA_CTL_SRC_MSIZE(bl) | DMA_CTL_DST_MSIZE(bl) | DMA_CTL_DINC_NOCHANGE | @@ -744,8 +748,10 @@ static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems, /* Program the CFG register. */ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.high), + DMA_CFG_HW_HS_SRC(dma_ch) | DMA_CFG_HW_HS_DEST(dma_ch) | DMA_CFG_PROTCTL | DMA_CFG_FCMOD_REQ); - out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.low), 0); + out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.low), + DMA_CFG_HW_CH_PRIOR(dma_ch)); /* Program the address of the linked list */ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].llp.low), @@ -1581,10 +1587,31 @@ static void sata_dwc_qc_prep(struct ata_queued_cmd *qc) static void sata_dwc_error_handler(struct ata_port *ap) { - ap->link.flags |= ATA_LFLAG_NO_HRST; ata_sff_error_handler(ap); } +int sata_dwc_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(link->ap); + int ret; + + ret = sata_sff_hardreset(link, class, deadline); + + sata_dwc_enable_interrupts(hsdev); + + /* Reconfigure the DMA control register */ + out_le32(&hsdev->sata_dwc_regs->dmacr, + SATA_DWC_DMACR_TXRXCH_CLEAR); + + /* Reconfigure the DMA Burst Transaction Size register */ + out_le32(&hsdev->sata_dwc_regs->dbtsr, + SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | + SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)); + + return ret; +} + /* * scsi mid-layer and libata interface structures */ @@ -1604,6 +1631,7 @@ static struct ata_port_operations sata_dwc_ops = { .inherits = &ata_sff_port_ops, .error_handler = sata_dwc_error_handler, + .hardreset = sata_dwc_hardreset, .qc_prep = sata_dwc_qc_prep, .qc_issue = sata_dwc_qc_issue, @@ -1638,6 +1666,8 @@ static int sata_dwc_probe(struct platform_device *ofdev) struct ata_host *host; struct ata_port_info pi = sata_dwc_port_info[0]; const struct ata_port_info *ppi[] = { &pi, NULL }; + struct device_node *np = ofdev->dev.of_node; + u32 dma_chan; /* Allocate DWC SATA device */ hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); @@ -1647,6 +1677,13 @@ static int sata_dwc_probe(struct platform_device *ofdev) goto error; } + if (of_property_read_u32(np, "dma-channel", &dma_chan)) { + dev_warn(&ofdev->dev, "no dma-channel property set." + " Use channel 0\n"); + dma_chan = 0; + } + host_pvt.dma_channel = dma_chan; + /* Ioremap SATA registers */ base = of_iomap(ofdev->dev.of_node, 0); if (!base) { diff --git a/drivers/ata/sata_dwc_pmp.c b/drivers/ata/sata_dwc_pmp.c new file mode 100644 index 00000000000..3ff190af8d2 --- /dev/null +++ b/drivers/ata/sata_dwc_pmp.c @@ -0,0 +1,3041 @@ +/* + * drivers/ata/sata_dwc.c + * + * Synopsys DesignWare Cores (DWC) SATA host driver + * + * Author: Mark Miesfeld <mmiesfeld@amcc.com> + * + * Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese <sr@denx.de> + * Copyright 2008 DENX Software Engineering + * + * Based on versions provided by AMCC and Synopsys which are: + * Copyright 2006 Applied Micro Circuits Corporation + * COPYRIGHT (C) 2005 SYNOPSYS, INC. ALL RIGHTS RESERVED + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/libata.h> +#include <linux/rtc.h> + +#include <scsi/scsi_host.h> +#include <scsi/scsi_cmnd.h> + + +#ifdef CONFIG_SATA_DWC_DEBUG +#define dwc_dev_dbg(dev, format, arg...) \ + ({ if (0) dev_printk(KERN_INFO, dev, format, ##arg); 0; }) +#define dwc_port_dbg(ap, format, arg...) \ + ata_port_printk(ap, KERN_INFO, format, ##arg) +#define dwc_link_dbg(link, format, arg...) \ + ata_link_printk(link, KERN_INFO, format, ##arg) +#else +#define dwc_dev_dbg(dev, format, arg...) \ + ({ 0; }) +#define dwc_port_dbg(ap, format, arg...) \ + ({ 0; }) +#define dwc_link_dbg(link, format, arg...) \ + ({ 0; }) +#endif + +#ifdef CONFIG_SATA_DWC_VDEBUG +#define DEBUG_NCQ +#define dwc_dev_vdbg(dev, format, arg...) \ + ({ if (0) dev_printk(KERN_INFO, dev, format, ##arg); 0; }) +#define dwc_port_vdbg(ap, format, arg...) \ + ata_port_printk(ap, KERN_INFO, format, ##arg) +#define dwc_link_vdbg(link, format, arg...) \ + ata_link_printk(link, KERN_INFO, format, ##arg) +#else +#define dwc_dev_vdbg(dev, format, arg...) \ + ({ 0; }) +#define dwc_port_vdbg(ap, format, arg...) \ + ({ 0; }) +#define dwc_link_vdbg(link, format, arg...) \ + ({ 0; }) +#endif + +#define dwc_dev_info(dev, format, arg...) \ + ({ if (0) dev_printk(KERN_INFO, dev, format, ##arg); 0; }) +#define dwc_port_info(ap, format, arg...) \ + ata_port_printk(ap, KERN_INFO, format, ##arg) +#define dwc_link_info(link, format, arg...) \ + ata_link_printk(link, KERN_INFO, format, ##arg) + +/* These two are defined in "libata.h" */ +#undef DRV_NAME +#undef DRV_VERSION +#define DRV_NAME "sata-dwc" +#define DRV_VERSION "2.0" + +/* Port Multiplier discovery Signature */ +#define PSCR_SCONTROL_DET_ENABLE 0x00000001 +#define PSCR_SSTATUS_DET_PRESENT 0x00000001 +#define PSCR_SERROR_DIAG_X 0x04000000 + +/* Port multiplier port entry in SCONTROL register */ +#define SCONTROL_PMP_MASK 0x000f0000 +#define PMP_TO_SCONTROL(p) ((p << 16) & 0x000f0000) +#define SCONTROL_TO_PMP(p) (((p) & 0x000f0000) >> 16) + + +/* SATA DMA driver Globals */ +#if defined(CONFIG_APM821xx) +#define DMA_NUM_CHANS 2 +#else +#define DMA_NUM_CHANS 1 +#endif + +#define DMA_NUM_CHAN_REGS 8 + +/* SATA DMA Register definitions */ +#if defined(CONFIG_APM821xx) +#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length */ +#else +#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length */ +#endif + +#if defined(CONFIG_APOLLO3G) +extern void signal_hdd_led(int, int); +#endif +struct dmareg { + u32 low; /* Low bits 0-31 */ + u32 high; /* High bits 32-63 */ +}; + +/* DMA Per Channel registers */ + +struct dma_chan_regs { + struct dmareg sar; /* Source Address */ + struct dmareg dar; /* Destination address */ + struct dmareg llp; /* Linked List Pointer */ + struct dmareg ctl; /* Control */ + struct dmareg sstat; /* Source Status not implemented in core */ + struct dmareg dstat; /* Destination Status not implemented in core */ + struct dmareg sstatar; /* Source Status Address not impl in core */ + struct dmareg dstatar; /* Destination Status Address not implemented */ + struct dmareg cfg; /* Config */ + struct dmareg sgr; /* Source Gather */ + struct dmareg dsr; /* Destination Scatter */ +}; + +/* Generic Interrupt Registers */ +struct dma_interrupt_regs { + struct dmareg tfr; /* Transfer Interrupt */ + struct dmareg block; /* Block Interrupt */ + struct dmareg srctran; /* Source Transfer Interrupt */ + struct dmareg dsttran; /* Dest Transfer Interrupt */ + struct dmareg error; /* Error */ +}; + +struct ahb_dma_regs { + struct dma_chan_regs chan_regs[DMA_NUM_CHAN_REGS]; + struct dma_interrupt_regs interrupt_raw; /* Raw Interrupt */ + struct dma_interrupt_regs interrupt_status; /* Interrupt Status */ + struct dma_interrupt_regs interrupt_mask; /* Interrupt Mask */ + struct dma_interrupt_regs interrupt_clear; /* Interrupt Clear */ + struct dmareg statusInt; /* Interrupt combined */ + struct dmareg rq_srcreg; /* Src Trans Req */ + struct dmareg rq_dstreg; /* Dst Trans Req */ + struct dmareg rq_sgl_srcreg; /* Sngl Src Trans Req */ + struct dmareg rq_sgl_dstreg; /* Sngl Dst Trans Req */ + struct dmareg rq_lst_srcreg; /* Last Src Trans Req */ + struct dmareg rq_lst_dstreg; /* Last Dst Trans Req */ + struct dmareg dma_cfg; /* DMA Config */ + struct dmareg dma_chan_en; /* DMA Channel Enable */ + struct dmareg dma_id; /* DMA ID */ + struct dmareg dma_test; /* DMA Test */ + struct dmareg res1; /* reserved */ + struct dmareg res2; /* reserved */ + + /* DMA Comp Params + * Param 6 = dma_param[0], Param 5 = dma_param[1], + * Param 4 = dma_param[2] ... + */ + struct dmareg dma_params[6]; +}; + +/* Data structure for linked list item */ +struct lli { + u32 sar; /* Source Address */ + u32 dar; /* Destination address */ + u32 llp; /* Linked List Pointer */ + struct dmareg ctl; /* Control */ +#if defined(CONFIG_APM821xx) + u32 dstat; /* Source status is not supported */ +#else + struct dmareg dstat; /* Destination Status */ +#endif +}; + +#define SATA_DWC_DMAC_LLI_SZ (sizeof(struct lli)) +#define SATA_DWC_DMAC_LLI_NUM 256 +#define SATA_DWC_DMAC_TWIDTH_BYTES 4 +#define SATA_DWC_DMAC_LLI_TBL_SZ \ + (SATA_DWC_DMAC_LLI_SZ * SATA_DWC_DMAC_LLI_NUM) +#if defined(CONFIG_APM821xx) +#define SATA_DWC_DMAC_CTRL_TSIZE_MAX \ + (0x00000800 * SATA_DWC_DMAC_TWIDTH_BYTES) +#else +#define SATA_DWC_DMAC_CTRL_TSIZE_MAX \ + (0x00000800 * SATA_DWC_DMAC_TWIDTH_BYTES) +#endif +/* DMA Register Operation Bits */ +#define DMA_EN 0x00000001 /* Enable AHB DMA */ +#define DMA_CHANNEL(ch) (0x00000001 << (ch)) /* Select channel */ +#define DMA_ENABLE_CHAN(ch) ((0x00000001 << (ch)) | \ + ((0x000000001 << (ch)) << 8)) +#define DMA_DISABLE_CHAN(ch) (0x00000000 | ((0x000000001 << (ch)) << 8)) + +/* Channel Control Register */ +#define DMA_CTL_BLK_TS(size) ((size) & 0x000000FFF) /* Blk Transfer size */ +#define DMA_CTL_LLP_SRCEN 0x10000000 /* Blk chain enable Src */ +#define DMA_CTL_LLP_DSTEN 0x08000000 /* Blk chain enable Dst */ +/* + * This define is used to set block chaining disabled in the control low + * register. It is already in little endian format so it can be &'d dirctly. + * It is essentially: cpu_to_le32(~(DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN)) + */ +#define DMA_CTL_LLP_DISABLE_LE32 0xffffffe7 +#define DMA_CTL_SMS(num) ((num & 0x3) << 25) /*Src Master Select*/ +#define DMA_CTL_DMS(num) ((num & 0x3) << 23) /*Dst Master Select*/ +#define DMA_CTL_TTFC(type) ((type & 0x7) << 20) /*Type&Flow cntr*/ +#define DMA_CTL_TTFC_P2M_DMAC 0x00000002 /*Per mem,DMAC cntr*/ +#define DMA_CTL_TTFC_M2P_PER 0x00000003 /*Mem per,peri cntr*/ +#define DMA_CTL_SRC_MSIZE(size) ((size & 0x7) << 14) /*Src Burst Len*/ +#define DMA_CTL_DST_MSIZE(size) ((size & 0x7) << 11) /*Dst Burst Len*/ +#define DMA_CTL_SINC_INC 0x00000000 /*Src addr incr*/ +#define DMA_CTL_SINC_DEC 0x00000200 +#define DMA_CTL_SINC_NOCHANGE 0x00000400 +#define DMA_CTL_DINC_INC 0x00000000 /*Dst addr incr*/ +#define DMA_CTL_DINC_DEC 0x00000080 +#define DMA_CTL_DINC_NOCHANGE 0x00000100 +#define DMA_CTL_SRC_TRWID(size) ((size & 0x7) << 4) /*Src Trnsfr Width*/ +#define DMA_CTL_DST_TRWID(size) ((size & 0x7) << 1) /*Dst Trnsfr Width*/ +#define DMA_CTL_INT_EN 0x00000001 /*Interrupt Enable*/ + +/* Channel Configuration Register high bits */ +#define DMA_CFG_FCMOD_REQ 0x00000001 /*Flow cntrl req*/ +#define DMA_CFG_PROTCTL (0x00000003 << 2) /*Protection cntrl*/ + +/* Channel Configuration Register low bits */ +#define DMA_CFG_RELD_DST 0x80000000 /*Reload Dst/Src Addr*/ +#define DMA_CFG_RELD_SRC 0x40000000 +#define DMA_CFG_HS_SELSRC 0x00000800 /*SW hndshk Src/Dst*/ +#define DMA_CFG_HS_SELDST 0x00000400 +#define DMA_CFG_FIFOEMPTY (0x00000001 << 9) /*FIFO Empty bit*/ + +/* Assign hardware handshaking interface (x) to dst / sre peripheral */ +#define DMA_CFG_HW_HS_DEST(int_num) ((int_num & 0xF) << 11) +#define DMA_CFG_HW_HS_SRC(int_num) ((int_num & 0xF) << 7) + +/* Channel Linked List Pointer Register */ +#define DMA_LLP_LMS(addr, master) (((addr) & 0xfffffffc) | (master)) +#define DMA_LLP_AHBMASTER1 0 /* List Master Select */ +#define DMA_LLP_AHBMASTER2 1 + +#define SATA_DWC_MAX_PORTS 1 + +#define SATA_DWC_SCR_OFFSET 0x24 +#define SATA_DWC_REG_OFFSET 0x64 + +/* DWC SATA Registers */ +struct sata_dwc_regs { + u32 fptagr; /* 1st party DMA tag */ + u32 fpbor; /* 1st party DMA buffer offset */ + u32 fptcr; /* 1st party DMA Xfr count */ + u32 dmacr; /* DMA Control */ + u32 dbtsr; /* DMA Burst Transac size */ + u32 intpr; /* Interrupt Pending */ + u32 intmr; /* Interrupt Mask */ + u32 errmr; /* Error Mask */ + u32 llcr; /* Link Layer Control */ + u32 phycr; /* PHY Control */ + u32 physr; /* PHY Status */ + u32 rxbistpd; /* Recvd BIST pattern def register */ + u32 rxbistpd1; /* Recvd BIST data dword1 */ + u32 rxbistpd2; /* Recvd BIST pattern data dword2 */ + u32 txbistpd; /* Trans BIST pattern def register */ + u32 txbistpd1; /* Trans BIST data dword1 */ + u32 txbistpd2; /* Trans BIST data dword2 */ + u32 bistcr; /* BIST Control Register */ + u32 bistfctr; /* BIST FIS Count Register */ + u32 bistsr; /* BIST Status Register */ + u32 bistdecr; /* BIST Dword Error count register */ + u32 res[15]; /* Reserved locations */ + u32 testr; /* Test Register */ + u32 versionr; /* Version Register */ + u32 idr; /* ID Register */ + u32 unimpl[192]; /* Unimplemented */ + u32 dmadr[256]; /* FIFO Locations in DMA Mode */ +}; + +#define SCR_SCONTROL_DET_ENABLE 0x00000001 +#define SCR_SSTATUS_DET_PRESENT 0x00000001 +#define SCR_SERROR_DIAG_X 0x04000000 + +/* DWC SATA Register Operations */ +#define SATA_DWC_TXFIFO_DEPTH 0x01FF +#define SATA_DWC_RXFIFO_DEPTH 0x01FF + +#define SATA_DWC_DMACR_TMOD_TXCHEN 0x00000004 +#define SATA_DWC_DMACR_TXCHEN (0x00000001 | \ + SATA_DWC_DMACR_TMOD_TXCHEN) +#define SATA_DWC_DMACR_RXCHEN (0x00000002 | \ + SATA_DWC_DMACR_TMOD_TXCHEN) +#define SATA_DWC_DMACR_TX_CLEAR(v) (((v) & ~SATA_DWC_DMACR_TXCHEN) | \ + SATA_DWC_DMACR_TMOD_TXCHEN) +#define SATA_DWC_DMACR_RX_CLEAR(v) (((v) & ~SATA_DWC_DMACR_RXCHEN) | \ + SATA_DWC_DMACR_TMOD_TXCHEN) +#define SATA_DWC_DMACR_TXRXCH_CLEAR SATA_DWC_DMACR_TMOD_TXCHEN + +#define SATA_DWC_DBTSR_MWR(size) ((size/4) & \ + SATA_DWC_TXFIFO_DEPTH) +#define SATA_DWC_DBTSR_MRD(size) (((size/4) & \ + SATA_DWC_RXFIFO_DEPTH) << 16) + +// SATA DWC Interrupts +#define SATA_DWC_INTPR_DMAT 0x00000001 +#define SATA_DWC_INTPR_NEWFP 0x00000002 +#define SATA_DWC_INTPR_PMABRT 0x00000004 +#define SATA_DWC_INTPR_ERR 0x00000008 +#define SATA_DWC_INTPR_NEWBIST 0x00000010 +#define SATA_DWC_INTPR_IPF 0x80000000 +// Interrupt masks +#define SATA_DWC_INTMR_DMATM 0x00000001 +#define SATA_DWC_INTMR_NEWFPM 0x00000002 +#define SATA_DWC_INTMR_PMABRTM 0x00000004 +#define SATA_DWC_INTMR_ERRM 0x00000008 +#define SATA_DWC_INTMR_NEWBISTM 0x00000010 +#define SATA_DWC_INTMR_PRIMERRM 0x00000020 +#define SATA_DWC_INTPR_CMDGOOD 0x00000080 +#define SATA_DWC_INTPR_CMDABORT 0x00000040 + +#define SATA_DWC_LLCR_SCRAMEN 0x00000001 +#define SATA_DWC_LLCR_DESCRAMEN 0x00000002 +#define SATA_DWC_LLCR_RPDEN 0x00000004 + +// Defines for SError register +#define SATA_DWC_SERR_ERRI 0x00000001 // Recovered data integrity error +#define SATA_DWC_SERR_ERRM 0x00000002 // Recovered communication error +#define SATA_DWC_SERR_ERRT 0x00000100 // Non-recovered transient data integrity error +#define SATA_DWC_SERR_ERRC 0x00000200 // Non-recovered persistent communication or data integrity error +#define SATA_DWC_SERR_ERRP 0x00000400 // Protocol error +#define SATA_DWC_SERR_ERRE 0x00000800 // Internal host adapter error +#define SATA_DWC_SERR_DIAGN 0x00010000 // PHYRdy change +#define SATA_DWC_SERR_DIAGI 0x00020000 // PHY internal error +#define SATA_DWC_SERR_DIAGW 0x00040000 // Phy COMWAKE signal is detected +#define SATA_DWC_SERR_DIAGB 0x00080000 // 10b to 8b decoder err +#define SATA_DWC_SERR_DIAGT 0x00100000 // Disparity error +#define SATA_DWC_SERR_DIAGC 0x00200000 // CRC error +#define SATA_DWC_SERR_DIAGH 0x00400000 // Handshake error +#define SATA_DWC_SERR_DIAGL 0x00800000 // Link sequence (illegal transition) error +#define SATA_DWC_SERR_DIAGS 0x01000000 // Transport state transition error +#define SATA_DWC_SERR_DIAGF 0x02000000 // Unrecognized FIS type +#define SATA_DWC_SERR_DIAGX 0x04000000 // Exchanged error - Set when PHY COMINIT signal is detected. +#define SATA_DWC_SERR_DIAGA 0x08000000 // Port Selector Presence detected + +/* This is all error bits, zero's are reserved fields. */ +#define SATA_DWC_SERR_ERR_BITS 0x0FFF0F03 + +#define SATA_DWC_SCR0_SPD_GET(v) ((v >> 4) & 0x0000000F) + +struct sata_dwc_device { + struct resource reg; /* Resource for register */ + struct device *dev; /* generic device struct */ + struct ata_probe_ent *pe; /* ptr to probe-ent */ + struct ata_host *host; + u8 *reg_base; + struct sata_dwc_regs *sata_dwc_regs; /* DW Synopsys SATA specific */ + u8 *scr_base; + int dma_channel; /* DWC SATA DMA channel */ + int irq_dma; + struct timer_list an_timer; +}; + +#define SATA_DWC_QCMD_MAX 32 + +struct sata_dwc_device_port { + struct sata_dwc_device *hsdev; + int cmd_issued[SATA_DWC_QCMD_MAX]; + struct lli *llit[SATA_DWC_QCMD_MAX]; + dma_addr_t llit_dma[SATA_DWC_QCMD_MAX]; + u32 dma_chan[SATA_DWC_QCMD_MAX]; + int dma_pending[SATA_DWC_QCMD_MAX]; + u32 sata_dwc_sactive_issued; /* issued queued ops */ + u32 sata_dwc_sactive_queued; /* queued ops */ + u32 dma_interrupt_count; + +}; + +static struct sata_dwc_device* dwc_dev_list[2]; +static int dma_intr_registered = 0; +/* + * Commonly used DWC SATA driver Macros + */ +#define HSDEV_FROM_HOST(host) ((struct sata_dwc_device *) \ + (host)->private_data) +#define HSDEV_FROM_AP(ap) ((struct sata_dwc_device *) \ + (ap)->host->private_data) +#define HSDEVP_FROM_AP(ap) ((struct sata_dwc_device_port *) \ + (ap)->private_data) +#define HSDEV_FROM_QC(qc) ((struct sata_dwc_device *) \ + (qc)->ap->host->private_data) +#define HSDEV_FROM_HSDEVP(p) ((struct sata_dwc_device *) \ + (hsdevp)->hsdev) + +enum { + SATA_DWC_CMD_ISSUED_NOT = 0, + SATA_DWC_CMD_ISSUED_PENDING = 1, + SATA_DWC_CMD_ISSUED_EXEC = 2, + SATA_DWC_CMD_ISSUED_NODATA = 3, + + SATA_DWC_DMA_PENDING_NONE = 0, + SATA_DWC_DMA_PENDING_TX = 1, + SATA_DWC_DMA_PENDING_RX = 2, +}; + +/* + * Globals + */ +static struct ahb_dma_regs *sata_dma_regs = 0; + +/* + * Prototypes + */ +static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag); +static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc, + u32 check_status); +static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status); +static void sata_dwc_port_stop(struct ata_port *ap); +static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag); + +static int dma_dwc_init(struct sata_dwc_device *hsdev); +static void dma_dwc_exit(struct sata_dwc_device *hsdev); +static int dma_dwc_xfer_setup(struct ata_queued_cmd *qc, + struct lli *lli, dma_addr_t dma_lli, + void __iomem *addr); +static void dma_dwc_xfer_start(int dma_ch); +static void dma_dwc_terminate_dma(struct ata_port *ap, int dma_ch); +static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev); +static void sata_dwc_init_port ( struct ata_port *ap ); +u8 sata_dwc_check_status(struct ata_port *ap); + + + + +static const char *dir_2_txt(enum dma_data_direction dir) +{ + switch (dir) { + case DMA_BIDIRECTIONAL: + return "bi"; + case DMA_FROM_DEVICE: + return "from"; + case DMA_TO_DEVICE: + return "to"; + case DMA_NONE: + return "none"; + default: + return "err"; + } +} + +static const char *prot_2_txt(enum ata_tf_protocols protocol) +{ + switch (protocol) { + case ATA_PROT_UNKNOWN: + return "unknown"; + case ATA_PROT_NODATA: + return "nodata"; + case ATA_PROT_PIO: + return "pio"; + case ATA_PROT_DMA: + return "dma"; + case ATA_PROT_NCQ: + return "ncq"; + case ATAPI_PROT_PIO: + return "atapi pio"; + case ATAPI_PROT_NODATA: + return "atapi nodata"; + case ATAPI_PROT_DMA: + return "atapi dma"; + default: + return "err"; + } +} + +inline const char *ata_cmd_2_txt(const struct ata_taskfile *tf) +{ + switch (tf->command) { + case ATA_CMD_CHK_POWER: + return "ATA_CMD_CHK_POWER"; + case ATA_CMD_EDD: + return "ATA_CMD_EDD"; + case ATA_CMD_FLUSH: + return "ATA_CMD_FLUSH"; + case ATA_CMD_FLUSH_EXT: + return "ATA_CMD_FLUSH_EXT"; + case ATA_CMD_ID_ATA: + return "ATA_CMD_ID_ATA"; + case ATA_CMD_ID_ATAPI: + return "ATA_CMD_ID_ATAPI"; + case ATA_CMD_FPDMA_READ: + return "ATA_CMD_FPDMA_READ"; + case ATA_CMD_FPDMA_WRITE: + return "ATA_CMD_FPDMA_WRITE"; + case ATA_CMD_READ: + return "ATA_CMD_READ"; + case ATA_CMD_READ_EXT: + return "ATA_CMD_READ_EXT"; + case ATA_CMD_READ_NATIVE_MAX_EXT : + return "ATA_CMD_READ_NATIVE_MAX_EXT"; + case ATA_CMD_VERIFY_EXT : + return "ATA_CMD_VERIFY_EXT"; + case ATA_CMD_WRITE: + return "ATA_CMD_WRITE"; + case ATA_CMD_WRITE_EXT: + return "ATA_CMD_WRITE_EXT"; + case ATA_CMD_PIO_READ: + return "ATA_CMD_PIO_READ"; + case ATA_CMD_PIO_READ_EXT: + return "ATA_CMD_PIO_READ_EXT"; + case ATA_CMD_PIO_WRITE: + return "ATA_CMD_PIO_WRITE"; + case ATA_CMD_PIO_WRITE_EXT: + return "ATA_CMD_PIO_WRITE_EXT"; + case ATA_CMD_SET_FEATURES: + return "ATA_CMD_SET_FEATURES"; + case ATA_CMD_PACKET: + return "ATA_CMD_PACKET"; + case ATA_CMD_PMP_READ: + return "ATA_CMD_PMP_READ"; + case ATA_CMD_PMP_WRITE: + return "ATA_CMD_PMP_WRITE"; + default: + return "ATA_CMD_???"; + } +} + +/* + * Dump content of the taskfile + */ +static void sata_dwc_tf_dump(struct device *dwc_dev, struct ata_taskfile *tf) +{ + dwc_dev_vdbg(dwc_dev, "taskfile cmd: 0x%02x protocol: %s flags: 0x%lx" + "device: %x\n", tf->command, prot_2_txt(tf->protocol), + tf->flags, tf->device); + dwc_dev_vdbg(dwc_dev, "feature: 0x%02x nsect: 0x%x lbal: 0x%x lbam:" + "0x%x lbah: 0x%x\n", tf->feature, tf->nsect, tf->lbal, + tf->lbam, tf->lbah); + dwc_dev_vdbg(dwc_dev, "hob_feature: 0x%02x hob_nsect: 0x%x hob_lbal: 0x%x " + "hob_lbam: 0x%x hob_lbah: 0x%x\n", tf->hob_feature, + tf->hob_nsect, tf->hob_lbal, tf->hob_lbam, + tf->hob_lbah); +} + +/* + * Function: get_burst_length_encode + * arguments: datalength: length in bytes of data + * returns value to be programmed in register corresponding to data length + * This value is effectively the log(base 2) of the length + */ +static inline int get_burst_length_encode(int datalength) +{ + int items = datalength >> 2; /* div by 4 to get lword count */ + + if (items >= 64) + return 5; + + if (items >= 32) + return 4; + + if (items >= 16) + return 3; + + if (items >= 8) + return 2; + + if (items >= 4) + return 1; + + return 0; +} + +/* + * Clear Interrupts on a DMA channel + */ +static inline void clear_chan_interrupts(int c) +{ + out_le32(&(sata_dma_regs->interrupt_clear.tfr.low), DMA_CHANNEL(c)); + out_le32(&(sata_dma_regs->interrupt_clear.block.low), DMA_CHANNEL(c)); + out_le32(&(sata_dma_regs->interrupt_clear.srctran.low), DMA_CHANNEL(c)); + out_le32(&(sata_dma_regs->interrupt_clear.dsttran.low), DMA_CHANNEL(c)); + out_le32(&(sata_dma_regs->interrupt_clear.error.low), DMA_CHANNEL(c)); +} + +/* + * Function: dma_request_channel + * arguments: None + * returns channel number if available else -1 + * This function assigns the next available DMA channel from the list to the + * requester + */ +static int dma_request_channel(struct ata_port *ap) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + + if (!(in_le32(&(sata_dma_regs->dma_chan_en.low)) & DMA_CHANNEL(hsdev->dma_channel))) { + dwc_port_vdbg(ap, "%s Successfully requested DMA channel %d\n", + __func__, hsdev->dma_channel); + return (hsdev->dma_channel); + } + + return -1; +} + + + +/* + * Function: dma_dwc_interrupt + * arguments: irq, dev_id, pt_regs + * returns channel number if available else -1 + * Interrupt Handler for DW AHB SATA DMA + */ +static int dma_dwc_interrupt(int irq, void *hsdev_instance) +{ + volatile u32 tfr_reg, err_reg; + unsigned long flags; + struct sata_dwc_device *hsdev = hsdev_instance; + struct ata_host *host = (struct ata_host *)hsdev->host; + struct ata_port *ap; + struct sata_dwc_device_port *hsdevp; + u8 tag = 0; + int chan; + unsigned int port = 0; + spin_lock_irqsave(&host->lock, flags); + + ap = host->ports[port]; + hsdevp = HSDEVP_FROM_AP(ap); + tag = ap->link.active_tag; + + dwc_port_vdbg(ap, "%s: DMA interrupt in channel %d\n", __func__, hsdev->dma_channel); + + tfr_reg = in_le32(&(sata_dma_regs->interrupt_status.tfr.low)); + err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low)); + + dwc_port_vdbg(ap, "eot=0x%08x err=0x%08x pending=%d active port=%d\n", + tfr_reg, err_reg, hsdevp->dma_pending[tag], port); + chan = hsdev->dma_channel; + + if (tfr_reg & DMA_CHANNEL(chan)) { + /* + *Each DMA command produces 2 interrupts. Only + * complete the command after both interrupts have been + * seen. (See sata_dwc_isr()) + */ + hsdevp->dma_interrupt_count++; + sata_dwc_clear_dmacr(hsdevp, tag); + + if (unlikely(hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_NONE)) { + dev_err(ap->dev, "DMA not pending eot=0x%08x " + "err=0x%08x tag=0x%02x pending=%d\n", + tfr_reg, err_reg, tag, + hsdevp->dma_pending[tag]); + } + + // Do remain jobs after DMA transfer complete + if ((hsdevp->dma_interrupt_count % 2) == 0) + sata_dwc_dma_xfer_complete(ap, 1); + + /* Clear the interrupt */ + out_le32(&(sata_dma_regs->interrupt_clear.tfr.low), + DMA_CHANNEL(chan)); + } + + /* Process error interrupt. */ + // We do not expect error happen + if (unlikely(err_reg & DMA_CHANNEL(chan))) { + /* TODO Need error handler ! */ + dev_err(ap->dev, "error interrupt err_reg=0x%08x\n", + err_reg); + + spin_lock_irqsave(ap->lock, flags); + //if (ata_is_dma(qc->tf.protocol)) { + /* disable DMAC */ + dma_dwc_terminate_dma(ap, chan); + //} + spin_unlock_irqrestore(ap->lock, flags); + + /* Clear the interrupt. */ + out_le32(&(sata_dma_regs->interrupt_clear.error.low), + DMA_CHANNEL(chan)); + } + + spin_unlock_irqrestore(&host->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t dma_dwc_handler(int irq, void *hsdev_instance) +{ + volatile u32 tfr_reg, err_reg; + int chan; + + tfr_reg = in_le32(&(sata_dma_regs->interrupt_status.tfr.low)); + err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low)); + + for (chan = 0; chan < DMA_NUM_CHANS; chan++) { + /* Check for end-of-transfer interrupt. */ + + if (tfr_reg & DMA_CHANNEL(chan)) { + dma_dwc_interrupt(0, dwc_dev_list[chan]); + } + else + + /* Check for error interrupt. */ + if (err_reg & DMA_CHANNEL(chan)) { + dma_dwc_interrupt(0, dwc_dev_list[chan]); + } + } + + return IRQ_HANDLED; +} + +static int dma_register_interrupt (struct sata_dwc_device *hsdev) +{ + int retval = 0; + int irq = hsdev->irq_dma; + /* + * FIXME: 2 SATA controllers share the same DMA engine so + * currently, they also share same DMA interrupt + */ + if (!dma_intr_registered) { + printk("%s register irq (%d)\n", __func__, irq); + retval = request_irq(irq, dma_dwc_handler, IRQF_SHARED, "SATA DMA", hsdev); + //retval = request_irq(irq, dma_dwc_handler, IRQF_DISABLED, "SATA DMA", NULL); + if (retval) { + dev_err(hsdev->dev, "%s: could not get IRQ %d\n", __func__, irq); + return -ENODEV; + } + //dma_intr_registered = 1; + } + return retval; +} + +/* + * Function: dma_request_interrupts + * arguments: hsdev + * returns status + * This function registers ISR for a particular DMA channel interrupt + */ +static int dma_request_interrupts(struct sata_dwc_device *hsdev, int irq) +{ + int retval = 0; + int dma_chan = hsdev->dma_channel; + + /* Unmask error interrupt */ + out_le32(&sata_dma_regs->interrupt_mask.error.low, + in_le32(&sata_dma_regs->interrupt_mask.error.low) | DMA_ENABLE_CHAN(dma_chan)); + + /* Unmask end-of-transfer interrupt */ + out_le32(&sata_dma_regs->interrupt_mask.tfr.low, + in_le32(&sata_dma_regs->interrupt_mask.tfr.low) | DMA_ENABLE_CHAN(dma_chan)); + + dwc_dev_vdbg(hsdev->dev, "Current value of interrupt_mask.error=0x%0x\n", in_le32(&sata_dma_regs->interrupt_mask.error.low)); + dwc_dev_vdbg(hsdev->dev, "Current value of interrupt_mask.tfr=0x%0x\n", in_le32(&sata_dma_regs->interrupt_mask.tfr.low)); +#if 0 + out_le32(&sata_dma_regs->interrupt_mask.block.low, + DMA_ENABLE_CHAN(dma_chan)); + + out_le32(&sata_dma_regs->interrupt_mask.srctran.low, + DMA_ENABLE_CHAN(dma_chan)); + + out_le32(&sata_dma_regs->interrupt_mask.dsttran.low, + DMA_ENABLE_CHAN(dma_chan)); +#endif + return retval; +} + +/* + * Function: map_sg_to_lli + * arguments: sg: scatter/gather list(sg) + * num_elems: no of elements in sg list + * dma_lli: LLI table + * dest: destination address + * read: whether the transfer is read or write + * returns array of AHB DMA Linked List Items + * This function creates a list of LLIs for DMA Xfr and returns the number + * of elements in the DMA linked list. + * + * Note that the Synopsis driver has a comment proposing that better performance + * is possible by only enabling interrupts on the last item in the linked list. + * However, it seems that could be a problem if an error happened on one of the + * first items. The transfer would halt, but no error interrupt would occur. + * + * Currently this function sets interrupts enabled for each linked list item: + * DMA_CTL_INT_EN. + */ +static int map_sg_to_lli(struct ata_queued_cmd *qc, struct lli *lli, + dma_addr_t dma_lli, void __iomem *dmadr_addr) +{ + struct scatterlist *sg = qc->sg; + struct device *dwc_dev = qc->ap->dev; + int num_elems = qc->n_elem; + int dir = qc->dma_dir; + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(qc->ap); + + int i, idx = 0; + int fis_len = 0; + dma_addr_t next_llp; + int bl; + unsigned int dma_ts = 0; + + dwc_port_vdbg(qc->ap, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x " + "dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli, + (u32)dmadr_addr); + + bl = get_burst_length_encode(AHB_DMA_BRST_DFLT); + + for (i = 0; i < num_elems; i++, sg++) { + u32 addr, offset; + u32 sg_len, len; + + addr = (u32) sg_dma_address(sg); + sg_len = sg_dma_len(sg); + + dwc_port_vdbg(qc->ap, "%s: elem=%d sg_addr=0x%x sg_len=%d\n", + __func__, i, addr, sg_len); + + while (sg_len) { + + if (unlikely(idx >= SATA_DWC_DMAC_LLI_NUM)) { + /* The LLI table is not large enough. */ + dev_err(dwc_dev, "LLI table overrun (idx=%d)\n", + idx); + break; + } + len = (sg_len > SATA_DWC_DMAC_CTRL_TSIZE_MAX) ? + SATA_DWC_DMAC_CTRL_TSIZE_MAX : sg_len; + + offset = addr & 0xffff; + if ((offset + sg_len) > 0x10000) + len = 0x10000 - offset; + + /* + * Make sure a LLI block is not created that will span a + * 8K max FIS boundary. If the block spans such a FIS + * boundary, there is a chance that a DMA burst will + * cross that boundary -- this results in an error in + * the host controller. + */ + if (unlikely(fis_len + len > 8192)) { + dwc_port_vdbg(qc->ap, "SPLITTING: fis_len=%d(0x%x) " + "len=%d(0x%x)\n", fis_len, fis_len, + len, len); + len = 8192 - fis_len; + fis_len = 0; + } else { + fis_len += len; + } + if (fis_len == 8192) + fis_len = 0; + + /* + * Set DMA addresses and lower half of control register + * based on direction. + */ + dwc_port_vdbg(qc->ap, "%s: sg_len = %d, len = %d\n", __func__, sg_len, len); + +#if defined(CONFIG_APM821xx) + if (dir == DMA_FROM_DEVICE) { + lli[idx].dar = cpu_to_le32(addr); + lli[idx].sar = cpu_to_le32((u32)dmadr_addr); + if (hsdevp->hsdev->dma_channel == 0) {/* DMA channel 0 */ + lli[idx].ctl.low = cpu_to_le32( + DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) | + DMA_CTL_SMS(1) | /* Source: Master 2 */ + DMA_CTL_DMS(0) | /* Dest: Master 1 */ + DMA_CTL_SRC_MSIZE(bl) | + DMA_CTL_DST_MSIZE(bl) | + DMA_CTL_SINC_NOCHANGE | + DMA_CTL_SRC_TRWID(2) | + DMA_CTL_DST_TRWID(2) | + DMA_CTL_INT_EN | + DMA_CTL_LLP_SRCEN | + DMA_CTL_LLP_DSTEN); + } else if (hsdevp->hsdev->dma_channel == 1) {/* DMA channel 1 */ + lli[idx].ctl.low = cpu_to_le32( + DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) | + DMA_CTL_SMS(2) | /* Source: Master 3 */ + DMA_CTL_DMS(0) | /* Dest: Master 1 */ + DMA_CTL_SRC_MSIZE(bl) | + DMA_CTL_DST_MSIZE(bl) | + DMA_CTL_SINC_NOCHANGE | + DMA_CTL_SRC_TRWID(2) | + DMA_CTL_DST_TRWID(2) | + DMA_CTL_INT_EN | + DMA_CTL_LLP_SRCEN | + DMA_CTL_LLP_DSTEN); + } + } else { /* DMA_TO_DEVICE */ + lli[idx].sar = cpu_to_le32(addr); + lli[idx].dar = cpu_to_le32((u32)dmadr_addr); + if (hsdevp->hsdev->dma_channel == 0) {/* DMA channel 0 */ + lli[idx].ctl.low = cpu_to_le32( + DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) | + DMA_CTL_SMS(0) | + DMA_CTL_DMS(1) | + DMA_CTL_SRC_MSIZE(bl) | + DMA_CTL_DST_MSIZE(bl) | + DMA_CTL_DINC_NOCHANGE | + DMA_CTL_SRC_TRWID(2) | + DMA_CTL_DST_TRWID(2) | + DMA_CTL_INT_EN | + DMA_CTL_LLP_SRCEN | + DMA_CTL_LLP_DSTEN); + } else if (hsdevp->hsdev->dma_channel == 1) {/* DMA channel 1 */ + lli[idx].ctl.low = cpu_to_le32( + DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) | + DMA_CTL_SMS(0) | + DMA_CTL_DMS(2) | + DMA_CTL_SRC_MSIZE(bl) | + DMA_CTL_DST_MSIZE(bl) | + DMA_CTL_DINC_NOCHANGE | + DMA_CTL_SRC_TRWID(2) | + DMA_CTL_DST_TRWID(2) | + DMA_CTL_INT_EN | + DMA_CTL_LLP_SRCEN | + DMA_CTL_LLP_DSTEN); + } + } +#else + if (dir == DMA_FROM_DEVICE) { + lli[idx].dar = cpu_to_le32(addr); + lli[idx].sar = cpu_to_le32((u32)dmadr_addr); + + lli[idx].ctl.low = cpu_to_le32( + DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) | + DMA_CTL_SMS(0) | + DMA_CTL_DMS(1) | + DMA_CTL_SRC_MSIZE(bl) | + DMA_CTL_DST_MSIZE(bl) | + DMA_CTL_SINC_NOCHANGE | + DMA_CTL_SRC_TRWID(2) | + DMA_CTL_DST_TRWID(2) | + DMA_CTL_INT_EN | + DMA_CTL_LLP_SRCEN | + DMA_CTL_LLP_DSTEN); + } else { /* DMA_TO_DEVICE */ + lli[idx].sar = cpu_to_le32(addr); + lli[idx].dar = cpu_to_le32((u32)dmadr_addr); + + lli[idx].ctl.low = cpu_to_le32( + DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) | + DMA_CTL_SMS(1) | + DMA_CTL_DMS(0) | + DMA_CTL_SRC_MSIZE(bl) | + DMA_CTL_DST_MSIZE(bl) | + DMA_CTL_DINC_NOCHANGE | + DMA_CTL_SRC_TRWID(2) | + DMA_CTL_DST_TRWID(2) | + DMA_CTL_INT_EN | + DMA_CTL_LLP_SRCEN | + DMA_CTL_LLP_DSTEN); + } +#endif + dwc_port_vdbg(qc->ap, "%s setting ctl.high len: 0x%08x val: " + "0x%08x\n", __func__, len, + DMA_CTL_BLK_TS(len / 4)); + + /* Program the LLI CTL high register */ + dma_ts = DMA_CTL_BLK_TS(len / 4); + lli[idx].ctl.high = cpu_to_le32(dma_ts); + + /* + *Program the next pointer. The next pointer must be + * the physical address, not the virtual address. + */ + next_llp = (dma_lli + ((idx + 1) * sizeof(struct lli))); + + /* The last 2 bits encode the list master select. */ +#if defined(CONFIG_APM821xx) + next_llp = DMA_LLP_LMS(next_llp, DMA_LLP_AHBMASTER1); +#else + next_llp = DMA_LLP_LMS(next_llp, DMA_LLP_AHBMASTER2); +#endif + + lli[idx].llp = cpu_to_le32(next_llp); + + dwc_port_vdbg(qc->ap, "%s: index %d\n", __func__, idx); + dwc_port_vdbg(qc->ap, "%s setting ctl.high with val: 0x%08x\n", __func__, lli[idx].ctl.high); + dwc_port_vdbg(qc->ap, "%s setting ctl.low with val: 0x%08x\n", __func__, lli[idx].ctl.low); + dwc_port_vdbg(qc->ap, "%s setting lli.dar with val: 0x%08x\n", __func__, lli[idx].dar); + dwc_port_vdbg(qc->ap, "%s setting lli.sar with val: 0x%08x\n", __func__, lli[idx].sar); + dwc_port_vdbg(qc->ap, "%s setting next_llp with val: 0x%08x\n", __func__, lli[idx].llp); + + idx++; + sg_len -= len; + addr += len; + } + } + + /* + * The last next ptr has to be zero and the last control low register + * has to have LLP_SRC_EN and LLP_DST_EN (linked list pointer source + * and destination enable) set back to 0 (disabled.) This is what tells + * the core that this is the last item in the linked list. + */ + if (likely(idx)) { + lli[idx-1].llp = 0x00000000; + lli[idx-1].ctl.low &= DMA_CTL_LLP_DISABLE_LE32; + + /* Flush cache to memory */ + dma_cache_sync(NULL, lli, (sizeof(struct lli) * idx), + DMA_BIDIRECTIONAL); + } + + dwc_port_vdbg(qc->ap, "%s: Final index %d\n", __func__, idx-1); + dwc_port_vdbg(qc->ap, "%s setting ctl.high with val: 0x%08x\n", __func__, lli[idx-1].ctl.high); + dwc_port_vdbg(qc->ap, "%s setting ctl.low with val: 0x%08x\n", __func__, lli[idx-1].ctl.low); + dwc_port_vdbg(qc->ap, "%s setting lli.dar with val: 0x%08x\n", __func__, lli[idx-1].dar); + dwc_port_vdbg(qc->ap, "%s setting lli.sar with val: 0x%08x\n", __func__, lli[idx-1].sar); + dwc_port_vdbg(qc->ap, "%s setting next_llp with val: 0x%08x\n", __func__, lli[idx-1].llp); + + return idx; +} + +/* + * Function: dma_dwc_xfer_start + * arguments: Channel number + * Return : None + * Enables the DMA channel + */ +static void dma_dwc_xfer_start(int dma_ch) +{ + /* Enable the DMA channel */ + out_le32(&(sata_dma_regs->dma_chan_en.low), + in_le32(&(sata_dma_regs->dma_chan_en.low)) | + DMA_ENABLE_CHAN(dma_ch)); + +#if defined(CONFIG_SATA_DWC_VDEBUG) + printk("DMA CFG = 0x%08x\n", in_le32(&(sata_dma_regs->dma_cfg.low))); + printk("%s: setting sata_dma_regs->dma_chan_en.low with val: 0x%08x\n", + __func__, in_le32(&(sata_dma_regs->dma_chan_en.low))); +#endif + + +#if defined(CONFIG_APOLLO3G) + signal_hdd_led(1 /*blink=yes*/, 2 /* _3G_LED_GREEN */); +#endif + +} + +/* + * Check if the selected DMA channel is currently enabled. + */ +static int dma_dwc_channel_enabled(int ch) +{ + u32 dma_chan; + + // Read the DMA channel register + dma_chan = in_le32(&(sata_dma_regs->dma_chan_en.low)); + if (dma_chan & DMA_CHANNEL(ch)) + return 1; + + return 0; +} + +/* + * Terminate the current DMA transaction + */ +static void dma_dwc_terminate_dma(struct ata_port *ap, int dma_ch) +{ + int enabled = dma_dwc_channel_enabled(dma_ch); + + dev_info(ap->dev, "%s terminate DMA on channel=%d enabled=%d\n", + __func__, dma_ch, enabled); + + if (enabled) { + // Disable the selected channel + out_le32(&(sata_dma_regs->dma_chan_en.low), + in_le32(&(sata_dma_regs->dma_chan_en.low)) | DMA_DISABLE_CHAN(dma_ch)); + + // Wait for the channel is disabled + do { + enabled = dma_dwc_channel_enabled(dma_ch); + msleep(10); + } while (enabled); + } +} + + +/* + * Setup data and DMA configuration ready for DMA transfer + */ +static int dma_dwc_xfer_setup(struct ata_queued_cmd *qc, + struct lli *lli, dma_addr_t dma_lli, + void __iomem *addr) +{ + int dma_ch; + int num_lli; + + /* Acquire DMA channel */ + dma_ch = dma_request_channel(qc->ap); + if (unlikely(dma_ch == -1)) { + dev_err(qc->ap->dev, "%s: dma channel unavailable\n", __func__); + return -EAGAIN; + } + dwc_port_vdbg(qc->ap, "%s: Got channel %d\n", __func__, dma_ch); + + /* Convert SG list to linked list of items (LLIs) for AHB DMA */ + num_lli = map_sg_to_lli(qc, lli, dma_lli, addr); + + dwc_port_vdbg(qc->ap, "%s sg: 0x%p, count: %d lli: %p dma_lli: 0x%0xlx addr:" + " %p lli count: %d\n", __func__, qc->sg, qc->n_elem, lli, + (u32)dma_lli, addr, num_lli); + + /* Clear channel interrupts */ + clear_chan_interrupts(dma_ch); + + /* Program the CFG register. */ +#if defined(CONFIG_APM821xx) + if (dma_ch == 0) { + /* Buffer mode enabled, FIFO_MODE=0 */ + out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high), 0x0000009); + /* Channel 0 bit[7:5] */ + out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.low), 0x00000020); + } else if (dma_ch == 1) { + /* Buffer mode enabled, FIFO_MODE=0 */ + out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high), 0x0000088d); + /* Channel 1 bit[7:5] */ + out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.low), 0x00000020); + } +#else + out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high), + DMA_CFG_PROTCTL | DMA_CFG_FCMOD_REQ); + out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.low), 0); +#endif + + /* Program the address of the linked list */ +#if defined(CONFIG_APM821xx) + out_le32(&(sata_dma_regs->chan_regs[dma_ch].llp.low), + DMA_LLP_LMS(dma_lli, DMA_LLP_AHBMASTER1)); +#else + out_le32(&(sata_dma_regs->chan_regs[dma_ch].llp.low), + DMA_LLP_LMS(dma_lli, DMA_LLP_AHBMASTER2)); +#endif + + /* Program the CTL register with src enable / dst enable */ + //out_le32(&(sata_dma_regs->chan_regs[dma_ch].ctl.low), + // DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN); + out_le32(&(sata_dma_regs->chan_regs[dma_ch].ctl.low), 0x18000000); + + dwc_port_vdbg(qc->ap, "%s DMA channel %d is ready\n", __func__, dma_ch); + dwc_port_vdbg(qc->ap, "%s setting cfg.high of channel %d with val: 0x%08x\n", __func__, dma_ch, in_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high))); + dwc_port_vdbg(qc->ap, "%s setting cfg.low of channel %d with val: 0x%08x\n", __func__, dma_ch, in_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.low))); + dwc_port_vdbg(qc->ap, "%s setting llp.low of channel %d with val: 0x%08x\n", __func__, dma_ch, in_le32(&(sata_dma_regs->chan_regs[dma_ch].llp.low))); + dwc_port_vdbg(qc->ap, "%s setting ctl.low of channel %d with val: 0x%08x\n", __func__, dma_ch, in_le32(&(sata_dma_regs->chan_regs[dma_ch].ctl.low))); + + return dma_ch; +} + +/* + * Function: dma_dwc_exit + * arguments: None + * returns status + * This function exits the SATA DMA driver + */ +static void dma_dwc_exit(struct sata_dwc_device *hsdev) +{ + dwc_dev_vdbg(hsdev->dev, "%s:\n", __func__); + if (sata_dma_regs) + iounmap(sata_dma_regs); + + if (hsdev->irq_dma) + free_irq(hsdev->irq_dma, hsdev); +} + +/* + * Function: dma_dwc_init + * arguments: hsdev + * returns status + * This function initializes the SATA DMA driver + */ +static int dma_dwc_init(struct sata_dwc_device *hsdev) +{ + int err; + int irq = hsdev->irq_dma; + + err = dma_request_interrupts(hsdev, irq); + if (err) { + dev_err(hsdev->dev, "%s: dma_request_interrupts returns %d\n", + __func__, err); + goto error_out; + } + + /* Enabe DMA */ + out_le32(&(sata_dma_regs->dma_cfg.low), DMA_EN); + + dev_notice(hsdev->dev, "DMA initialized\n"); + dev_notice(hsdev->dev, "DMA CFG = 0x%08x\n", in_le32(&(sata_dma_regs->dma_cfg.low))); + dwc_dev_vdbg(hsdev->dev, "SATA DMA registers=0x%p\n", sata_dma_regs); + + return 0; + +error_out: + dma_dwc_exit(hsdev); + + return err; +} + + +static void sata_dwc_dev_config(struct ata_device *adev) +{ + /* + * Does not support NCQ over a port multiplier + * (no FIS-based switching). + */ + if (adev->flags & ATA_DFLAG_NCQ) { + /* + * TODO: debug why enabling NCQ makes the linux crashed + * in hot plug after the first hot unplug action. + * --> need to investigate more + */ + adev->flags &= ~ATA_DFLAG_NCQ; + if (sata_pmp_attached(adev->link->ap)) { + adev->flags &= ~ATA_DFLAG_NCQ; + ata_dev_printk(adev, KERN_INFO, + "NCQ disabled for command-based switching\n"); + } + } + + /* + * Since the sata_pmp_error_handler function in libata-pmp + * make FLAG_AN disabled in the first time SATA port is configured. + * Asynchronous notification is not configured. + * This will enable the AN feature manually. + */ + adev->flags |= ATA_DFLAG_AN; +} + + +static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val) +{ + if (unlikely(scr > SCR_NOTIFICATION)) { + dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n", + __func__, scr); + return -EINVAL; + } + + *val = in_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4)); + dwc_dev_vdbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", + __func__, link->ap->print_id, scr, *val); + + return 0; +} + +static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val) +{ + dwc_dev_vdbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", + __func__, link->ap->print_id, scr, val); + if (unlikely(scr > SCR_NOTIFICATION)) { + dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n", + __func__, scr); + return -EINVAL; + } + out_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4), val); + + return 0; +} + +static inline u32 sata_dwc_core_scr_read ( struct ata_port *ap, unsigned int scr) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + return in_le32((void __iomem *)hsdev->scr_base + (scr * 4)); +} + + +static inline void sata_dwc_core_scr_write ( struct ata_port *ap, unsigned int scr, u32 val) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + out_le32((void __iomem *)hsdev->scr_base + (scr * 4), val); +} + +static inline void clear_serror(struct ata_port *ap) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + out_le32( (void __iomem *)hsdev->scr_base + 4, + in_le32((void __iomem *)hsdev->scr_base + 4)); +} + +static inline void clear_intpr(struct sata_dwc_device *hsdev) +{ + out_le32(&hsdev->sata_dwc_regs->intpr, + in_le32(&hsdev->sata_dwc_regs->intpr)); +} + +static inline void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit) +{ + out_le32(&hsdev->sata_dwc_regs->intpr, bit); + // in_le32(&hsdev->sata_dwc_regs->intpr)); +} + + +static inline void enable_err_irq(struct sata_dwc_device *hsdev) +{ + out_le32(&hsdev->sata_dwc_regs->intmr, + in_le32(&hsdev->sata_dwc_regs->intmr) | SATA_DWC_INTMR_ERRM); + out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERR_ERR_BITS); +} + +static inline u32 qcmd_tag_to_mask(u8 tag) +{ + return 0x00000001 << (tag & 0x1f); +} + + +/* + * Timer to monitor SCR_NOTIFICATION registers on the + * SATA port + */ +static void sata_dwc_an_chk(unsigned long arg) +{ + struct ata_port *ap = (void *)arg; + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + unsigned long flags; + int rc = 0x0; + u32 sntf = 0x0; + + spin_lock_irqsave(ap->lock, flags); + rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); + + // If some changes on the SCR4, call asynchronous notification + if ( (rc == 0) & (sntf != 0)) { + dwc_port_dbg(ap, "Call assynchronous notification sntf=0x%08x\n", sntf); + sata_async_notification(ap); + hsdev->an_timer.expires = jiffies + msecs_to_jiffies(8000); + } else { + hsdev->an_timer.expires = jiffies + msecs_to_jiffies(3000); + } + add_timer(&hsdev->an_timer); + spin_unlock_irqrestore(ap->lock, flags); +} + + +/* + * sata_dwc_pmp_select - Set the PMP field in SControl to the specified port number. + * + * @port: The value (port number) to set the PMP field to. + * + * @return: The old value of the PMP field. + */ +static u32 sata_dwc_pmp_select(struct ata_port *ap, u32 port) +{ + u32 scontrol, old_port; + if (sata_pmp_supported(ap)) { + scontrol = sata_dwc_core_scr_read(ap, SCR_CONTROL); + old_port = SCONTROL_TO_PMP(scontrol); + + // Select new PMP port + if ( port != old_port ) { + scontrol &= ~SCONTROL_PMP_MASK; + sata_dwc_core_scr_write(ap, SCR_CONTROL, scontrol | PMP_TO_SCONTROL(port)); + dwc_port_dbg(ap, "%s: old port=%d new port=%d\n", __func__, old_port, port); + } + return old_port; + } + else + return port; +} + +/* + * Get the current PMP port + */ +static inline u32 current_pmp(struct ata_port *ap) +{ + return SCONTROL_TO_PMP(sata_dwc_core_scr_read(ap, SCR_CONTROL)); +} + + +/* + * Process when a PMP card is attached in the SATA port. + * Since our SATA port support command base switching only, + * NCQ will not be available. + * We disable the NCQ feature in SATA port. + */ +static void sata_dwc_pmp_attach ( struct ata_port *ap) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + + dev_info(ap->dev, "Attach SATA port multiplier with %d ports\n", ap->nr_pmp_links); + // Disable NCQ + ap->flags &= ~ATA_FLAG_NCQ; + + // Initialize timer for checking AN + init_timer(&hsdev->an_timer); + hsdev->an_timer.expires = jiffies + msecs_to_jiffies(20000); + hsdev->an_timer.function = sata_dwc_an_chk; + hsdev->an_timer.data = (unsigned long)(ap); + add_timer(&hsdev->an_timer); +} + +/* + * Process when PMP card is removed from the SATA port. + * Re-enable NCQ for using by the SATA drive in the future + */ +static void sata_dwc_pmp_detach ( struct ata_port *ap) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + + dev_info(ap->dev, "Detach SATA port\n"); + // Re-enable NCQ + // TODO: remove the below comment out when NCQ problem fixed + //ap->flags |= ATA_FLAG_NCQ; + + sata_dwc_pmp_select(ap, 0); + + // Delete timer since PMP card is detached + del_timer(&hsdev->an_timer); +} + + + +// Check the link to be ready +int sata_dwc_check_ready ( struct ata_link *link ) { + u8 status; + struct ata_port *ap = link->ap; + status = ioread8(ap->ioaddr.status_addr); + return ata_check_ready(status); +} + + +/* + * Do soft reset on the current SATA link. + */ +static int sata_dwc_softreset(struct ata_link *link, unsigned int *classes, + unsigned long deadline) +{ + int rc; + struct ata_port *ap = link->ap; + struct ata_ioports *ioaddr = &ap->ioaddr; + struct ata_taskfile tf; + + sata_dwc_pmp_select(link->ap, sata_srst_pmp(link)); + + /* Issue bus reset */ + iowrite8(ap->ctl, ioaddr->ctl_addr); + udelay(20); /* FIXME: flush */ + iowrite8(ap->ctl | ATA_SRST, ioaddr->ctl_addr); + udelay(20); /* FIXME: flush */ + iowrite8(ap->ctl, ioaddr->ctl_addr); + ap->last_ctl = ap->ctl; + + /* Always check readiness of the master device */ + rc = ata_wait_after_reset(link, deadline, sata_dwc_check_ready); + + // Classify the ata_port + *classes = ATA_DEV_NONE; + /* Verify if SStatus indicates device presence */ + if (ata_link_online(link)) { + memset(&tf, 0, sizeof(tf)); + ata_sff_tf_read(ap, &tf); + *classes = ata_dev_classify(&tf); + } + + if ( *classes == ATA_DEV_PMP) + dwc_link_dbg(link, "-->found PMP device by sig\n"); + + clear_serror(link->ap); + + return rc; +} + + + + +/* + * sata_dwc_hardreset - Do hardreset the SATA controller + */ +static int sata_dwc_hardreset(struct ata_link *link, unsigned int *classes, + unsigned long deadline) +{ + int rc; + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + bool online; + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(link->ap); + + dwc_link_dbg(link, "%s\n", __func__); + sata_dwc_pmp_select(link->ap, sata_srst_pmp(link)); + dwc_port_vdbg(link->ap, "dmacr=0x%08x\n",in_le32(&(hsdev->sata_dwc_regs->dmacr))); + + // Call standard hard reset + rc = sata_link_hardreset(link, timing, deadline, &online, NULL); + + // Reconfigure the port after hard reset + if ( ata_link_online(link) ) + sata_dwc_init_port(link->ap); + + return online ? -EAGAIN : rc; +} + +/* + * Do hard reset on each PMP link + */ +static int sata_dwc_pmp_hardreset(struct ata_link *link, unsigned int *classes, + unsigned long deadline) +{ + int rc = 0; + sata_dwc_pmp_select(link->ap, sata_srst_pmp(link)); + rc = sata_std_hardreset(link, classes, deadline); + return rc; +} + +/* See ahci.c */ +/* + * Process error when the SATAn_INTPR's ERR bit is set + * The processing is based on SCR_ERROR register content + */ +static void sata_dwc_error_intr(struct ata_port *ap, + struct sata_dwc_device *hsdev, uint intpr) +{ + struct ata_eh_info *ehi; + struct ata_link *link; + struct ata_queued_cmd *active_qc = NULL; + u32 serror; + bool freeze = false, abort = false; + int pmp, ret; + unsigned int err_mask = 0, action = 0; +#if defined(CONFIG_SATA_DWC_VDEBUG) + int dma_chan = hsdev->dma_channel; +#endif + + link = &ap->link; + ehi = &link->eh_info; + + /* Record irq stat */ + ata_ehi_clear_desc(ehi); + ata_ehi_push_desc(ehi, "irq_stat 0x%08x", intpr); + + // Record SERROR + serror = sata_dwc_core_scr_read(ap, SCR_ERROR); + dwc_port_dbg(ap, "%s serror = 0x%08x\n", __func__, serror); + + // Clear SERROR and interrupt bit + clear_serror(ap); + clear_intpr(hsdev); + + // Print out for test only + if ( serror ) { + dwc_port_info(ap, "Detect errors:"); + if ( serror & SATA_DWC_SERR_ERRI ) + printk(" ERRI"); + if ( serror & SATA_DWC_SERR_ERRM ) + printk(" ERRM"); + if ( serror & SATA_DWC_SERR_ERRT ) + printk(" ERRT"); + if ( serror & SATA_DWC_SERR_ERRC ) + printk(" ERRC"); + if ( serror & SATA_DWC_SERR_ERRP ) + printk(" ERRP"); + if ( serror & SATA_DWC_SERR_ERRE ) + printk(" ERRE"); + if ( serror & SATA_DWC_SERR_DIAGN ) + printk(" DIAGN"); + if ( serror & SATA_DWC_SERR_DIAGI ) + printk(" DIAGI"); + if ( serror & SATA_DWC_SERR_DIAGW ) + printk(" DIAGW"); + if ( serror & SATA_DWC_SERR_DIAGB ) + printk(" DIAGB"); + if ( serror & SATA_DWC_SERR_DIAGT ) + printk(" DIAGT"); + if ( serror & SATA_DWC_SERR_DIAGC ) + printk(" DIAGC"); + if ( serror & SATA_DWC_SERR_DIAGH ) + printk(" DIAGH"); + if ( serror & SATA_DWC_SERR_DIAGL ) + printk(" DIAGL"); + if ( serror & SATA_DWC_SERR_DIAGS ) + printk(" DIAGS"); + if ( serror & SATA_DWC_SERR_DIAGF ) + printk(" DIAGF"); + if ( serror & SATA_DWC_SERR_DIAGX ) + printk(" DIAGX"); + if ( serror & SATA_DWC_SERR_DIAGA ) + printk(" DIAGA"); + printk("\n"); + } + +#if defined(CONFIG_SATA_DWC_VDEBUG) + printk("%s reading cfg.high of channel %d with val: 0x%08x\n", __func__, dma_chan, in_le32(&(sata_dma_regs->chan_regs[dma_chan].cfg.high))); + printk("%s reading cfg.low of channel %d with val: 0x%08x\n", __func__, dma_chan, in_le32(&(sata_dma_regs->chan_regs[dma_chan].cfg.low))); + printk("%s reading llp.low of channel %d with val: 0x%08x\n", __func__, dma_chan, in_le32(&(sata_dma_regs->chan_regs[dma_chan].llp.low))); + printk("%s reading ctl.low of channel %d with val: 0x%08x\n", __func__, dma_chan, in_le32(&(sata_dma_regs->chan_regs[dma_chan].ctl.low))); + printk("%s reading sar.low of channel %d with val: 0x%08x\n", __func__, dma_chan, in_le32(&(sata_dma_regs->chan_regs[dma_chan].sar.low))); + printk("%s reading sar.high of channel %d with val: 0x%08x\n", __func__, dma_chan, in_le32(&(sata_dma_regs->chan_regs[dma_chan].sar.high))); + printk("%s reading dar.low of channel %d with val: 0x%08x\n", __func__, dma_chan, in_le32(&(sata_dma_regs->chan_regs[dma_chan].dar.low))); + printk("%s reading dar.high of channel %d with val: 0x%08x\n", __func__, dma_chan, in_le32(&(sata_dma_regs->chan_regs[dma_chan].dar.high))); +#endif + + // Process hotplug for SATA port + if ( serror & (SATA_DWC_SERR_DIAGX | SATA_DWC_SERR_DIAGW)) { + dwc_port_info(ap, "Detect hot plug signal\n"); + ata_ehi_hotplugged(ehi); + ata_ehi_push_desc(ehi, serror & SATA_DWC_SERR_DIAGN ? "PHY RDY changed" : "device exchanged"); + freeze = true; + } + + // Process PHY internal error / Link sequence (illegal transition) error + if ( serror & (SATA_DWC_SERR_DIAGI | SATA_DWC_SERR_DIAGL)) { + ehi->err_mask |= AC_ERR_HSM; + ehi->action |= ATA_EH_RESET; + freeze = true; + } + + // Process Internal host adapter error + if ( serror & SATA_DWC_SERR_ERRE ) { + dev_err(ap->dev, "Detect Internal host adapter error\n"); + // --> need to review + ehi->err_mask |= AC_ERR_HOST_BUS; + ehi->action |= ATA_EH_RESET; + freeze = true; + } + + // Process Protocol Error + if ( serror & SATA_DWC_SERR_ERRP ) { + dev_err(ap->dev, "Detect Protocol error\n"); + ehi->err_mask |= AC_ERR_HSM; + ehi->action |= ATA_EH_RESET; + freeze = true; + } + + // Process non-recovered persistent communication error + if ( serror & SATA_DWC_SERR_ERRC ) { + dev_err(ap->dev, "Detect non-recovered persistent communication error\n"); + // --> TODO: review processing error + ehi->err_mask |= AC_ERR_ATA_BUS; + ehi->action |= ATA_EH_SOFTRESET; + //ehi->flags |= ATA_EHI_NO_AUTOPSY; + //freeze = true; + } + + // Non-recovered transient data integrity error + if ( serror & SATA_DWC_SERR_ERRT ) { + dev_err(ap->dev, "Detect non-recovered transient data integrity error\n"); + ehi->err_mask |= AC_ERR_ATA_BUS; + //ehi->err_mask |= AC_ERR_DEV; + ehi->action |= ATA_EH_SOFTRESET; + //ehi->flags |= ATA_EHI_NO_AUTOPSY; + } + + // Since below errors have been recovered by hardware + // they don't need any error processing. + if ( serror & SATA_DWC_SERR_ERRM ) { + dev_warn(ap->dev, "Detect recovered communication error"); + } + if ( serror & SATA_DWC_SERR_ERRI ) { + dev_warn(ap->dev, "Detect recovered data integrity error"); + } + + // If any error occur, process the qc + if (serror & (SATA_DWC_SERR_ERRT | SATA_DWC_SERR_ERRC)) { + //if (serror & 0x03f60f0) { + abort = true; + /* find out the offending link and qc */ + if (sata_pmp_attached(ap)) { + pmp = current_pmp(ap); + // If we are working on the PMP port + if ( pmp < ap->nr_pmp_links ) { + link = &ap->pmp_link[pmp]; + ehi = &link->eh_info; + active_qc = ata_qc_from_tag(ap, link->active_tag); + err_mask |= AC_ERR_DEV; + ata_ehi_clear_desc(ehi); + ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat); + } else { + err_mask |= AC_ERR_HSM; + action |= ATA_EH_RESET; + freeze = true; + } + + } + // Work on SATA port + else { + freeze = true; + active_qc = ata_qc_from_tag(ap, link->active_tag); + } + + if ( active_qc) { + active_qc->err_mask |= err_mask; + } else { + ehi->err_mask = err_mask; + } + } + + if ( freeze | abort ) { + //sata_dwc_qc_complete(ap, active_qc, 1); + // Terminate DMA channel if it is currenly in use + if ( dma_request_channel(ap) != -1 ) { + dwc_port_dbg(ap, "Terminate DMA channel %d for handling error\n", hsdev->dma_channel); + dma_dwc_terminate_dma(ap, hsdev->dma_channel); + } + } + + if (freeze) { + ret = ata_port_freeze(ap); + ata_port_printk(ap, KERN_INFO, "Freeze port with %d QCs aborted\n", ret); + } + else if (abort) { + if (active_qc) { + ret = ata_link_abort(active_qc->dev->link); + ata_link_printk(link, KERN_INFO, "Abort %d QCs\n", ret); + } else { + ret = ata_port_abort(ap); + ata_port_printk(ap, KERN_INFO, "Abort %d QCs on the SATA port\n", ret); + } + } +} + + +/* + * Function : sata_dwc_isr + * arguments : irq, void *dev_instance, struct pt_regs *regs + * Return value : irqreturn_t - status of IRQ + * This Interrupt handler called via port ops registered function. + * .irq_handler = sata_dwc_isr + */ +static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) +{ + struct ata_host *host = (struct ata_host *)dev_instance; + struct sata_dwc_device *hsdev = HSDEV_FROM_HOST(host); + struct ata_port *ap; + struct ata_queued_cmd *qc; + unsigned long flags; + u8 status, tag; + int handled, num_processed, port = 0; + u32 intpr, sactive, sactive2, tag_mask; + struct sata_dwc_device_port *hsdevp; + + spin_lock_irqsave(&host->lock, flags); + + /* Read the interrupt register */ + intpr = in_le32(&hsdev->sata_dwc_regs->intpr); + + ap = host->ports[port]; + hsdevp = HSDEVP_FROM_AP(ap); + + dwc_port_dbg(ap,"%s\n",__func__); + if ( intpr != 0x80000080) + dwc_port_dbg(ap, "%s intpr=0x%08x active_tag=%d\n", __func__, intpr, ap->link.active_tag); + //dwc_port_dbg(ap, "%s: INTMR=0x%08x, ERRMR=0x%08x\n", __func__, in_le32(&hsdev->sata_dwc_regs->intmr), in_le32(&hsdev->sata_dwc_regs->errmr)); + + /* Check for error interrupt */ + if (intpr & SATA_DWC_INTPR_ERR) { + sata_dwc_error_intr(ap, hsdev, intpr); + handled = 1; +#if defined(CONFIG_APOLLO3G) + signal_hdd_led(0 /*off blink*/, 1 /*red color*/); +#endif + goto done_irqrestore; + } + + /* Check for DMA SETUP FIS (FP DMA) interrupt */ + if (intpr & SATA_DWC_INTPR_NEWFP) { + dwc_port_dbg(ap, "%s: NEWFP INTERRUPT in HSDEV with DMA channel %d\n", __func__, hsdev->dma_channel); + clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP); + + tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr)); + dwc_dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag); + if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PENDING) + dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag); + + hsdevp->sata_dwc_sactive_issued |= qcmd_tag_to_mask(tag); + + qc = ata_qc_from_tag(ap, tag); + /* + * Start FP DMA for NCQ command. At this point the tag is the + * active tag. It is the tag that matches the command about to + * be completed. + */ + qc->ap->link.active_tag = tag; + sata_dwc_bmdma_start_by_tag(qc, tag); + qc->ap->hsm_task_state = HSM_ST_LAST; + + handled = 1; + goto done_irqrestore; + } + + sactive = sata_dwc_core_scr_read(ap, SCR_ACTIVE); + tag_mask = (hsdevp->sata_dwc_sactive_issued | sactive) ^ sactive; + + /* If no sactive issued and tag_mask is zero then this is not NCQ */ + if (hsdevp->sata_dwc_sactive_issued == 0 && tag_mask == 0) { + if (ap->link.active_tag == ATA_TAG_POISON) + tag = 0; + else + tag = ap->link.active_tag; + qc = ata_qc_from_tag(ap, tag); + + /* DEV interrupt w/ no active qc? */ + if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) { + dev_err(ap->dev, "%s intr with no active qc qc=%p\n", + __func__, qc); + ap->ops->sff_check_status(ap); + handled = 1; + goto done_irqrestore; + } + + status = ap->ops->sff_check_status(ap); + + qc->ap->link.active_tag = tag; + hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT; + + if (status & ATA_ERR) { + dwc_dev_dbg(ap->dev, "interrupt ATA_ERR (0x%x)\n", status); + sata_dwc_qc_complete(ap, qc, 1); + handled = 1; + goto done_irqrestore; + } + + dwc_dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n", + __func__, prot_2_txt(qc->tf.protocol)); +drv_still_busy: + if (ata_is_dma(qc->tf.protocol)) { + int dma_flag = hsdevp->dma_pending[tag]; + /* + * Each DMA transaction produces 2 interrupts. The DMAC + * transfer complete interrupt and the SATA controller + * operation done interrupt. The command should be + * completed only after both interrupts are seen. + */ + hsdevp->dma_interrupt_count++; + if (unlikely(dma_flag == SATA_DWC_DMA_PENDING_NONE)) { + dev_err(ap->dev, "%s: DMA not pending " + "intpr=0x%08x status=0x%08x pend=%d\n", + __func__, intpr, status, dma_flag); + } + + if ((hsdevp->dma_interrupt_count % 2) == 0) + sata_dwc_dma_xfer_complete(ap, 1); + } else if (ata_is_pio(qc->tf.protocol)) { + ata_sff_hsm_move(ap, qc, status, 0); + handled = 1; + goto done_irqrestore; + } else { + if (unlikely(sata_dwc_qc_complete(ap, qc, 1))) + goto drv_still_busy; + } + + handled = 1; + goto done_irqrestore; + } + + /* + * This is a NCQ command. At this point we need to figure out for which + * tags we have gotten a completion interrupt. One interrupt may serve + * as completion for more than one operation when commands are queued + * (NCQ). We need to process each completed command. + */ + +process_cmd: /* process completed commands */ + sactive = sata_dwc_core_scr_read(ap, SCR_ACTIVE); + tag_mask = (hsdevp->sata_dwc_sactive_issued | sactive) ^ sactive; + + if (sactive != 0 || hsdevp->sata_dwc_sactive_issued > 1 || tag_mask > 1) { + dwc_dev_dbg(ap->dev, "%s NCQ: sactive=0x%08x sactive_issued=0x%08x" + " tag_mask=0x%08x\n", __func__, sactive, + hsdevp->sata_dwc_sactive_issued, tag_mask); + } + + if (unlikely((tag_mask | hsdevp->sata_dwc_sactive_issued) != hsdevp->sata_dwc_sactive_issued)) { + dev_warn(ap->dev, "Bad tag mask? sactive=0x%08x " + "sata_dwc_sactive_issued=0x%08x tag_mask=0x%08x\n", + sactive, hsdevp->sata_dwc_sactive_issued, tag_mask); + } + + /* read just to clear ... not bad if currently still busy */ + status = ap->ops->sff_check_status(ap); + dwc_dev_dbg(ap->dev, "%s ATA status register=0x%x, tag_mask=0x%x\n", __func__, status, tag_mask); + + tag = 0; + num_processed = 0; + while (tag_mask) { + num_processed++; + while (!(tag_mask & 0x00000001)) { + tag++; + tag_mask <<= 1; + } + tag_mask &= (~0x00000001); + qc = ata_qc_from_tag(ap, tag); + + /* To be picked up by completion functions */ + qc->ap->link.active_tag = tag; + hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT; + + /* Let libata/scsi layers handle error */ + if (unlikely(status & ATA_ERR)) { + dwc_dev_vdbg(ap->dev, "%s ATA_ERR (0x%x)\n", + __func__, status); + + sata_dwc_qc_complete(ap, qc, 1); + handled = 1; + goto done_irqrestore; + } + + /* Process completed command */ + dwc_dev_dbg(ap->dev, "%s NCQ command, protocol: %s\n", __func__, + prot_2_txt(qc->tf.protocol)); + if (ata_is_dma(qc->tf.protocol)) { + hsdevp->dma_interrupt_count++; + if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_NONE) + dev_warn(ap->dev, + "%s: DMA not pending?\n", __func__); + if ((hsdevp->dma_interrupt_count % 2) == 0) + sata_dwc_dma_xfer_complete(ap, 1); + } else { + if (unlikely(sata_dwc_qc_complete(ap, qc, 1))) + goto still_busy; + } + continue; + +still_busy: + ap->stats.idle_irq++; + dev_warn(ap->dev, "STILL BUSY IRQ ata%d: irq trap\n", + ap->print_id); + } /* while tag_mask */ + + /* + * Check to see if any commands completed while we were processing our + * initial set of completed commands (reading of status clears + * interrupts, so we might miss a completed command interrupt if one + * came in while we were processing: + * we read status as part of processing a completed command). + */ + sactive2 = sata_dwc_core_scr_read(ap, SCR_ACTIVE); + if (sactive2 != sactive) { + dwc_dev_dbg(ap->dev, "More finished - sactive=0x%x sactive2=0x%x\n", + sactive, sactive2); + goto process_cmd; + } + handled = 1; + +done_irqrestore: + spin_unlock_irqrestore(&host->lock, flags); +#if defined(CONFIG_APOLLO3G) + signal_hdd_led(0 /*off blink*/, -1 /* no color */); +#endif + return IRQ_RETVAL(handled); +} + + +/* + * Clear DMA Control Register after completing transferring data + * using AHB DMA. + */ +static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_HSDEVP(hsdevp); + + if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_RX) { + // Clear receive channel enable bit + out_le32(&(hsdev->sata_dwc_regs->dmacr), + SATA_DWC_DMACR_RX_CLEAR( + in_le32(&(hsdev->sata_dwc_regs->dmacr)))); + } else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX) { + // Clear transmit channel enable bit + out_le32(&(hsdev->sata_dwc_regs->dmacr), + SATA_DWC_DMACR_TX_CLEAR( + in_le32(&(hsdev->sata_dwc_regs->dmacr)))); + } else { + /* + * This should not happen, it indicates the driver is out of + * sync. If it does happen, clear dmacr anyway. + */ + dev_err(hsdev->dev, "%s DMA protocol RX and TX DMA not pending " + "tag=0x%02x pending=%d dmacr: 0x%08x\n", + __func__, tag, hsdevp->dma_pending[tag], + in_le32(&(hsdev->sata_dwc_regs->dmacr))); + + // Clear all transmit and receive bit, but TXMOD bit is set to 1 + out_le32(&(hsdev->sata_dwc_regs->dmacr), + SATA_DWC_DMACR_TXRXCH_CLEAR); + } +} + +/* + * + */ +static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status) +{ + struct ata_queued_cmd *qc; + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + u8 tag = 0; + + tag = ap->link.active_tag; + qc = ata_qc_from_tag(ap, tag); + +#ifdef DEBUG_NCQ + if (tag > 0) { + dev_info(ap->dev, "%s tag=%u cmd=0x%02x dma dir=%s proto=%s " + "dmacr=0x%08x\n", __func__, qc->tag, qc->tf.command, + dir_2_txt(qc->dma_dir), prot_2_txt(qc->tf.protocol), + in_le32(&(hsdev->sata_dwc_regs->dmacr))); + } +#endif + + if (ata_is_dma(qc->tf.protocol)) { + // DMA out of sync error + if (unlikely(hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_NONE)) { + dev_err(ap->dev, "%s DMA protocol RX and TX DMA not " + "pending dmacr: 0x%08x\n", __func__, + in_le32(&(hsdev->sata_dwc_regs->dmacr))); + } + + hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_NONE; + sata_dwc_qc_complete(ap, qc, check_status); + ap->link.active_tag = ATA_TAG_POISON; + } else { + sata_dwc_qc_complete(ap, qc, check_status); + } +} + +/* + * + */ +static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc, + u32 check_status) +{ + u8 status = 0; + int i = 0; + u32 mask = 0x0; + u8 tag = qc->tag; + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + u32 serror; + int dma_ch; + + dwc_dev_vdbg(ap->dev, "%s checkstatus? %x\n", __func__, check_status); + + if (unlikely(hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX)) + dev_err(ap->dev, "TX DMA PENDINGING\n"); + else if (unlikely(hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_RX)) + dev_err(ap->dev, "RX DMA PENDINGING\n"); + + if (check_status) { + i = 0; + do { + /* check main status, clearing INTRQ */ + status = ap->ops->sff_check_status(ap); + if (status & ATA_BUSY) { + dwc_dev_vdbg(ap->dev, "STATUS BUSY (0x%02x) [%d]\n", + status, i); + } + if (++i > 10) + break; + } while (status & ATA_BUSY); + + status = ap->ops->sff_check_status(ap); + if (unlikely(status & ATA_BUSY)) + dev_err(ap->dev, "QC complete cmd=0x%02x STATUS BUSY " + "(0x%02x) [%d]\n", qc->tf.command, status, i); + + + // Check error ==> need to process error here + serror = sata_dwc_core_scr_read(ap, SCR_ERROR); + if (unlikely(serror & SATA_DWC_SERR_ERR_BITS)) + { + dev_err(ap->dev, "****** SERROR=0x%08x ******\n", serror); + ap->link.eh_context.i.action |= ATA_EH_RESET; + if (ata_is_dma(qc->tf.protocol)) { + dma_ch = hsdevp->dma_chan[tag]; + dma_dwc_terminate_dma(ap, dma_ch); + } else { + dma_ch = hsdevp->dma_chan[0]; + dma_dwc_terminate_dma(ap, dma_ch); + } + } + } + dwc_dev_vdbg(ap->dev, "QC complete cmd=0x%02x status=0x%02x ata%u: " + "protocol=%d\n", qc->tf.command, status, ap->print_id, + qc->tf.protocol); + + /* clear active bit */ + mask = (~(qcmd_tag_to_mask(tag))); + hsdevp->sata_dwc_sactive_queued = hsdevp->sata_dwc_sactive_queued & mask; + hsdevp->sata_dwc_sactive_issued = hsdevp->sata_dwc_sactive_issued & mask; + dwc_port_vdbg(ap, "%s - sata_dwc_sactive_queued=0x%08x, sata_dwc_sactive_issued=0x%08x\n",__func__, hsdevp->sata_dwc_sactive_queued, hsdevp->sata_dwc_sactive_issued); + dwc_port_vdbg(ap, "dmacr=0x%08x\n",in_le32(&(hsdev->sata_dwc_regs->dmacr))); + + /* Complete taskfile transaction (does not read SCR registers) */ + ata_qc_complete(qc); + + return 0; +} + +/* + * Clear interrupt and error flags in DMA status register. + */ +void sata_dwc_irq_clear (struct ata_port *ap) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + dwc_port_dbg(ap,"%s\n",__func__); + + // Clear DMA interrupts + clear_chan_interrupts(hsdev->dma_channel); + //sata_dma_regs + //out_le32(&hsdev->sata_dwc_regs->intmr, + // in_le32(&hsdev->sata_dwc_regs->intmr) & ~SATA_DWC_INTMR_ERRM); + //out_le32(&hsdev->sata_dwc_regs->errmr, 0x0); + //sata_dwc_check_status(ap); +} + +/* + * Turn on IRQ + */ +void sata_dwc_irq_on(struct ata_port *ap) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + u8 tmp; + + dwc_port_dbg(ap,"%s\n",__func__); + ap->ctl &= ~ATA_NIEN; + ap->last_ctl = ap->ctl; + + if (ioaddr->ctl_addr) + iowrite8(ap->ctl, ioaddr->ctl_addr); + tmp = ata_wait_idle(ap); + + ap->ops->sff_irq_clear(ap); + enable_err_irq(hsdev); +} + + +/* + * This function enables the interrupts in IMR and unmasks them in ERRMR + * + */ +static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev) +{ + // Enable interrupts + out_le32(&hsdev->sata_dwc_regs->intmr, + SATA_DWC_INTMR_ERRM | + SATA_DWC_INTMR_NEWFPM | + SATA_DWC_INTMR_PMABRTM | + SATA_DWC_INTMR_DMATM); + + /* + * Unmask the error bits that should trigger an error interrupt by + * setting the error mask register. + */ + out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERR_ERR_BITS); + + dwc_dev_dbg(hsdev->dev, "%s: INTMR = 0x%08x, ERRMR = 0x%08x\n", __func__, + in_le32(&hsdev->sata_dwc_regs->intmr), + in_le32(&hsdev->sata_dwc_regs->errmr)); +} + +/* + * Configure DMA and interrupts on SATA port. This should be called after + * hardreset is executed on the SATA port. + */ +static void sata_dwc_init_port ( struct ata_port *ap ) { + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + + // Configure DMA + if (ap->port_no == 0) { + dwc_dev_dbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n", + __func__); + + // Clear all transmit/receive bits + out_le32(&hsdev->sata_dwc_regs->dmacr, + SATA_DWC_DMACR_TXRXCH_CLEAR); + + dwc_dev_dbg(ap->dev, "%s: setting burst size in DBTSR\n", __func__); + out_le32(&hsdev->sata_dwc_regs->dbtsr, + (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | + SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT))); + } + + // Enable interrupts + sata_dwc_enable_interrupts(hsdev); +} + + +/* + * Setup SATA ioport with corresponding register addresses + */ +static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base) +{ + port->cmd_addr = (void *)base + 0x00; + port->data_addr = (void *)base + 0x00; + + port->error_addr = (void *)base + 0x04; + port->feature_addr = (void *)base + 0x04; + + port->nsect_addr = (void *)base + 0x08; + + port->lbal_addr = (void *)base + 0x0c; + port->lbam_addr = (void *)base + 0x10; + port->lbah_addr = (void *)base + 0x14; + + port->device_addr = (void *)base + 0x18; + port->command_addr = (void *)base + 0x1c; + port->status_addr = (void *)base + 0x1c; + + port->altstatus_addr = (void *)base + 0x20; + port->ctl_addr = (void *)base + 0x20; +} + + +/* + * Function : sata_dwc_port_start + * arguments : struct ata_ioports *port + * Return value : returns 0 if success, error code otherwise + * This function allocates the scatter gather LLI table for AHB DMA + */ +static int sata_dwc_port_start(struct ata_port *ap) +{ + int err = 0; + struct sata_dwc_device *hsdev; + struct sata_dwc_device_port *hsdevp = NULL; + struct device *pdev; + u32 sstatus; + int i; + + hsdev = HSDEV_FROM_AP(ap); + + dwc_dev_dbg(ap->dev, "%s: port_no=%d\n", __func__, ap->port_no); + + hsdev->host = ap->host; + pdev = ap->host->dev; + if (!pdev) { + dev_err(ap->dev, "%s: no ap->host->dev\n", __func__); + err = -ENODEV; + goto cleanup_exit; + } + + /* Allocate Port Struct */ + hsdevp = kzalloc(sizeof(*hsdevp), GFP_KERNEL); + if (!hsdevp) { + dev_err(ap->dev, "%s: kmalloc failed for hsdevp\n", __func__); + err = -ENOMEM; + goto cleanup_exit; + } + hsdevp->hsdev = hsdev; + + for (i = 0; i < SATA_DWC_QCMD_MAX; i++) + hsdevp->cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT; + + ap->bmdma_prd = 0; /* set these so libata doesn't use them */ + ap->bmdma_prd_dma = 0; + + /* + * DMA - Assign scatter gather LLI table. We can't use the libata + * version since it's PRD is IDE PCI specific. + */ + for (i = 0; i < SATA_DWC_QCMD_MAX; i++) { + hsdevp->llit[i] = dma_alloc_coherent(pdev, + SATA_DWC_DMAC_LLI_TBL_SZ, + &(hsdevp->llit_dma[i]), + GFP_ATOMIC); + if (!hsdevp->llit[i]) { + dev_err(ap->dev, "%s: dma_alloc_coherent failed size " + "0x%x\n", __func__, SATA_DWC_DMAC_LLI_TBL_SZ); + err = -ENOMEM; + goto cleanup_exit; + } + } + + if (ap->port_no == 0) { + dwc_dev_vdbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n", + __func__); + + out_le32(&hsdev->sata_dwc_regs->dmacr, + SATA_DWC_DMACR_TXRXCH_CLEAR); + + dwc_dev_vdbg(ap->dev, "%s: setting burst size in DBTSR\n", __func__); + out_le32(&hsdev->sata_dwc_regs->dbtsr, + (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | + SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT))); + ata_port_printk(ap, KERN_INFO, "%s: setting burst size in DBTSR: 0x%08x\n", + __func__, in_le32(&hsdev->sata_dwc_regs->dbtsr)); + } + + /* Clear any error bits before libata starts issuing commands */ + clear_serror(ap); + + ap->private_data = hsdevp; + + /* Are we in Gen I or II */ + sstatus = sata_dwc_core_scr_read(ap, SCR_STATUS); + switch (SATA_DWC_SCR0_SPD_GET(sstatus)) { + case 0x0: + dev_info(ap->dev, "**** No neg speed (nothing attached?) \n"); + break; + case 0x1: + dev_info(ap->dev, "**** GEN I speed rate negotiated \n"); + break; + case 0x2: + dev_info(ap->dev, "**** GEN II speed rate negotiated \n"); + break; + } + +cleanup_exit: + if (err) { + kfree(hsdevp); + sata_dwc_port_stop(ap); + dwc_dev_vdbg(ap->dev, "%s: fail\n", __func__); + } else { + dwc_dev_vdbg(ap->dev, "%s: done\n", __func__); + } + + return err; +} + + +static void sata_dwc_port_stop(struct ata_port *ap) +{ + int i; + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + + dwc_port_dbg(ap, "%s: stop port\n", __func__); + + if (hsdevp && hsdev) { + /* deallocate LLI table */ + for (i = 0; i < SATA_DWC_QCMD_MAX; i++) { + dma_free_coherent(ap->host->dev, + SATA_DWC_DMAC_LLI_TBL_SZ, + hsdevp->llit[i], hsdevp->llit_dma[i]); + } + + kfree(hsdevp); + } + ap->private_data = NULL; +} + +/* + * Since the SATA DWC is master only. The dev select operation will + * be removed. + */ +void sata_dwc_dev_select(struct ata_port *ap, unsigned int device) +{ + // Do nothing + ndelay(100); +} + +/* + * Function : sata_dwc_exec_command_by_tag + * arguments : ata_port *ap, ata_taskfile *tf, u8 tag, u32 cmd_issued + * Return value : None + * This function keeps track of individual command tag ids and calls + * ata_exec_command in libata + */ +static void sata_dwc_exec_command_by_tag(struct ata_port *ap, + struct ata_taskfile *tf, + u8 tag, u32 cmd_issued) +{ + unsigned long flags; + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + + dwc_dev_dbg(ap->dev, "%s cmd(0x%02x): %s tag=%d, ap->link->tag=0x%08x\n", __func__, tf->command, + ata_cmd_2_txt(tf), tag, ap->link.active_tag); + + spin_lock_irqsave(&ap->host->lock, flags); + hsdevp->cmd_issued[tag] = cmd_issued; + spin_unlock_irqrestore(&ap->host->lock, flags); + + /* + * Clear SError before executing a new command. + * + * TODO if we read a PM's registers now, we will throw away the task + * file values loaded into the shadow registers for this command. + * + * sata_dwc_scr_write and read can not be used here. Clearing the PM + * managed SError register for the disk needs to be done before the + * task file is loaded. + */ + clear_serror(ap); + ata_sff_exec_command(ap, tf); +} + +static void sata_dwc_bmdma_setup_by_tag(struct ata_queued_cmd *qc, u8 tag) +{ + sata_dwc_exec_command_by_tag(qc->ap, &qc->tf, tag, + SATA_DWC_CMD_ISSUED_PENDING); +} + +static void sata_dwc_bmdma_setup(struct ata_queued_cmd *qc) +{ + u8 tag = qc->tag; + + dwc_port_dbg(qc->ap, "%s\n", __func__); + if (ata_is_ncq(qc->tf.protocol)) { + dwc_dev_vdbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n", + __func__, qc->ap->link.sactive, tag); + } else { + tag = 0; + } + + sata_dwc_bmdma_setup_by_tag(qc, tag); +} + +static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag) +{ + volatile int start_dma; + u32 reg, dma_chan; + struct sata_dwc_device *hsdev = HSDEV_FROM_QC(qc); + struct ata_port *ap = qc->ap; + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + int dir = qc->dma_dir; + dma_chan = hsdevp->dma_chan[tag]; + + /* Used for ata_bmdma_start(qc) -- we are not BMDMA compatible */ + + if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_NOT) { + start_dma = 1; + if (dir == DMA_TO_DEVICE) + hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_TX; + else + hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_RX; + } else { + dev_err(ap->dev, "%s: Command not pending cmd_issued=%d " + "(tag=%d) - DMA NOT started\n", __func__, + hsdevp->cmd_issued[tag], tag); + start_dma = 0; + } + + dwc_dev_dbg(ap->dev, "%s qc=%p tag: %x cmd: 0x%02x dma_dir: %s " + "start_dma? %x\n", __func__, qc, tag, qc->tf.command, + dir_2_txt(qc->dma_dir), start_dma); + sata_dwc_tf_dump(hsdev->dev, &(qc->tf)); + + // Start DMA transfer + if (start_dma) { + reg = sata_dwc_core_scr_read(ap, SCR_ERROR); + if (unlikely(reg & SATA_DWC_SERR_ERR_BITS)) { + dev_err(ap->dev, "%s: ****** SError=0x%08x ******\n", + __func__, reg); + //sata_async_notification(ap); + //return; + } + + // Set DMA control registers + if (dir == DMA_TO_DEVICE) + out_le32(&hsdev->sata_dwc_regs->dmacr, + SATA_DWC_DMACR_TXCHEN); + else + out_le32(&hsdev->sata_dwc_regs->dmacr, + SATA_DWC_DMACR_RXCHEN); + + dwc_dev_vdbg(ap->dev, "%s: setting DMACR: 0x%08x\n", __func__, in_le32(&hsdev->sata_dwc_regs->dmacr)); + /* Enable AHB DMA transfer on the specified channel */ + dma_dwc_xfer_start(dma_chan); + } +} + + +static void sata_dwc_bmdma_start(struct ata_queued_cmd *qc) +{ + u8 tag = qc->tag; + + if (ata_is_ncq(qc->tf.protocol)) { + dwc_dev_vdbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n", + __func__, qc->ap->link.sactive, tag); + } else { + tag = 0; + } + + dwc_port_dbg(qc->ap, "%s, tag=0x%08x\n", __func__, tag); + sata_dwc_bmdma_start_by_tag(qc, tag); +} + +/* + * Function : sata_dwc_qc_prep_by_tag + * arguments : ata_queued_cmd *qc, u8 tag + * Return value : None + * qc_prep for a particular queued command based on tag + */ +static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag) +{ + struct ata_port *ap = qc->ap; + int dma_chan; + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + int dir; + + // DMA direction + dir = qc->dma_dir; + + if ((dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO)) + return; + + dwc_dev_vdbg(ap->dev, "%s: port=%d dma dir=%s n_elem=%d\n", + __func__, ap->port_no, dir_2_txt(dir), qc->n_elem); + + // Setup DMA for transfer + dma_chan = dma_dwc_xfer_setup(qc, hsdevp->llit[tag], + hsdevp->llit_dma[tag], + (void *__iomem)(&hsdev->sata_dwc_regs->dmadr)); + + if (unlikely(dma_chan < 0)) { + dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns err %d\n", + __func__, dma_chan); + return; + } + + hsdevp->dma_chan[tag] = dma_chan; +} + + + +/** + * ata_sff_exec_command - issue ATA command to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Issues ATA command, with proper synchronization with interrupt + * handler / other threads. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void sata_dwc_exec_command(struct ata_port *ap, const struct ata_taskfile *tf) +{ + iowrite8(tf->command, ap->ioaddr.command_addr); + /* If we have an mmio device with no ctl and no altstatus + * method this will fail. No such devices are known to exist. + */ + if (ap->ioaddr.altstatus_addr) + ioread8(ap->ioaddr.altstatus_addr); + ndelay(400); +} + +/** + * sata_dwc_tf_to_host - issue ATA taskfile to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Issues ATA taskfile register set to ATA host controller, + * with proper synchronization with interrupt handler and + * other threads. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +static inline void sata_dwc_tf_to_host(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + dwc_port_dbg(ap,"%s\n",__func__); + ap->ops->sff_tf_load(ap, tf); + sata_dwc_exec_command(ap, tf); +} + + +/* + * Process command queue issue + */ +static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + int ret = 0; + struct ata_eh_info *ehi; + u32 scontrol, sstatus; + scontrol = sata_dwc_core_scr_read(ap, SCR_CONTROL); + + ehi = &ap->link.eh_info; + /* + * Fix the problem when PMP card is unplugged from the SATA port. + * QC is still issued but no device present. Ignore the current QC. + * and pass error to error handler + */ + sstatus = sata_dwc_core_scr_read(ap, SCR_STATUS); + if ( sstatus == 0x0) { + ata_port_printk(ap, KERN_INFO, "Detect connection lost while commands are executing --> ignore current command\n"); + ata_ehi_hotplugged(ehi); + ap->link.eh_context.i.action |= ATA_EH_RESET; + return ret; + } + + // Set PMP field in the SCONTROL register + if ( sata_pmp_attached(ap) ) + sata_dwc_pmp_select(ap, qc->dev->link->pmp); + +#ifdef DEBUG_NCQ + if (qc->tag > 0 || ap->link.sactive > 1) { + dev_info(ap->dev, "%s ap id=%d cmd(0x%02x)=%s qc tag=%d prot=%s" + " ap active_tag=0x%08x ap sactive=0x%08x\n", + __func__, ap->print_id, qc->tf.command, + ata_cmd_2_txt(&qc->tf), qc->tag, + prot_2_txt(qc->tf.protocol), ap->link.active_tag, + ap->link.sactive); + } +#endif + + // Process NCQ + if (ata_is_ncq(qc->tf.protocol)) { + dwc_link_dbg(qc->dev->link, "%s --> process NCQ , ap->link.active_tag=0x%08x, active_tag=0%08x\n", __func__, ap->link.active_tag, qc->tag); + ap->link.active_tag = qc->tag; + ap->ops->sff_tf_load(ap, &qc->tf); + sata_dwc_exec_command_by_tag(ap, &qc->tf, qc->tag, + SATA_DWC_CMD_ISSUED_PENDING); + } else { + dwc_link_dbg(qc->dev->link, "%s --> non NCQ process, ap->link.active_tag=%d, active_tag=0%08x\n", __func__, ap->link.active_tag, qc->tag); + // Sync ata_port with qc->tag + ap->link.active_tag = qc->tag; + ret = ata_bmdma_qc_issue(qc); + } + + return ret; +} + +#if 0 +/* + * Function : sata_dwc_eng_timeout + * arguments : ata_port *ap + * Return value : None + * error handler for DMA time out + * ata_eng_timeout(ap) -- this does bmdma stuff which can not be done by this + * driver. SEE ALSO ata_qc_timeout(ap) + */ +static void sata_dwc_eng_timeout(struct ata_port *ap) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + struct ata_queued_cmd *qc; + u8 tag; + uint mask = 0x0; + unsigned long flags; + u32 serror, intpr, dma_ch; + + tag = ap->link.active_tag; + dma_ch = hsdevp->dma_chan[tag]; + qc = ata_qc_from_tag(ap, tag); + + dev_err(ap->dev, "%s: id=%d active_tag=%d qc=%p dma_chan=%d\n", + __func__, ap->print_id, tag, qc, dma_ch); + + intpr = in_le32(&hsdev->sata_dwc_regs->intpr); + serror = sata_dwc_core_scr_read(ap, SCR_ERROR); + + dev_err(ap->dev, "intpr=0x%08x serror=0x%08x\n", intpr, serror); + + /* If there are no error bits set, can we just pass this on to eh? */ + if (!(serror & SATA_DWC_SERR_ERR_BITS) && + !(intpr & SATA_DWC_INTPR_ERR)) { + + spin_lock_irqsave(&ap->host->lock, flags); + if (dma_dwc_channel_enabled(dma_ch)) + dma_dwc_terminate_dma(ap, dma_ch); + + hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_NONE; + + /* clear active bit */ + mask = (~(qcmd_tag_to_mask(tag))); + hsdevp->sata_dwc_sactive_queued = hsdevp->sata_dwc_sactive_queued & mask; + hsdevp->sata_dwc_sactive_issued = hsdevp->sata_dwc_sactive_issued & mask; + + spin_unlock_irqrestore(&ap->host->lock, flags); + } else { + /* This is wrong, what really needs to be done is a reset. */ + + spin_lock_irqsave(ap->lock, flags); + + if (ata_is_dma(qc->tf.protocol)) { + /* disable DMAC */ + dma_dwc_terminate_dma(ap, dma_ch); + } + + spin_unlock_irqrestore(ap->lock, flags); + } + WARN_ON(!(qc->flags & ATA_QCFLAG_ACTIVE)); + if (qc->flags & ATA_QCFLAG_ACTIVE) { + qc->err_mask |= AC_ERR_TIMEOUT; + /* + * test-only: The original code (AMCC: 2.6.19) called + * ata_eng_timeout(ap) here. This function is not available + * anymore. So what to do now? + */ + } +} +#endif +/* + * Function : sata_dwc_qc_prep + * arguments : ata_queued_cmd *qc + * Return value : None + * qc_prep for a particular queued command + */ +static void sata_dwc_qc_prep(struct ata_queued_cmd *qc) +{ + u32 sactive; + u8 tag = qc->tag; + + if ((qc->dma_dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO)) + return; + +#ifdef DEBUG_NCQ + if (qc->tag > 0) { + dev_info(qc->ap->dev, "%s: qc->tag=%d ap->active_tag=0x%08x\n", + __func__, qc->tag, qc->ap->link.active_tag); + } +#endif + + if (qc->tf.protocol == ATA_PROT_NCQ) { + sactive = sata_dwc_core_scr_read(qc->ap, SCR_ACTIVE); + sactive |= (0x00000001 << tag); + sata_dwc_core_scr_write(qc->ap, SCR_ACTIVE, sactive); + dwc_dev_vdbg(qc->ap->dev, "%s: tag=%d ap->link.sactive = 0x%08x " + "sactive=0x%08x\n", __func__, tag, qc->ap->link.sactive, + sactive); + } else { + tag = 0; + } + + sata_dwc_qc_prep_by_tag(qc, tag); +} + + + +static void sata_dwc_post_internal_cmd(struct ata_queued_cmd *qc) +{ + if (qc->flags & ATA_QCFLAG_FAILED) + ata_eh_freeze_port(qc->ap); +} + +static void sata_dwc_error_handler(struct ata_port *ap) +{ + u32 serror; + u32 intmr, errmr; + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + + serror = sata_dwc_core_scr_read(ap, SCR_ERROR); + intmr = in_le32(&hsdev->sata_dwc_regs->intmr); + errmr = in_le32(&hsdev->sata_dwc_regs->errmr); + + //sata_dwc_dma_xfer_complete(ap,1); + dwc_port_dbg(ap, "%s: SERROR=0x%08x, INTMR=0x%08x, ERRMR=0x%08x\n", __func__, serror, intmr, errmr); + + dwc_port_vdbg(ap, "%s - sata_dwc_sactive_queued=0x%08x, sata_dwc_sactive_issued=0x%08x\n",__func__, hsdevp->sata_dwc_sactive_queued, hsdevp->sata_dwc_sactive_issued); + dwc_port_vdbg(ap, "dmacr=0x%08x\n",in_le32(&(hsdev->sata_dwc_regs->dmacr))); + dwc_port_vdbg(ap, "qc_active=0x%08x, qc_allocated=0x%08x, active_tag=%d\n", ap->qc_active, ap->qc_allocated, ap->link.active_tag); + + sata_pmp_error_handler(ap); +} + +/* + * sata_dwc_check_status - Get value of the Status Register + * @ap: Port to check + * + * Output content of the status register (CDR7) + */ +u8 sata_dwc_check_status(struct ata_port *ap) +{ + return ioread8(ap->ioaddr.status_addr); +} + + +/* + * Freeze the port by clear interrupt + * @ap: Port to freeze + */ +void sata_dwc_freeze(struct ata_port *ap) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + dwc_port_dbg(ap, "call %s ...\n",__func__); + // turn IRQ off + clear_intpr(hsdev); + clear_serror(ap); + out_le32(&hsdev->sata_dwc_regs->intmr, 0x0); +} + +/* + * Thaw the port by turning IRQ on + */ +void sata_dwc_thaw(struct ata_port *ap) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + + dwc_port_dbg(ap, "call %s ...\n",__func__); + // Clear IRQ + clear_intpr(hsdev); + // Turn IRQ back on + sata_dwc_enable_interrupts(hsdev); +} + + +/* + * scsi mid-layer and libata interface structures + */ +static struct scsi_host_template sata_dwc_sht = { + ATA_NCQ_SHT(DRV_NAME), + /* + * test-only: Currently this driver doesn't handle NCQ + * correctly. We enable NCQ but set the queue depth to a + * max of 1. This will get fixed in in a future release. + */ +// .sg_tablesize = LIBATA_MAX_PRD, + .can_queue = ATA_DEF_QUEUE, /* ATA_MAX_QUEUE */ + .dma_boundary = ATA_DMA_BOUNDARY, +}; + + +static struct ata_port_operations sata_dwc_ops = { + .inherits = &sata_pmp_port_ops, + .dev_config = sata_dwc_dev_config, + + .error_handler = sata_dwc_error_handler, + .softreset = sata_dwc_softreset, + .hardreset = sata_dwc_hardreset, + .pmp_softreset = sata_dwc_softreset, + .pmp_hardreset = sata_dwc_pmp_hardreset, + + .qc_defer = sata_pmp_qc_defer_cmd_switch, + .qc_prep = sata_dwc_qc_prep, + .qc_issue = sata_dwc_qc_issue, + .qc_fill_rtf = ata_sff_qc_fill_rtf, + + .scr_read = sata_dwc_scr_read, + .scr_write = sata_dwc_scr_write, + + .port_start = sata_dwc_port_start, + .port_stop = sata_dwc_port_stop, + + .bmdma_setup = sata_dwc_bmdma_setup, + .bmdma_start = sata_dwc_bmdma_start, + // Reuse some SFF functions + .sff_check_status = sata_dwc_check_status, + .sff_tf_read = ata_sff_tf_read, + .sff_data_xfer = ata_sff_data_xfer, + .sff_tf_load = ata_sff_tf_load, + .sff_dev_select = sata_dwc_dev_select, + .sff_exec_command = sata_dwc_exec_command, + + .sff_irq_on = sata_dwc_irq_on, +/* .sff_irq_clear = sata_dwc_irq_clear, + .freeze = sata_dwc_freeze, + .thaw = sata_dwc_thaw, + .sff_irq_on = ata_sff_irq_on, + */ + .sff_irq_clear = ata_bmdma_irq_clear, + .freeze = ata_sff_freeze, + .thaw = ata_sff_thaw, + .pmp_attach = sata_dwc_pmp_attach, + .pmp_detach = sata_dwc_pmp_detach, + .post_internal_cmd = sata_dwc_post_internal_cmd, + + /* test-only: really needed? */ + //.eng_timeout = sata_dwc_eng_timeout, +}; + +static const struct ata_port_info sata_dwc_port_info[] = { + { + /* + * test-only: Currently this driver doesn't handle NCQ + * correctly. So we disable NCQ here for now. To enable + * it ATA_FLAG_NCQ needs to be added to the flags below. + */ + .flags = ATA_FLAG_SATA | + ATA_FLAG_NCQ | + ATA_FLAG_PMP | ATA_FLAG_AN, + .pio_mask = ATA_PIO4, /* pio 0-4 */ + .udma_mask = ATA_UDMA6, + .port_ops = &sata_dwc_ops, + }, +}; + +static int sata_dwc_probe(struct platform_device *ofdev) +{ + struct sata_dwc_device *hsdev; + u32 idr, versionr; + char *ver = (char *)&versionr; + u8 *base = NULL; + int err = 0; + int irq; + struct ata_host *host; + struct ata_port_info pi = sata_dwc_port_info[0]; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct device_node *np = ofdev->dev.of_node; + const unsigned int *dma_channel; + /* + * Check if device is enabled + */ + if (!of_device_is_available(np)) { + printk(KERN_INFO "%s: Port disabled via device-tree\n", + np->full_name); + return 0; + } + + /* Allocate DWC SATA device */ + hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); + if (hsdev == NULL) { + dev_err(&ofdev->dev, "kmalloc failed for hsdev\n"); + err = -ENOMEM; + goto error; + } + + + // Identify SATA DMA channel used for the current SATA device + dma_channel = of_get_property(np, "dma-channel", NULL); + if ( dma_channel ) { + dev_notice(&ofdev->dev, "Gettting DMA channel %d\n", *dma_channel); + hsdev->dma_channel = *dma_channel; + } else + hsdev->dma_channel = 0; + + /* Ioremap SATA registers */ + base = of_iomap(np, 0); + if (!base) { + dev_err(&ofdev->dev, "ioremap failed for SATA register address\n"); + err = -ENODEV; + goto error_kmalloc; + } + hsdev->reg_base = base; + dwc_dev_vdbg(&ofdev->dev, "ioremap done for SATA register address\n"); + + /* Synopsys DWC SATA specific Registers */ + hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET); + + /* Allocate and fill host */ + host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS); + if (!host) { + dev_err(&ofdev->dev, "ata_host_alloc_pinfo failed\n"); + err = -ENOMEM; + goto error_iomap; + } + + host->private_data = hsdev; + + /* Setup port */ + host->ports[0]->ioaddr.cmd_addr = base; + host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET; + hsdev->scr_base = (u8 *)(base + SATA_DWC_SCR_OFFSET); + sata_dwc_setup_port(&host->ports[0]->ioaddr, (unsigned long)base); + + /* Read the ID and Version Registers */ + idr = in_le32(&hsdev->sata_dwc_regs->idr); + versionr = in_le32(&hsdev->sata_dwc_regs->versionr); + dev_notice(&ofdev->dev, "id %d, controller version %c.%c%c\n", + idr, ver[0], ver[1], ver[2]); + + /* Get SATA DMA interrupt number */ + irq = irq_of_parse_and_map(np, 1); + if (irq == NO_IRQ) { + dev_err(&ofdev->dev, "no SATA DMA irq\n"); + err = -ENODEV; + goto error_out; + } + + /* Get physical SATA DMA register base address */ + if (!sata_dma_regs) { + sata_dma_regs = of_iomap(np, 1); + if (!sata_dma_regs) { + dev_err(&ofdev->dev, "ioremap failed for AHBDMA register address\n"); + err = -ENODEV; + goto error_out; + } + } + /* Save dev for later use in dev_xxx() routines */ + hsdev->dev = &ofdev->dev; + + /* Init glovbal dev list */ + dwc_dev_list[hsdev->dma_channel] = hsdev; + + /* Initialize AHB DMAC */ + hsdev->irq_dma = irq; + dma_dwc_init(hsdev); + dma_register_interrupt(hsdev); + + + /* Enable SATA Interrupts */ + sata_dwc_enable_interrupts(hsdev); + + /* Get SATA interrupt number */ + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + dev_err(&ofdev->dev, "no SATA irq\n"); + err = -ENODEV; + goto error_out; + } + + /* + * Now, register with libATA core, this will also initiate the + * device discovery process, invoking our port_start() handler & + * error_handler() to execute a dummy Softreset EH session + */ + ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); + + dev_set_drvdata(&ofdev->dev, host); + + /* Everything is fine */ + return 0; + +error_out: + /* Free SATA DMA resources */ + dma_dwc_exit(hsdev); + +error_iomap: + iounmap(base); +error_kmalloc: + kfree(hsdev); +error: + return err; +} + +static int sata_dwc_remove(struct platform_device *ofdev) +{ + struct device *dev = &ofdev->dev; + struct ata_host *host = dev_get_drvdata(dev); + struct sata_dwc_device *hsdev = host->private_data; + + ata_host_detach(host); + + dev_set_drvdata(dev, NULL); + + /* Free SATA DMA resources */ + dma_dwc_exit(hsdev); + + iounmap(hsdev->reg_base); + kfree(hsdev); + kfree(host); + + dwc_dev_vdbg(&ofdev->dev, "done\n"); + + return 0; +} + +static const struct of_device_id sata_dwc_match[] = { + { .compatible = "amcc,sata-460ex", }, + { .compatible = "amcc,sata-apm82181", }, + {} +}; +MODULE_DEVICE_TABLE(of, sata_dwc_match); + +static struct platform_driver sata_dwc_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = sata_dwc_match, + }, + .probe = sata_dwc_probe, + .remove = sata_dwc_remove, +}; + +module_platform_driver(sata_dwc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@amcc.com>"); +MODULE_DESCRIPTION("DesignWare Cores SATA controller driver"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 0120b0d1e9a..616a6d2ac20 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -6,7 +6,7 @@ * Author: Ashish Kalra <ashish.kalra@freescale.com> * Li Yang <leoli@freescale.com> * - * Copyright (c) 2006-2007, 2011 Freescale Semiconductor, Inc. + * Copyright (c) 2006-2007, 2011-2012 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -24,8 +24,19 @@ #include <scsi/scsi_cmnd.h> #include <linux/libata.h> #include <asm/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> +static unsigned int intr_coalescing_count; +module_param(intr_coalescing_count, int, S_IRUGO); +MODULE_PARM_DESC(intr_coalescing_count, + "INT coalescing count threshold (1..31)"); + +static unsigned int intr_coalescing_ticks; +module_param(intr_coalescing_ticks, int, S_IRUGO); +MODULE_PARM_DESC(intr_coalescing_ticks, + "INT coalescing timer threshold in AHB ticks"); /* Controller information */ enum { SATA_FSL_QUEUE_DEPTH = 16, @@ -83,6 +94,16 @@ enum { }; /* + * Interrupt Coalescing Control Register bitdefs */ +enum { + ICC_MIN_INT_COUNT_THRESHOLD = 1, + ICC_MAX_INT_COUNT_THRESHOLD = ((1 << 5) - 1), + ICC_MIN_INT_TICKS_THRESHOLD = 0, + ICC_MAX_INT_TICKS_THRESHOLD = ((1 << 19) - 1), + ICC_SAFE_INT_TICKS = 1, +}; + +/* * Host Controller command register set - per port */ enum { @@ -104,6 +125,7 @@ enum { ONLINE = (1 << 31), GOING_OFFLINE = (1 << 30), BIST_ERR = (1 << 29), + CLEAR_ERROR = (1 << 27), FATAL_ERR_HC_MASTER_ERR = (1 << 18), FATAL_ERR_PARITY_ERR_TX = (1 << 17), @@ -124,6 +146,7 @@ enum { FATAL_ERR_CRC_ERR_RX | FATAL_ERR_FIFO_OVRFL_TX | FATAL_ERR_FIFO_OVRFL_RX, + INT_ON_DATA_LENGTH_MISMATCH = (1 << 12), INT_ON_FATAL_ERR = (1 << 5), INT_ON_PHYRDY_CHG = (1 << 4), @@ -263,8 +286,109 @@ struct sata_fsl_host_priv { void __iomem *csr_base; int irq; int data_snoop; + struct device_attribute intr_coalescing; + struct device_attribute rx_watermark; }; +static void fsl_sata_set_irq_coalescing(struct ata_host *host, + unsigned int count, unsigned int ticks) +{ + struct sata_fsl_host_priv *host_priv = host->private_data; + void __iomem *hcr_base = host_priv->hcr_base; + unsigned long flags; + + if (count > ICC_MAX_INT_COUNT_THRESHOLD) + count = ICC_MAX_INT_COUNT_THRESHOLD; + else if (count < ICC_MIN_INT_COUNT_THRESHOLD) + count = ICC_MIN_INT_COUNT_THRESHOLD; + + if (ticks > ICC_MAX_INT_TICKS_THRESHOLD) + ticks = ICC_MAX_INT_TICKS_THRESHOLD; + else if ((ICC_MIN_INT_TICKS_THRESHOLD == ticks) && + (count > ICC_MIN_INT_COUNT_THRESHOLD)) + ticks = ICC_SAFE_INT_TICKS; + + spin_lock_irqsave(&host->lock, flags); + iowrite32((count << 24 | ticks), hcr_base + ICC); + + intr_coalescing_count = count; + intr_coalescing_ticks = ticks; + spin_unlock_irqrestore(&host->lock, flags); + + DPRINTK("interrupt coalescing, count = 0x%x, ticks = %x\n", + intr_coalescing_count, intr_coalescing_ticks); + DPRINTK("ICC register status: (hcr base: 0x%x) = 0x%x\n", + hcr_base, ioread32(hcr_base + ICC)); +} + +static ssize_t fsl_sata_intr_coalescing_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d %d\n", + intr_coalescing_count, intr_coalescing_ticks); +} + +static ssize_t fsl_sata_intr_coalescing_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int coalescing_count, coalescing_ticks; + + if (sscanf(buf, "%d%d", + &coalescing_count, + &coalescing_ticks) != 2) { + printk(KERN_ERR "fsl-sata: wrong parameter format.\n"); + return -EINVAL; + } + + fsl_sata_set_irq_coalescing(dev_get_drvdata(dev), + coalescing_count, coalescing_ticks); + + return strlen(buf); +} + +static ssize_t fsl_sata_rx_watermark_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int rx_watermark; + unsigned long flags; + struct ata_host *host = dev_get_drvdata(dev); + struct sata_fsl_host_priv *host_priv = host->private_data; + void __iomem *csr_base = host_priv->csr_base; + + spin_lock_irqsave(&host->lock, flags); + rx_watermark = ioread32(csr_base + TRANSCFG); + rx_watermark &= 0x1f; + + spin_unlock_irqrestore(&host->lock, flags); + return sprintf(buf, "%d\n", rx_watermark); +} + +static ssize_t fsl_sata_rx_watermark_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int rx_watermark; + unsigned long flags; + struct ata_host *host = dev_get_drvdata(dev); + struct sata_fsl_host_priv *host_priv = host->private_data; + void __iomem *csr_base = host_priv->csr_base; + u32 temp; + + if (sscanf(buf, "%d", &rx_watermark) != 1) { + printk(KERN_ERR "fsl-sata: wrong parameter format.\n"); + return -EINVAL; + } + + spin_lock_irqsave(&host->lock, flags); + temp = ioread32(csr_base + TRANSCFG); + temp &= 0xffffffe0; + iowrite32(temp | rx_watermark, csr_base + TRANSCFG); + + spin_unlock_irqrestore(&host->lock, flags); + return strlen(buf); +} + static inline unsigned int sata_fsl_tag(unsigned int tag, void __iomem *hcr_base) { @@ -346,10 +470,10 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc, (unsigned long long)sg_addr, sg_len); /* warn if each s/g element is not dword aligned */ - if (sg_addr & 0x03) + if (unlikely(sg_addr & 0x03)) ata_port_err(qc->ap, "s/g addr unaligned : 0x%llx\n", (unsigned long long)sg_addr); - if (sg_len & 0x03) + if (unlikely(sg_len & 0x03)) ata_port_err(qc->ap, "s/g len unaligned : 0x%x\n", sg_len); @@ -650,20 +774,6 @@ static int sata_fsl_port_start(struct ata_port *ap) VPRINTK("HControl = 0x%x\n", ioread32(hcr_base + HCONTROL)); VPRINTK("CHBA = 0x%x\n", ioread32(hcr_base + CHBA)); -#ifdef CONFIG_MPC8315_DS - /* - * Workaround for 8315DS board 3gbps link-up issue, - * currently limit SATA port to GEN1 speed - */ - sata_fsl_scr_read(&ap->link, SCR_CONTROL, &temp); - temp &= ~(0xF << 4); - temp |= (0x1 << 4); - sata_fsl_scr_write(&ap->link, SCR_CONTROL, temp); - - sata_fsl_scr_read(&ap->link, SCR_CONTROL, &temp); - dev_warn(dev, "scr_control, speed limited to %x\n", temp); -#endif - return 0; } @@ -1105,25 +1215,54 @@ static void sata_fsl_host_intr(struct ata_port *ap) u32 hstatus, done_mask = 0; struct ata_queued_cmd *qc; u32 SError; + u32 tag; + u32 status_mask = INT_ON_ERROR; hstatus = ioread32(hcr_base + HSTATUS); sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError); + /* Read command completed register */ + done_mask = ioread32(hcr_base + CC); + + /* Workaround for data length mismatch errata */ + if (unlikely(hstatus & INT_ON_DATA_LENGTH_MISMATCH)) { + for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { + qc = ata_qc_from_tag(ap, tag); + if (qc && ata_is_atapi(qc->tf.protocol)) { + u32 hcontrol; + /* Set HControl[27] to clear error registers */ + hcontrol = ioread32(hcr_base + HCONTROL); + iowrite32(hcontrol | CLEAR_ERROR, + hcr_base + HCONTROL); + + /* Clear HControl[27] */ + iowrite32(hcontrol & ~CLEAR_ERROR, + hcr_base + HCONTROL); + + /* Clear SError[E] bit */ + sata_fsl_scr_write(&ap->link, SCR_ERROR, + SError); + + /* Ignore fatal error and device error */ + status_mask &= ~(INT_ON_SINGL_DEVICE_ERR + | INT_ON_FATAL_ERR); + break; + } + } + } + if (unlikely(SError & 0xFFFF0000)) { DPRINTK("serror @host_intr : 0x%x\n", SError); sata_fsl_error_intr(ap); } - if (unlikely(hstatus & INT_ON_ERROR)) { + if (unlikely(hstatus & status_mask)) { DPRINTK("error interrupt!!\n"); sata_fsl_error_intr(ap); return; } - /* Read command completed register */ - done_mask = ioread32(hcr_base + CC); - VPRINTK("Status of all queues :\n"); VPRINTK("done_mask/CC = 0x%x, CA = 0x%x, CE=0x%x,CQ=0x%x,apqa=0x%x\n", done_mask, @@ -1245,6 +1384,13 @@ static int sata_fsl_init_controller(struct ata_host *host) iowrite32(0x00000FFFF, hcr_base + CE); iowrite32(0x00000FFFF, hcr_base + DE); + /* + * reset the number of command complete bits which will cause the + * interrupt to be signaled + */ + fsl_sata_set_irq_coalescing(host, intr_coalescing_count, + intr_coalescing_ticks); + /* * host controller will be brought on-line, during xx_port_start() * callback, that should also initiate the OOB, COMINIT sequence @@ -1309,7 +1455,7 @@ static int sata_fsl_probe(struct platform_device *ofdev) void __iomem *csr_base = NULL; struct sata_fsl_host_priv *host_priv = NULL; int irq; - struct ata_host *host; + struct ata_host *host = NULL; u32 temp; struct ata_port_info pi = sata_fsl_port_info[0]; @@ -1356,6 +1502,10 @@ static int sata_fsl_probe(struct platform_device *ofdev) /* allocate host structure */ host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_FSL_MAX_PORTS); + if (!host) { + retval = -ENOMEM; + goto error_exit_with_cleanup; + } /* host->iomap is not used currently */ host->private_data = host_priv; @@ -1371,28 +1521,51 @@ static int sata_fsl_probe(struct platform_device *ofdev) ata_host_activate(host, irq, sata_fsl_interrupt, SATA_FSL_IRQ_FLAG, &sata_fsl_sht); - dev_set_drvdata(&ofdev->dev, host); + platform_set_drvdata(ofdev, host); + + host_priv->intr_coalescing.show = fsl_sata_intr_coalescing_show; + host_priv->intr_coalescing.store = fsl_sata_intr_coalescing_store; + sysfs_attr_init(&host_priv->intr_coalescing.attr); + host_priv->intr_coalescing.attr.name = "intr_coalescing"; + host_priv->intr_coalescing.attr.mode = S_IRUGO | S_IWUSR; + retval = device_create_file(host->dev, &host_priv->intr_coalescing); + if (retval) + goto error_exit_with_cleanup; + + host_priv->rx_watermark.show = fsl_sata_rx_watermark_show; + host_priv->rx_watermark.store = fsl_sata_rx_watermark_store; + sysfs_attr_init(&host_priv->rx_watermark.attr); + host_priv->rx_watermark.attr.name = "rx_watermark"; + host_priv->rx_watermark.attr.mode = S_IRUGO | S_IWUSR; + retval = device_create_file(host->dev, &host_priv->rx_watermark); + if (retval) { + device_remove_file(&ofdev->dev, &host_priv->intr_coalescing); + goto error_exit_with_cleanup; + } return 0; error_exit_with_cleanup: + if (host) + ata_host_detach(host); + if (hcr_base) iounmap(hcr_base); - if (host_priv) - kfree(host_priv); + kfree(host_priv); return retval; } static int sata_fsl_remove(struct platform_device *ofdev) { - struct ata_host *host = dev_get_drvdata(&ofdev->dev); + struct ata_host *host = platform_get_drvdata(ofdev); struct sata_fsl_host_priv *host_priv = host->private_data; - ata_host_detach(host); + device_remove_file(&ofdev->dev, &host_priv->intr_coalescing); + device_remove_file(&ofdev->dev, &host_priv->rx_watermark); - dev_set_drvdata(&ofdev->dev, NULL); + ata_host_detach(host); irq_dispose_mapping(host_priv->irq); iounmap(host_priv->hcr_base); @@ -1401,16 +1574,16 @@ static int sata_fsl_remove(struct platform_device *ofdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sata_fsl_suspend(struct platform_device *op, pm_message_t state) { - struct ata_host *host = dev_get_drvdata(&op->dev); + struct ata_host *host = platform_get_drvdata(op); return ata_host_suspend(host, state); } static int sata_fsl_resume(struct platform_device *op) { - struct ata_host *host = dev_get_drvdata(&op->dev); + struct ata_host *host = platform_get_drvdata(op); struct sata_fsl_host_priv *host_priv = host->private_data; int ret; void __iomem *hcr_base = host_priv->hcr_base; @@ -1457,7 +1630,7 @@ static struct platform_driver fsl_sata_driver = { }, .probe = sata_fsl_probe, .remove = sata_fsl_remove, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = sata_fsl_suspend, .resume = sata_fsl_resume, #endif diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c new file mode 100644 index 00000000000..65965cf5af0 --- /dev/null +++ b/drivers/ata/sata_highbank.c @@ -0,0 +1,650 @@ +/* + * Calxeda Highbank AHCI SATA platform driver + * Copyright 2012 Calxeda, Inc. + * + * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> + +#include "ahci.h" + +#define CPHY_MAP(dev, addr) ((((dev) & 0x1f) << 7) | (((addr) >> 9) & 0x7f)) +#define CPHY_ADDR(addr) (((addr) & 0x1ff) << 2) +#define SERDES_CR_CTL 0x80a0 +#define SERDES_CR_ADDR 0x80a1 +#define SERDES_CR_DATA 0x80a2 +#define CR_BUSY 0x0001 +#define CR_START 0x0001 +#define CR_WR_RDN 0x0002 +#define CPHY_TX_INPUT_STS 0x2001 +#define CPHY_RX_INPUT_STS 0x2002 +#define CPHY_SATA_TX_OVERRIDE 0x8000 +#define CPHY_SATA_RX_OVERRIDE 0x4000 +#define CPHY_TX_OVERRIDE 0x2004 +#define CPHY_RX_OVERRIDE 0x2005 +#define SPHY_LANE 0x100 +#define SPHY_HALF_RATE 0x0001 +#define CPHY_SATA_DPLL_MODE 0x0700 +#define CPHY_SATA_DPLL_SHIFT 8 +#define CPHY_SATA_DPLL_RESET (1 << 11) +#define CPHY_SATA_TX_ATTEN 0x1c00 +#define CPHY_SATA_TX_ATTEN_SHIFT 10 +#define CPHY_PHY_COUNT 6 +#define CPHY_LANE_COUNT 4 +#define CPHY_PORT_COUNT (CPHY_PHY_COUNT * CPHY_LANE_COUNT) + +static DEFINE_SPINLOCK(cphy_lock); +/* Each of the 6 phys can have up to 4 sata ports attached to i. Map 0-based + * sata ports to their phys and then to their lanes within the phys + */ +struct phy_lane_info { + void __iomem *phy_base; + u8 lane_mapping; + u8 phy_devs; + u8 tx_atten; +}; +static struct phy_lane_info port_data[CPHY_PORT_COUNT]; + +static DEFINE_SPINLOCK(sgpio_lock); +#define SCLOCK 0 +#define SLOAD 1 +#define SDATA 2 +#define SGPIO_PINS 3 +#define SGPIO_PORTS 8 + +struct ecx_plat_data { + u32 n_ports; + /* number of extra clocks that the SGPIO PIC controller expects */ + u32 pre_clocks; + u32 post_clocks; + unsigned sgpio_gpio[SGPIO_PINS]; + u32 sgpio_pattern; + u32 port_to_sgpio[SGPIO_PORTS]; +}; + +#define SGPIO_SIGNALS 3 +#define ECX_ACTIVITY_BITS 0x300000 +#define ECX_ACTIVITY_SHIFT 0 +#define ECX_LOCATE_BITS 0x80000 +#define ECX_LOCATE_SHIFT 1 +#define ECX_FAULT_BITS 0x400000 +#define ECX_FAULT_SHIFT 2 +static inline int sgpio_bit_shift(struct ecx_plat_data *pdata, u32 port, + u32 shift) +{ + return 1 << (3 * pdata->port_to_sgpio[port] + shift); +} + +static void ecx_parse_sgpio(struct ecx_plat_data *pdata, u32 port, u32 state) +{ + if (state & ECX_ACTIVITY_BITS) + pdata->sgpio_pattern |= sgpio_bit_shift(pdata, port, + ECX_ACTIVITY_SHIFT); + else + pdata->sgpio_pattern &= ~sgpio_bit_shift(pdata, port, + ECX_ACTIVITY_SHIFT); + if (state & ECX_LOCATE_BITS) + pdata->sgpio_pattern |= sgpio_bit_shift(pdata, port, + ECX_LOCATE_SHIFT); + else + pdata->sgpio_pattern &= ~sgpio_bit_shift(pdata, port, + ECX_LOCATE_SHIFT); + if (state & ECX_FAULT_BITS) + pdata->sgpio_pattern |= sgpio_bit_shift(pdata, port, + ECX_FAULT_SHIFT); + else + pdata->sgpio_pattern &= ~sgpio_bit_shift(pdata, port, + ECX_FAULT_SHIFT); +} + +/* + * Tell the LED controller that the signal has changed by raising the clock + * line for 50 uS and then lowering it for 50 uS. + */ +static void ecx_led_cycle_clock(struct ecx_plat_data *pdata) +{ + gpio_set_value(pdata->sgpio_gpio[SCLOCK], 1); + udelay(50); + gpio_set_value(pdata->sgpio_gpio[SCLOCK], 0); + udelay(50); +} + +static ssize_t ecx_transmit_led_message(struct ata_port *ap, u32 state, + ssize_t size) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ecx_plat_data *pdata = hpriv->plat_data; + struct ahci_port_priv *pp = ap->private_data; + unsigned long flags; + int pmp, i; + struct ahci_em_priv *emp; + u32 sgpio_out; + + /* get the slot number from the message */ + pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; + if (pmp < EM_MAX_SLOTS) + emp = &pp->em_priv[pmp]; + else + return -EINVAL; + + if (!(hpriv->em_msg_type & EM_MSG_TYPE_LED)) + return size; + + spin_lock_irqsave(&sgpio_lock, flags); + ecx_parse_sgpio(pdata, ap->port_no, state); + sgpio_out = pdata->sgpio_pattern; + for (i = 0; i < pdata->pre_clocks; i++) + ecx_led_cycle_clock(pdata); + + gpio_set_value(pdata->sgpio_gpio[SLOAD], 1); + ecx_led_cycle_clock(pdata); + gpio_set_value(pdata->sgpio_gpio[SLOAD], 0); + /* + * bit-bang out the SGPIO pattern, by consuming a bit and then + * clocking it out. + */ + for (i = 0; i < (SGPIO_SIGNALS * pdata->n_ports); i++) { + gpio_set_value(pdata->sgpio_gpio[SDATA], sgpio_out & 1); + sgpio_out >>= 1; + ecx_led_cycle_clock(pdata); + } + for (i = 0; i < pdata->post_clocks; i++) + ecx_led_cycle_clock(pdata); + + /* save off new led state for port/slot */ + emp->led_state = state; + + spin_unlock_irqrestore(&sgpio_lock, flags); + return size; +} + +static void highbank_set_em_messages(struct device *dev, + struct ahci_host_priv *hpriv, + struct ata_port_info *pi) +{ + struct device_node *np = dev->of_node; + struct ecx_plat_data *pdata = hpriv->plat_data; + int i; + int err; + + for (i = 0; i < SGPIO_PINS; i++) { + err = of_get_named_gpio(np, "calxeda,sgpio-gpio", i); + if (IS_ERR_VALUE(err)) + return; + + pdata->sgpio_gpio[i] = err; + err = gpio_request(pdata->sgpio_gpio[i], "CX SGPIO"); + if (err) { + pr_err("sata_highbank gpio_request %d failed: %d\n", + i, err); + return; + } + gpio_direction_output(pdata->sgpio_gpio[i], 1); + } + of_property_read_u32_array(np, "calxeda,led-order", + pdata->port_to_sgpio, + pdata->n_ports); + if (of_property_read_u32(np, "calxeda,pre-clocks", &pdata->pre_clocks)) + pdata->pre_clocks = 0; + if (of_property_read_u32(np, "calxeda,post-clocks", + &pdata->post_clocks)) + pdata->post_clocks = 0; + + /* store em_loc */ + hpriv->em_loc = 0; + hpriv->em_buf_sz = 4; + hpriv->em_msg_type = EM_MSG_TYPE_LED; + pi->flags |= ATA_FLAG_EM | ATA_FLAG_SW_ACTIVITY; +} + +static u32 __combo_phy_reg_read(u8 sata_port, u32 addr) +{ + u32 data; + u8 dev = port_data[sata_port].phy_devs; + spin_lock(&cphy_lock); + writel(CPHY_MAP(dev, addr), port_data[sata_port].phy_base + 0x800); + data = readl(port_data[sata_port].phy_base + CPHY_ADDR(addr)); + spin_unlock(&cphy_lock); + return data; +} + +static void __combo_phy_reg_write(u8 sata_port, u32 addr, u32 data) +{ + u8 dev = port_data[sata_port].phy_devs; + spin_lock(&cphy_lock); + writel(CPHY_MAP(dev, addr), port_data[sata_port].phy_base + 0x800); + writel(data, port_data[sata_port].phy_base + CPHY_ADDR(addr)); + spin_unlock(&cphy_lock); +} + +static void combo_phy_wait_for_ready(u8 sata_port) +{ + while (__combo_phy_reg_read(sata_port, SERDES_CR_CTL) & CR_BUSY) + udelay(5); +} + +static u32 combo_phy_read(u8 sata_port, u32 addr) +{ + combo_phy_wait_for_ready(sata_port); + __combo_phy_reg_write(sata_port, SERDES_CR_ADDR, addr); + __combo_phy_reg_write(sata_port, SERDES_CR_CTL, CR_START); + combo_phy_wait_for_ready(sata_port); + return __combo_phy_reg_read(sata_port, SERDES_CR_DATA); +} + +static void combo_phy_write(u8 sata_port, u32 addr, u32 data) +{ + combo_phy_wait_for_ready(sata_port); + __combo_phy_reg_write(sata_port, SERDES_CR_ADDR, addr); + __combo_phy_reg_write(sata_port, SERDES_CR_DATA, data); + __combo_phy_reg_write(sata_port, SERDES_CR_CTL, CR_WR_RDN | CR_START); +} + +static void highbank_cphy_disable_overrides(u8 sata_port) +{ + u8 lane = port_data[sata_port].lane_mapping; + u32 tmp; + if (unlikely(port_data[sata_port].phy_base == NULL)) + return; + tmp = combo_phy_read(sata_port, CPHY_RX_INPUT_STS + lane * SPHY_LANE); + tmp &= ~CPHY_SATA_RX_OVERRIDE; + combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp); +} + +static void cphy_override_tx_attenuation(u8 sata_port, u32 val) +{ + u8 lane = port_data[sata_port].lane_mapping; + u32 tmp; + + if (val & 0x8) + return; + + tmp = combo_phy_read(sata_port, CPHY_TX_INPUT_STS + lane * SPHY_LANE); + tmp &= ~CPHY_SATA_TX_OVERRIDE; + combo_phy_write(sata_port, CPHY_TX_OVERRIDE + lane * SPHY_LANE, tmp); + + tmp |= CPHY_SATA_TX_OVERRIDE; + combo_phy_write(sata_port, CPHY_TX_OVERRIDE + lane * SPHY_LANE, tmp); + + tmp |= (val << CPHY_SATA_TX_ATTEN_SHIFT) & CPHY_SATA_TX_ATTEN; + combo_phy_write(sata_port, CPHY_TX_OVERRIDE + lane * SPHY_LANE, tmp); +} + +static void cphy_override_rx_mode(u8 sata_port, u32 val) +{ + u8 lane = port_data[sata_port].lane_mapping; + u32 tmp; + tmp = combo_phy_read(sata_port, CPHY_RX_INPUT_STS + lane * SPHY_LANE); + tmp &= ~CPHY_SATA_RX_OVERRIDE; + combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp); + + tmp |= CPHY_SATA_RX_OVERRIDE; + combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp); + + tmp &= ~CPHY_SATA_DPLL_MODE; + tmp |= val << CPHY_SATA_DPLL_SHIFT; + combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp); + + tmp |= CPHY_SATA_DPLL_RESET; + combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp); + + tmp &= ~CPHY_SATA_DPLL_RESET; + combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp); + + msleep(15); +} + +static void highbank_cphy_override_lane(u8 sata_port) +{ + u8 lane = port_data[sata_port].lane_mapping; + u32 tmp, k = 0; + + if (unlikely(port_data[sata_port].phy_base == NULL)) + return; + do { + tmp = combo_phy_read(sata_port, CPHY_RX_INPUT_STS + + lane * SPHY_LANE); + } while ((tmp & SPHY_HALF_RATE) && (k++ < 1000)); + cphy_override_rx_mode(sata_port, 3); + cphy_override_tx_attenuation(sata_port, port_data[sata_port].tx_atten); +} + +static int highbank_initialize_phys(struct device *dev, void __iomem *addr) +{ + struct device_node *sata_node = dev->of_node; + int phy_count = 0, phy, port = 0, i; + void __iomem *cphy_base[CPHY_PHY_COUNT] = {}; + struct device_node *phy_nodes[CPHY_PHY_COUNT] = {}; + u32 tx_atten[CPHY_PORT_COUNT] = {}; + + memset(port_data, 0, sizeof(struct phy_lane_info) * CPHY_PORT_COUNT); + + do { + u32 tmp; + struct of_phandle_args phy_data; + if (of_parse_phandle_with_args(sata_node, + "calxeda,port-phys", "#phy-cells", + port, &phy_data)) + break; + for (phy = 0; phy < phy_count; phy++) { + if (phy_nodes[phy] == phy_data.np) + break; + } + if (phy_nodes[phy] == NULL) { + phy_nodes[phy] = phy_data.np; + cphy_base[phy] = of_iomap(phy_nodes[phy], 0); + if (cphy_base[phy] == NULL) { + return 0; + } + phy_count += 1; + } + port_data[port].lane_mapping = phy_data.args[0]; + of_property_read_u32(phy_nodes[phy], "phydev", &tmp); + port_data[port].phy_devs = tmp; + port_data[port].phy_base = cphy_base[phy]; + of_node_put(phy_data.np); + port += 1; + } while (port < CPHY_PORT_COUNT); + of_property_read_u32_array(sata_node, "calxeda,tx-atten", + tx_atten, port); + for (i = 0; i < port; i++) + port_data[i].tx_atten = (u8) tx_atten[i]; + return 0; +} + +/* + * The Calxeda SATA phy intermittently fails to bring up a link with Gen3 + * Retrying the phy hard reset can work around the issue, but the drive + * may fail again. In less than 150 out of 15000 test runs, it took more + * than 10 tries for the link to be established (but never more than 35). + * Triple the maximum observed retry count to provide plenty of margin for + * rare events and to guarantee that the link is established. + * + * Also, the default 2 second time-out on a failed drive is too long in + * this situation. The uboot implementation of the same driver function + * uses a much shorter time-out period and never experiences a time out + * issue. Reducing the time-out to 500ms improves the responsiveness. + * The other timing constants were kept the same as the stock AHCI driver. + * This change was also tested 15000 times on 24 drives and none of them + * experienced a time out. + */ +static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + static const unsigned long timing[] = { 5, 100, 500}; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; + u32 sstatus; + int rc; + int retry = 100; + + ahci_stop_engine(ap); + + /* clear D2H reception area to properly wait for D2H FIS */ + ata_tf_init(link->device, &tf); + tf.command = ATA_BUSY; + ata_tf_to_fis(&tf, 0, 0, d2h_fis); + + do { + highbank_cphy_disable_overrides(link->ap->port_no); + rc = sata_link_hardreset(link, timing, deadline, &online, NULL); + highbank_cphy_override_lane(link->ap->port_no); + + /* If the status is 1, we are connected, but the link did not + * come up. So retry resetting the link again. + */ + if (sata_scr_read(link, SCR_STATUS, &sstatus)) + break; + if (!(sstatus & 0x3)) + break; + } while (!online && retry--); + + hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); + + return rc; +} + +static struct ata_port_operations ahci_highbank_ops = { + .inherits = &ahci_ops, + .hardreset = ahci_highbank_hardreset, + .transmit_led_message = ecx_transmit_led_message, +}; + +static const struct ata_port_info ahci_highbank_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_highbank_ops, +}; + +static struct scsi_host_template ahci_highbank_platform_sht = { + AHCI_SHT("sata_highbank"), +}; + +static const struct of_device_id ahci_of_match[] = { + { .compatible = "calxeda,hb-ahci" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ahci_of_match); + +static int ahci_highbank_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct ecx_plat_data *pdata; + struct ata_host *host; + struct resource *mem; + int irq; + int i; + int rc; + u32 n_ports; + struct ata_port_info pi = ahci_highbank_port_info; + const struct ata_port_info *ppi[] = { &pi, NULL }; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(dev, "no mmio space\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "no irq\n"); + return -EINVAL; + } + + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + dev_err(dev, "can't alloc ahci_host_priv\n"); + return -ENOMEM; + } + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "can't alloc ecx_plat_data\n"); + return -ENOMEM; + } + + hpriv->flags |= (unsigned long)pi.private_data; + + hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); + if (!hpriv->mmio) { + dev_err(dev, "can't map %pR\n", mem); + return -ENOMEM; + } + + rc = highbank_initialize_phys(dev, hpriv->mmio); + if (rc) + return rc; + + + ahci_save_initial_config(dev, hpriv, 0, 0); + + /* prepare host */ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + + if (hpriv->cap & HOST_CAP_PMP) + pi.flags |= ATA_FLAG_PMP; + + if (hpriv->cap & HOST_CAP_64) + dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + pdata->n_ports = n_ports; + hpriv->plat_data = pdata; + highbank_set_em_messages(dev, hpriv, &pi); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) { + rc = -ENOMEM; + goto err0; + } + + host->private_data = hpriv; + + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) + host->flags |= ATA_HOST_PARALLEL_SCAN; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + ata_port_desc(ap, "mmio %pR", mem); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + } + + rc = ahci_reset_controller(host); + if (rc) + goto err0; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + + rc = ata_host_activate(host, irq, ahci_interrupt, 0, + &ahci_highbank_platform_sht); + if (rc) + goto err0; + + return 0; +err0: + return rc; +} + +#ifdef CONFIG_PM_SLEEP +static int ahci_highbank_suspend(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 ctl; + int rc; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(dev, "firmware update required for suspend/resume\n"); + return -EIO; + } + + /* + * AHCI spec rev1.1 section 8.3.3: + * Software must disable interrupts prior to requesting a + * transition of the HBA to D3 state. + */ + ctl = readl(mmio + HOST_CTL); + ctl &= ~HOST_IRQ_EN; + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + + rc = ata_host_suspend(host, PMSG_SUSPEND); + if (rc) + return rc; + + return 0; +} + +static int ahci_highbank_resume(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + int rc; + + if (dev->power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + } + + ata_host_resume(host); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ahci_highbank_pm_ops, + ahci_highbank_suspend, ahci_highbank_resume); + +static struct platform_driver ahci_highbank_driver = { + .remove = ata_platform_remove_one, + .driver = { + .name = "highbank-ahci", + .owner = THIS_MODULE, + .of_match_table = ahci_of_match, + .pm = &ahci_highbank_pm_ops, + }, + .probe = ahci_highbank_probe, +}; + +module_platform_driver(ahci_highbank_driver); + +MODULE_DESCRIPTION("Calxeda Highbank AHCI SATA platform driver"); +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("sata:highbank"); diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 5c7d70c03bf..069827826b2 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -6,6 +6,18 @@ * * This file is released under GPL v2. * + * **** WARNING **** + * + * This driver never worked properly and unfortunately data corruption is + * relatively common. There isn't anyone working on the driver and there's + * no support from the vendor. Do not use this driver in any production + * environment. + * + * http://thread.gmane.org/gmane.linux.debian.devel.bugs.rc/378525/focus=54491 + * https://bugzilla.kernel.org/show_bug.cgi?id=60565 + * + * ***************** + * * This controller is eccentric and easily locks up if something isn't * right. Documentation is available at initio's website but it only * documents registers (not programming model). @@ -273,12 +285,10 @@ static void inic_reset_port(void __iomem *port_base) static int inic_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val) { void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; - void __iomem *addr; if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) return -EINVAL; - addr = scr_addr + scr_map[sc_reg] * 4; *val = readl(scr_addr + scr_map[sc_reg] * 4); /* this controller has stuck DIAG.N, ignore it */ @@ -775,10 +785,10 @@ static int init_controller(void __iomem *mmio_base, u16 hctl) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int inic_pci_device_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); struct inic_host_priv *hpriv = host->private_data; int rc; @@ -809,6 +819,8 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ata_print_version_once(&pdev->dev, DRV_VERSION); + dev_alert(&pdev->dev, "inic162x support is broken with common data corruption issues and will be disabled by default, contact linux-ide@vger.kernel.org if in production use\n"); + /* alloc host */ host = ata_host_alloc_pinfo(&pdev->dev, ppi, NR_PORTS); hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); @@ -886,7 +898,7 @@ static const struct pci_device_id inic_pci_tbl[] = { static struct pci_driver inic_pci_driver = { .name = DRV_NAME, .id_table = inic_pci_tbl, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = inic_pci_device_resume, #endif @@ -894,21 +906,10 @@ static struct pci_driver inic_pci_driver = { .remove = ata_pci_remove_one, }; -static int __init inic_init(void) -{ - return pci_register_driver(&inic_pci_driver); -} - -static void __exit inic_exit(void) -{ - pci_unregister_driver(&inic_pci_driver); -} +module_pci_driver(inic_pci_driver); MODULE_AUTHOR("Tejun Heo"); MODULE_DESCRIPTION("low-level driver for Initio 162x SATA"); MODULE_LICENSE("GPL v2"); MODULE_DEVICE_TABLE(pci, inic_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(inic_init); -module_exit(inic_exit); diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 38950ea8398..391cfda1e83 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -60,11 +60,14 @@ #include <linux/dma-mapping.h> #include <linux/device.h> #include <linux/clk.h> +#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/ata_platform.h> #include <linux/mbus.h> #include <linux/bitops.h> #include <linux/gfp.h> +#include <linux/of.h> +#include <linux/of_irq.h> #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -77,8 +80,8 @@ * module options */ -static int msi; #ifdef CONFIG_PCI +static int msi; module_param(msi, int, S_IRUGO); MODULE_PARM_DESC(msi, "Enable use of PCI MSI (0=off, 1=on)"); #endif @@ -302,6 +305,7 @@ enum { MV5_LTMODE = 0x30, MV5_PHY_CTL = 0x0C, SATA_IFCFG = 0x050, + LP_PHY_CTL = 0x058, MV_M2_PREAMP_MASK = 0x7e0, @@ -429,6 +433,7 @@ enum { MV_HP_CUT_THROUGH = (1 << 10), /* can use EDMA cut-through */ MV_HP_FLAG_SOC = (1 << 11), /* SystemOnChip, no PCI */ MV_HP_QUIRK_LED_BLINK_EN = (1 << 12), /* is led blinking enabled? */ + MV_HP_FIX_LP_PHY_CTL = (1 << 13), /* fix speed in LP_PHY_CTL ? */ /* Port private flags (pp_flags) */ MV_PP_FLAG_EDMA_EN = (1 << 0), /* is EDMA engine enabled? */ @@ -551,9 +556,21 @@ struct mv_host_priv { u32 irq_mask_offset; u32 unmask_all_irqs; -#if defined(CONFIG_HAVE_CLK) + /* + * Needed on some devices that require their clocks to be enabled. + * These are optional: if the platform device does not have any + * clocks, they won't be used. Also, if the underlying hardware + * does not support the common clock framework (CONFIG_HAVE_CLK=n), + * all the clock operations become no-ops (see clk.h). + */ struct clk *clk; -#endif + struct clk **port_clks; + /* + * Some devices have a SATA PHY which can be enabled/disabled + * in order to save power. These are optional: if the platform + * devices does not have any phy, they won't be used. + */ + struct phy **port_phys; /* * These consistent DMA memory pools give us guaranteed * alignment for hardware-accessed data structures, @@ -649,12 +666,13 @@ static u8 mv_sff_check_status(struct ata_port *ap); * because we have to allow room for worst case splitting of * PRDs for 64K boundaries in mv_fill_sg(). */ +#ifdef CONFIG_PCI static struct scsi_host_template mv5_sht = { ATA_BASE_SHT(DRV_NAME), .sg_tablesize = MV_MAX_SG_CT / 2, .dma_boundary = MV_DMA_BOUNDARY, }; - +#endif static struct scsi_host_template mv6_sht = { ATA_NCQ_SHT(DRV_NAME), .can_queue = MV_MAX_Q_DEPTH - 1, @@ -1249,7 +1267,7 @@ static void mv_dump_mem(void __iomem *start, unsigned bytes) } } #endif - +#if defined(ATA_DEBUG) || defined(CONFIG_PCI) static void mv_dump_pci_cfg(struct pci_dev *pdev, unsigned bytes) { #ifdef ATA_DEBUG @@ -1266,6 +1284,7 @@ static void mv_dump_pci_cfg(struct pci_dev *pdev, unsigned bytes) } #endif } +#endif static void mv_dump_all_regs(void __iomem *mmio_base, int port, struct pci_dev *pdev) { @@ -1348,6 +1367,7 @@ static int mv_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val) if (ofs != 0xffffffffU) { void __iomem *addr = mv_ap_base(link->ap) + ofs; + struct mv_host_priv *hpriv = link->ap->host->private_data; if (sc_reg_in == SCR_CONTROL) { /* * Workaround for 88SX60x1 FEr SATA#26: @@ -1364,6 +1384,18 @@ static int mv_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val) */ if ((val & 0xf) == 1 || (readl(addr) & 0xf) == 1) val |= 0xf000; + + if (hpriv->hp_flags & MV_HP_FIX_LP_PHY_CTL) { + void __iomem *lp_phy_addr = + mv_ap_base(link->ap) + LP_PHY_CTL; + /* + * Set PHY speed according to SControl speed. + */ + if ((val & 0xf0) == 0x10) + writelfl(0x7, lp_phy_addr); + else + writelfl(0x227, lp_phy_addr); + } } writelfl(val, addr); return 0; @@ -4025,7 +4057,9 @@ static int mv_platform_probe(struct platform_device *pdev) struct ata_host *host; struct mv_host_priv *hpriv; struct resource *res; - int n_ports, rc; + int n_ports = 0, irq = 0; + int rc; + int port; ata_print_version_once(&pdev->dev, DRV_VERSION); @@ -4045,16 +4079,31 @@ static int mv_platform_probe(struct platform_device *pdev) return -EINVAL; /* allocate host */ - mv_platform_data = pdev->dev.platform_data; - n_ports = mv_platform_data->n_ports; + if (pdev->dev.of_node) { + of_property_read_u32(pdev->dev.of_node, "nr-ports", &n_ports); + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + } else { + mv_platform_data = dev_get_platdata(&pdev->dev); + n_ports = mv_platform_data->n_ports; + irq = platform_get_irq(pdev, 0); + } host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); if (!host || !hpriv) return -ENOMEM; + hpriv->port_clks = devm_kzalloc(&pdev->dev, + sizeof(struct clk *) * n_ports, + GFP_KERNEL); + if (!hpriv->port_clks) + return -ENOMEM; + hpriv->port_phys = devm_kzalloc(&pdev->dev, + sizeof(struct phy *) * n_ports, + GFP_KERNEL); + if (!hpriv->port_phys) + return -ENOMEM; host->private_data = hpriv; - hpriv->n_ports = n_ports; hpriv->board_idx = chip_soc; host->iomap = NULL; @@ -4062,13 +4111,37 @@ static int mv_platform_probe(struct platform_device *pdev) resource_size(res)); hpriv->base -= SATAHC0_REG_BASE; -#if defined(CONFIG_HAVE_CLK) hpriv->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(hpriv->clk)) - dev_notice(&pdev->dev, "cannot get clkdev\n"); + dev_notice(&pdev->dev, "cannot get optional clkdev\n"); else - clk_enable(hpriv->clk); -#endif + clk_prepare_enable(hpriv->clk); + + for (port = 0; port < n_ports; port++) { + char port_number[16]; + sprintf(port_number, "%d", port); + hpriv->port_clks[port] = clk_get(&pdev->dev, port_number); + if (!IS_ERR(hpriv->port_clks[port])) + clk_prepare_enable(hpriv->port_clks[port]); + + sprintf(port_number, "port%d", port); + hpriv->port_phys[port] = devm_phy_optional_get(&pdev->dev, + port_number); + if (IS_ERR(hpriv->port_phys[port])) { + rc = PTR_ERR(hpriv->port_phys[port]); + hpriv->port_phys[port] = NULL; + if (rc != -EPROBE_DEFER) + dev_warn(&pdev->dev, "error getting phy %d", rc); + + /* Cleanup only the initialized ports */ + hpriv->n_ports = port; + goto err; + } else + phy_power_on(hpriv->port_phys[port]); + } + + /* All the ports have been initialized */ + hpriv->n_ports = n_ports; /* * (Re-)program MBUS remapping windows if we are asked to. @@ -4081,6 +4154,15 @@ static int mv_platform_probe(struct platform_device *pdev) if (rc) goto err; + /* + * To allow disk hotplug on Armada 370/XP SoCs, the PHY speed must be + * updated in the LP_PHY_CTL register. + */ + if (pdev->dev.of_node && + of_device_is_compatible(pdev->dev.of_node, + "marvell,armada-370-sata")) + hpriv->hp_flags |= MV_HP_FIX_LP_PHY_CTL; + /* initialize adapter */ rc = mv_init_host(host); if (rc) @@ -4089,18 +4171,23 @@ static int mv_platform_probe(struct platform_device *pdev) dev_info(&pdev->dev, "slots %u ports %d\n", (unsigned)MV_MAX_Q_DEPTH, host->n_ports); - rc = ata_host_activate(host, platform_get_irq(pdev, 0), mv_interrupt, - IRQF_SHARED, &mv6_sht); + rc = ata_host_activate(host, irq, mv_interrupt, IRQF_SHARED, &mv6_sht); if (!rc) return 0; err: -#if defined(CONFIG_HAVE_CLK) if (!IS_ERR(hpriv->clk)) { - clk_disable(hpriv->clk); + clk_disable_unprepare(hpriv->clk); clk_put(hpriv->clk); } -#endif + for (port = 0; port < hpriv->n_ports; port++) { + if (!IS_ERR(hpriv->port_clks[port])) { + clk_disable_unprepare(hpriv->port_clks[port]); + clk_put(hpriv->port_clks[port]); + } + if (hpriv->port_phys[port]) + phy_power_off(hpriv->port_phys[port]); + } return rc; } @@ -4113,24 +4200,29 @@ err: * A platform bus SATA device has been unplugged. Perform the needed * cleanup. Also called on module unload for any active devices. */ -static int __devexit mv_platform_remove(struct platform_device *pdev) +static int mv_platform_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); -#if defined(CONFIG_HAVE_CLK) struct mv_host_priv *hpriv = host->private_data; -#endif + int port; ata_host_detach(host); -#if defined(CONFIG_HAVE_CLK) if (!IS_ERR(hpriv->clk)) { - clk_disable(hpriv->clk); + clk_disable_unprepare(hpriv->clk); clk_put(hpriv->clk); } -#endif + for (port = 0; port < host->n_ports; port++) { + if (!IS_ERR(hpriv->port_clks[port])) { + clk_disable_unprepare(hpriv->port_clks[port]); + clk_put(hpriv->port_clks[port]); + } + if (hpriv->port_phys[port]) + phy_power_off(hpriv->port_phys[port]); + } return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mv_platform_suspend(struct platform_device *pdev, pm_message_t state) { struct ata_host *host = platform_get_drvdata(pdev); @@ -4172,22 +4264,32 @@ static int mv_platform_resume(struct platform_device *pdev) #define mv_platform_resume NULL #endif +#ifdef CONFIG_OF +static struct of_device_id mv_sata_dt_ids[] = { + { .compatible = "marvell,armada-370-sata", }, + { .compatible = "marvell,orion-sata", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_sata_dt_ids); +#endif + static struct platform_driver mv_platform_driver = { - .probe = mv_platform_probe, - .remove = __devexit_p(mv_platform_remove), - .suspend = mv_platform_suspend, - .resume = mv_platform_resume, - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, + .probe = mv_platform_probe, + .remove = mv_platform_remove, + .suspend = mv_platform_suspend, + .resume = mv_platform_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mv_sata_dt_ids), + }, }; #ifdef CONFIG_PCI static int mv_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mv_pci_device_resume(struct pci_dev *pdev); #endif @@ -4197,7 +4299,7 @@ static struct pci_driver mv_pci_driver = { .id_table = mv_pci_tbl, .probe = mv_pci_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = mv_pci_device_resume, #endif @@ -4355,7 +4457,7 @@ static int mv_pci_init_one(struct pci_dev *pdev, IS_GEN_I(hpriv) ? &mv5_sht : &mv6_sht); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mv_pci_device_resume(struct pci_dev *pdev) { struct ata_host *host = pci_get_drvdata(pdev); @@ -4377,9 +4479,6 @@ static int mv_pci_device_resume(struct pci_dev *pdev) #endif #endif -static int mv_platform_probe(struct platform_device *pdev); -static int __devexit mv_platform_remove(struct platform_device *pdev); - static int __init mv_init(void) { int rc = -ENODEV; diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 55d6179dde5..cdf99fac139 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -40,7 +40,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -296,7 +295,7 @@ struct nv_swncq_port_priv { #define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & (1 << (19 + (12 * (PORT))))) static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int nv_pci_device_resume(struct pci_dev *pdev); #endif static void nv_ck804_host_stop(struct ata_host *host); @@ -380,7 +379,7 @@ static struct pci_driver nv_pci_driver = { .name = DRV_NAME, .id_table = nv_pci_tbl, .probe = nv_init_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = nv_pci_device_resume, #endif @@ -2432,10 +2431,10 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return ata_pci_sff_activate_host(host, ipriv->irq_handler, ipriv->sht); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int nv_pci_device_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); struct nv_host_priv *hpriv = host->private_data; int rc; @@ -2510,22 +2509,11 @@ static void nv_adma_host_stop(struct ata_host *host) nv_ck804_host_stop(host); } -static int __init nv_init(void) -{ - return pci_register_driver(&nv_pci_driver); -} +module_pci_driver(nv_pci_driver); -static void __exit nv_exit(void) -{ - pci_unregister_driver(&nv_pci_driver); -} - -module_init(nv_init); -module_exit(nv_exit); module_param_named(adma, adma_enabled, bool, 0444); MODULE_PARM_DESC(adma, "Enable use of ADMA (Default: false)"); module_param_named(swncq, swncq_enabled, bool, 0444); MODULE_PARM_DESC(swncq, "Enable use of SWNCQ (Default: true)"); module_param_named(msi, msi_enabled, bool, 0444); MODULE_PARM_DESC(msi, "Enable use of MSI (Default: false)"); - diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 000fcc99e01..3638887476f 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -1,8 +1,8 @@ /* * sata_promise.c - Promise SATA * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> - * Mikael Pettersson <mikpe@it.uu.se> + * Maintained by: Tejun Heo <tj@kernel.org> + * Mikael Pettersson * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -35,7 +35,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -147,6 +146,10 @@ struct pdc_port_priv { dma_addr_t pkt_dma; }; +struct pdc_host_priv { + spinlock_t hard_reset_lock; +}; + static int pdc_sata_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); static int pdc_sata_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int pdc_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); @@ -801,9 +804,10 @@ static void pdc_hard_reset_port(struct ata_port *ap) void __iomem *host_mmio = ap->host->iomap[PDC_MMIO_BAR]; void __iomem *pcictl_b1_mmio = host_mmio + PDC_PCI_CTL + 1; unsigned int ata_no = pdc_ata_port_to_ata_no(ap); + struct pdc_host_priv *hpriv = ap->host->private_data; u8 tmp; - spin_lock(&ap->host->lock); + spin_lock(&hpriv->hard_reset_lock); tmp = readb(pcictl_b1_mmio); tmp &= ~(0x10 << ata_no); @@ -814,7 +818,7 @@ static void pdc_hard_reset_port(struct ata_port *ap) writeb(tmp, pcictl_b1_mmio); readb(pcictl_b1_mmio); /* flush */ - spin_unlock(&ap->host->lock); + spin_unlock(&hpriv->hard_reset_lock); } static int pdc_sata_hardreset(struct ata_link *link, unsigned int *class, @@ -1182,6 +1186,7 @@ static int pdc_ata_init_one(struct pci_dev *pdev, const struct ata_port_info *pi = &pdc_port_info[ent->driver_data]; const struct ata_port_info *ppi[PDC_MAX_PORTS]; struct ata_host *host; + struct pdc_host_priv *hpriv; void __iomem *host_mmio; int n_ports, i, rc; int is_sataii_tx4; @@ -1218,6 +1223,11 @@ static int pdc_ata_init_one(struct pci_dev *pdev, dev_err(&pdev->dev, "failed to allocate host\n"); return -ENOMEM; } + hpriv = devm_kzalloc(&pdev->dev, sizeof *hpriv, GFP_KERNEL); + if (!hpriv) + return -ENOMEM; + spin_lock_init(&hpriv->hard_reset_lock); + host->private_data = hpriv; host->iomap = pcim_iomap_table(pdev); is_sataii_tx4 = pdc_is_sataii_tx4(pi->flags); @@ -1249,21 +1259,10 @@ static int pdc_ata_init_one(struct pci_dev *pdev, &pdc_ata_sht); } -static int __init pdc_ata_init(void) -{ - return pci_register_driver(&pdc_ata_pci_driver); -} - -static void __exit pdc_ata_exit(void) -{ - pci_unregister_driver(&pdc_ata_pci_driver); -} +module_pci_driver(pdc_ata_pci_driver); MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("Promise ATA TX2/TX4/TX4000 low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pdc_ata_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(pdc_ata_init); -module_exit(pdc_ata_exit); diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 9d1a47bb21b..9a6bd4cd29a 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -31,7 +31,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -635,21 +634,10 @@ static int qs_ata_init_one(struct pci_dev *pdev, &qs_ata_sht); } -static int __init qs_ata_init(void) -{ - return pci_register_driver(&qs_ata_pci_driver); -} - -static void __exit qs_ata_exit(void) -{ - pci_unregister_driver(&qs_ata_pci_driver); -} +module_pci_driver(qs_ata_pci_driver); MODULE_AUTHOR("Mark Lord"); MODULE_DESCRIPTION("Pacific Digital Corporation QStor SATA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, qs_ata_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(qs_ata_init); -module_exit(qs_ata_exit); diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c new file mode 100644 index 00000000000..61eb6d77dac --- /dev/null +++ b/drivers/ata/sata_rcar.c @@ -0,0 +1,1004 @@ +/* + * Renesas R-Car SATA driver + * + * Author: Vladimir Barinov <source@cogentembedded.com> + * Copyright (C) 2013 Cogent Embedded, Inc. + * Copyright (C) 2013 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ata.h> +#include <linux/libata.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> + +#define DRV_NAME "sata_rcar" + +/* SH-Navi2G/ATAPI-ATA compatible task registers */ +#define DATA_REG 0x100 +#define SDEVCON_REG 0x138 + +/* SH-Navi2G/ATAPI module compatible control registers */ +#define ATAPI_CONTROL1_REG 0x180 +#define ATAPI_STATUS_REG 0x184 +#define ATAPI_INT_ENABLE_REG 0x188 +#define ATAPI_DTB_ADR_REG 0x198 +#define ATAPI_DMA_START_ADR_REG 0x19C +#define ATAPI_DMA_TRANS_CNT_REG 0x1A0 +#define ATAPI_CONTROL2_REG 0x1A4 +#define ATAPI_SIG_ST_REG 0x1B0 +#define ATAPI_BYTE_SWAP_REG 0x1BC + +/* ATAPI control 1 register (ATAPI_CONTROL1) bits */ +#define ATAPI_CONTROL1_ISM BIT(16) +#define ATAPI_CONTROL1_DTA32M BIT(11) +#define ATAPI_CONTROL1_RESET BIT(7) +#define ATAPI_CONTROL1_DESE BIT(3) +#define ATAPI_CONTROL1_RW BIT(2) +#define ATAPI_CONTROL1_STOP BIT(1) +#define ATAPI_CONTROL1_START BIT(0) + +/* ATAPI status register (ATAPI_STATUS) bits */ +#define ATAPI_STATUS_SATAINT BIT(11) +#define ATAPI_STATUS_DNEND BIT(6) +#define ATAPI_STATUS_DEVTRM BIT(5) +#define ATAPI_STATUS_DEVINT BIT(4) +#define ATAPI_STATUS_ERR BIT(2) +#define ATAPI_STATUS_NEND BIT(1) +#define ATAPI_STATUS_ACT BIT(0) + +/* Interrupt enable register (ATAPI_INT_ENABLE) bits */ +#define ATAPI_INT_ENABLE_SATAINT BIT(11) +#define ATAPI_INT_ENABLE_DNEND BIT(6) +#define ATAPI_INT_ENABLE_DEVTRM BIT(5) +#define ATAPI_INT_ENABLE_DEVINT BIT(4) +#define ATAPI_INT_ENABLE_ERR BIT(2) +#define ATAPI_INT_ENABLE_NEND BIT(1) +#define ATAPI_INT_ENABLE_ACT BIT(0) + +/* Access control registers for physical layer control register */ +#define SATAPHYADDR_REG 0x200 +#define SATAPHYWDATA_REG 0x204 +#define SATAPHYACCEN_REG 0x208 +#define SATAPHYRESET_REG 0x20C +#define SATAPHYRDATA_REG 0x210 +#define SATAPHYACK_REG 0x214 + +/* Physical layer control address command register (SATAPHYADDR) bits */ +#define SATAPHYADDR_PHYRATEMODE BIT(10) +#define SATAPHYADDR_PHYCMD_READ BIT(9) +#define SATAPHYADDR_PHYCMD_WRITE BIT(8) + +/* Physical layer control enable register (SATAPHYACCEN) bits */ +#define SATAPHYACCEN_PHYLANE BIT(0) + +/* Physical layer control reset register (SATAPHYRESET) bits */ +#define SATAPHYRESET_PHYRST BIT(1) +#define SATAPHYRESET_PHYSRES BIT(0) + +/* Physical layer control acknowledge register (SATAPHYACK) bits */ +#define SATAPHYACK_PHYACK BIT(0) + +/* Serial-ATA HOST control registers */ +#define BISTCONF_REG 0x102C +#define SDATA_REG 0x1100 +#define SSDEVCON_REG 0x1204 + +#define SCRSSTS_REG 0x1400 +#define SCRSERR_REG 0x1404 +#define SCRSCON_REG 0x1408 +#define SCRSACT_REG 0x140C + +#define SATAINTSTAT_REG 0x1508 +#define SATAINTMASK_REG 0x150C + +/* SATA INT status register (SATAINTSTAT) bits */ +#define SATAINTSTAT_SERR BIT(3) +#define SATAINTSTAT_ATA BIT(0) + +/* SATA INT mask register (SATAINTSTAT) bits */ +#define SATAINTMASK_SERRMSK BIT(3) +#define SATAINTMASK_ERRMSK BIT(2) +#define SATAINTMASK_ERRCRTMSK BIT(1) +#define SATAINTMASK_ATAMSK BIT(0) + +#define SATA_RCAR_INT_MASK (SATAINTMASK_SERRMSK | \ + SATAINTMASK_ATAMSK) + +/* Physical Layer Control Registers */ +#define SATAPCTLR1_REG 0x43 +#define SATAPCTLR2_REG 0x52 +#define SATAPCTLR3_REG 0x5A +#define SATAPCTLR4_REG 0x60 + +/* Descriptor table word 0 bit (when DTA32M = 1) */ +#define SATA_RCAR_DTEND BIT(0) + +#define SATA_RCAR_DMA_BOUNDARY 0x1FFFFFFEUL + +/* Gen2 Physical Layer Control Registers */ +#define RCAR_GEN2_PHY_CTL1_REG 0x1704 +#define RCAR_GEN2_PHY_CTL1 0x34180002 +#define RCAR_GEN2_PHY_CTL1_SS 0xC180 /* Spread Spectrum */ + +#define RCAR_GEN2_PHY_CTL2_REG 0x170C +#define RCAR_GEN2_PHY_CTL2 0x00002303 + +#define RCAR_GEN2_PHY_CTL3_REG 0x171C +#define RCAR_GEN2_PHY_CTL3 0x000B0194 + +#define RCAR_GEN2_PHY_CTL4_REG 0x1724 +#define RCAR_GEN2_PHY_CTL4 0x00030994 + +#define RCAR_GEN2_PHY_CTL5_REG 0x1740 +#define RCAR_GEN2_PHY_CTL5 0x03004001 +#define RCAR_GEN2_PHY_CTL5_DC BIT(1) /* DC connection */ +#define RCAR_GEN2_PHY_CTL5_TR BIT(2) /* Termination Resistor */ + +enum sata_rcar_type { + RCAR_GEN1_SATA, + RCAR_GEN2_SATA, +}; + +struct sata_rcar_priv { + void __iomem *base; + struct clk *clk; + enum sata_rcar_type type; +}; + +static void sata_rcar_gen1_phy_preinit(struct sata_rcar_priv *priv) +{ + void __iomem *base = priv->base; + + /* idle state */ + iowrite32(0, base + SATAPHYADDR_REG); + /* reset */ + iowrite32(SATAPHYRESET_PHYRST, base + SATAPHYRESET_REG); + udelay(10); + /* deassert reset */ + iowrite32(0, base + SATAPHYRESET_REG); +} + +static void sata_rcar_gen1_phy_write(struct sata_rcar_priv *priv, u16 reg, + u32 val, int group) +{ + void __iomem *base = priv->base; + int timeout; + + /* deassert reset */ + iowrite32(0, base + SATAPHYRESET_REG); + /* lane 1 */ + iowrite32(SATAPHYACCEN_PHYLANE, base + SATAPHYACCEN_REG); + /* write phy register value */ + iowrite32(val, base + SATAPHYWDATA_REG); + /* set register group */ + if (group) + reg |= SATAPHYADDR_PHYRATEMODE; + /* write command */ + iowrite32(SATAPHYADDR_PHYCMD_WRITE | reg, base + SATAPHYADDR_REG); + /* wait for ack */ + for (timeout = 0; timeout < 100; timeout++) { + val = ioread32(base + SATAPHYACK_REG); + if (val & SATAPHYACK_PHYACK) + break; + } + if (timeout >= 100) + pr_err("%s timeout\n", __func__); + /* idle state */ + iowrite32(0, base + SATAPHYADDR_REG); +} + +static void sata_rcar_gen1_phy_init(struct sata_rcar_priv *priv) +{ + sata_rcar_gen1_phy_preinit(priv); + sata_rcar_gen1_phy_write(priv, SATAPCTLR1_REG, 0x00200188, 0); + sata_rcar_gen1_phy_write(priv, SATAPCTLR1_REG, 0x00200188, 1); + sata_rcar_gen1_phy_write(priv, SATAPCTLR3_REG, 0x0000A061, 0); + sata_rcar_gen1_phy_write(priv, SATAPCTLR2_REG, 0x20000000, 0); + sata_rcar_gen1_phy_write(priv, SATAPCTLR2_REG, 0x20000000, 1); + sata_rcar_gen1_phy_write(priv, SATAPCTLR4_REG, 0x28E80000, 0); +} + +static void sata_rcar_gen2_phy_init(struct sata_rcar_priv *priv) +{ + void __iomem *base = priv->base; + + iowrite32(RCAR_GEN2_PHY_CTL1, base + RCAR_GEN2_PHY_CTL1_REG); + iowrite32(RCAR_GEN2_PHY_CTL2, base + RCAR_GEN2_PHY_CTL2_REG); + iowrite32(RCAR_GEN2_PHY_CTL3, base + RCAR_GEN2_PHY_CTL3_REG); + iowrite32(RCAR_GEN2_PHY_CTL4, base + RCAR_GEN2_PHY_CTL4_REG); + iowrite32(RCAR_GEN2_PHY_CTL5 | RCAR_GEN2_PHY_CTL5_DC | + RCAR_GEN2_PHY_CTL5_TR, base + RCAR_GEN2_PHY_CTL5_REG); +} + +static void sata_rcar_freeze(struct ata_port *ap) +{ + struct sata_rcar_priv *priv = ap->host->private_data; + + /* mask */ + iowrite32(0x7ff, priv->base + SATAINTMASK_REG); + + ata_sff_freeze(ap); +} + +static void sata_rcar_thaw(struct ata_port *ap) +{ + struct sata_rcar_priv *priv = ap->host->private_data; + void __iomem *base = priv->base; + + /* ack */ + iowrite32(~(u32)SATA_RCAR_INT_MASK, base + SATAINTSTAT_REG); + + ata_sff_thaw(ap); + + /* unmask */ + iowrite32(0x7ff & ~SATA_RCAR_INT_MASK, base + SATAINTMASK_REG); +} + +static void sata_rcar_ioread16_rep(void __iomem *reg, void *buffer, int count) +{ + u16 *ptr = buffer; + + while (count--) { + u16 data = ioread32(reg); + + *ptr++ = data; + } +} + +static void sata_rcar_iowrite16_rep(void __iomem *reg, void *buffer, int count) +{ + const u16 *ptr = buffer; + + while (count--) + iowrite32(*ptr++, reg); +} + +static u8 sata_rcar_check_status(struct ata_port *ap) +{ + return ioread32(ap->ioaddr.status_addr); +} + +static u8 sata_rcar_check_altstatus(struct ata_port *ap) +{ + return ioread32(ap->ioaddr.altstatus_addr); +} + +static void sata_rcar_set_devctl(struct ata_port *ap, u8 ctl) +{ + iowrite32(ctl, ap->ioaddr.ctl_addr); +} + +static void sata_rcar_dev_select(struct ata_port *ap, unsigned int device) +{ + iowrite32(ATA_DEVICE_OBS, ap->ioaddr.device_addr); + ata_sff_pause(ap); /* needed; also flushes, for mmio */ +} + +static unsigned int sata_rcar_ata_devchk(struct ata_port *ap, + unsigned int device) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + u8 nsect, lbal; + + sata_rcar_dev_select(ap, device); + + iowrite32(0x55, ioaddr->nsect_addr); + iowrite32(0xaa, ioaddr->lbal_addr); + + iowrite32(0xaa, ioaddr->nsect_addr); + iowrite32(0x55, ioaddr->lbal_addr); + + iowrite32(0x55, ioaddr->nsect_addr); + iowrite32(0xaa, ioaddr->lbal_addr); + + nsect = ioread32(ioaddr->nsect_addr); + lbal = ioread32(ioaddr->lbal_addr); + + if (nsect == 0x55 && lbal == 0xaa) + return 1; /* found a device */ + + return 0; /* nothing found */ +} + +static int sata_rcar_wait_after_reset(struct ata_link *link, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + + ata_msleep(ap, ATA_WAIT_AFTER_RESET); + + return ata_sff_wait_ready(link, deadline); +} + +static int sata_rcar_bus_softreset(struct ata_port *ap, unsigned long deadline) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + DPRINTK("ata%u: bus reset via SRST\n", ap->print_id); + + /* software reset. causes dev0 to be selected */ + iowrite32(ap->ctl, ioaddr->ctl_addr); + udelay(20); + iowrite32(ap->ctl | ATA_SRST, ioaddr->ctl_addr); + udelay(20); + iowrite32(ap->ctl, ioaddr->ctl_addr); + ap->last_ctl = ap->ctl; + + /* wait the port to become ready */ + return sata_rcar_wait_after_reset(&ap->link, deadline); +} + +static int sata_rcar_softreset(struct ata_link *link, unsigned int *classes, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + unsigned int devmask = 0; + int rc; + u8 err; + + /* determine if device 0 is present */ + if (sata_rcar_ata_devchk(ap, 0)) + devmask |= 1 << 0; + + /* issue bus reset */ + DPRINTK("about to softreset, devmask=%x\n", devmask); + rc = sata_rcar_bus_softreset(ap, deadline); + /* if link is occupied, -ENODEV too is an error */ + if (rc && (rc != -ENODEV || sata_scr_valid(link))) { + ata_link_err(link, "SRST failed (errno=%d)\n", rc); + return rc; + } + + /* determine by signature whether we have ATA or ATAPI devices */ + classes[0] = ata_sff_dev_classify(&link->device[0], devmask, &err); + + DPRINTK("classes[0]=%u\n", classes[0]); + return 0; +} + +static void sata_rcar_tf_load(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; + + if (tf->ctl != ap->last_ctl) { + iowrite32(tf->ctl, ioaddr->ctl_addr); + ap->last_ctl = tf->ctl; + ata_wait_idle(ap); + } + + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { + iowrite32(tf->hob_feature, ioaddr->feature_addr); + iowrite32(tf->hob_nsect, ioaddr->nsect_addr); + iowrite32(tf->hob_lbal, ioaddr->lbal_addr); + iowrite32(tf->hob_lbam, ioaddr->lbam_addr); + iowrite32(tf->hob_lbah, ioaddr->lbah_addr); + VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n", + tf->hob_feature, + tf->hob_nsect, + tf->hob_lbal, + tf->hob_lbam, + tf->hob_lbah); + } + + if (is_addr) { + iowrite32(tf->feature, ioaddr->feature_addr); + iowrite32(tf->nsect, ioaddr->nsect_addr); + iowrite32(tf->lbal, ioaddr->lbal_addr); + iowrite32(tf->lbam, ioaddr->lbam_addr); + iowrite32(tf->lbah, ioaddr->lbah_addr); + VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n", + tf->feature, + tf->nsect, + tf->lbal, + tf->lbam, + tf->lbah); + } + + if (tf->flags & ATA_TFLAG_DEVICE) { + iowrite32(tf->device, ioaddr->device_addr); + VPRINTK("device 0x%X\n", tf->device); + } + + ata_wait_idle(ap); +} + +static void sata_rcar_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + tf->command = sata_rcar_check_status(ap); + tf->feature = ioread32(ioaddr->error_addr); + tf->nsect = ioread32(ioaddr->nsect_addr); + tf->lbal = ioread32(ioaddr->lbal_addr); + tf->lbam = ioread32(ioaddr->lbam_addr); + tf->lbah = ioread32(ioaddr->lbah_addr); + tf->device = ioread32(ioaddr->device_addr); + + if (tf->flags & ATA_TFLAG_LBA48) { + iowrite32(tf->ctl | ATA_HOB, ioaddr->ctl_addr); + tf->hob_feature = ioread32(ioaddr->error_addr); + tf->hob_nsect = ioread32(ioaddr->nsect_addr); + tf->hob_lbal = ioread32(ioaddr->lbal_addr); + tf->hob_lbam = ioread32(ioaddr->lbam_addr); + tf->hob_lbah = ioread32(ioaddr->lbah_addr); + iowrite32(tf->ctl, ioaddr->ctl_addr); + ap->last_ctl = tf->ctl; + } +} + +static void sata_rcar_exec_command(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command); + + iowrite32(tf->command, ap->ioaddr.command_addr); + ata_sff_pause(ap); +} + +static unsigned int sata_rcar_data_xfer(struct ata_device *dev, + unsigned char *buf, + unsigned int buflen, int rw) +{ + struct ata_port *ap = dev->link->ap; + void __iomem *data_addr = ap->ioaddr.data_addr; + unsigned int words = buflen >> 1; + + /* Transfer multiple of 2 bytes */ + if (rw == READ) + sata_rcar_ioread16_rep(data_addr, buf, words); + else + sata_rcar_iowrite16_rep(data_addr, buf, words); + + /* Transfer trailing byte, if any. */ + if (unlikely(buflen & 0x01)) { + unsigned char pad[2] = { }; + + /* Point buf to the tail of buffer */ + buf += buflen - 1; + + /* + * Use io*16_rep() accessors here as well to avoid pointlessly + * swapping bytes to and from on the big endian machines... + */ + if (rw == READ) { + sata_rcar_ioread16_rep(data_addr, pad, 1); + *buf = pad[0]; + } else { + pad[0] = *buf; + sata_rcar_iowrite16_rep(data_addr, pad, 1); + } + words++; + } + + return words << 1; +} + +static void sata_rcar_drain_fifo(struct ata_queued_cmd *qc) +{ + int count; + struct ata_port *ap; + + /* We only need to flush incoming data when a command was running */ + if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE) + return; + + ap = qc->ap; + /* Drain up to 64K of data before we give up this recovery method */ + for (count = 0; (ap->ops->sff_check_status(ap) & ATA_DRQ) && + count < 65536; count += 2) + ioread32(ap->ioaddr.data_addr); + + /* Can become DEBUG later */ + if (count) + ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count); +} + +static int sata_rcar_scr_read(struct ata_link *link, unsigned int sc_reg, + u32 *val) +{ + if (sc_reg > SCR_ACTIVE) + return -EINVAL; + + *val = ioread32(link->ap->ioaddr.scr_addr + (sc_reg << 2)); + return 0; +} + +static int sata_rcar_scr_write(struct ata_link *link, unsigned int sc_reg, + u32 val) +{ + if (sc_reg > SCR_ACTIVE) + return -EINVAL; + + iowrite32(val, link->ap->ioaddr.scr_addr + (sc_reg << 2)); + return 0; +} + +static void sata_rcar_bmdma_fill_sg(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_bmdma_prd *prd = ap->bmdma_prd; + struct scatterlist *sg; + unsigned int si; + + for_each_sg(qc->sg, sg, qc->n_elem, si) { + u32 addr, sg_len; + + /* + * Note: h/w doesn't support 64-bit, so we unconditionally + * truncate dma_addr_t to u32. + */ + addr = (u32)sg_dma_address(sg); + sg_len = sg_dma_len(sg); + + prd[si].addr = cpu_to_le32(addr); + prd[si].flags_len = cpu_to_le32(sg_len); + VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", si, addr, sg_len); + } + + /* end-of-table flag */ + prd[si - 1].addr |= cpu_to_le32(SATA_RCAR_DTEND); +} + +static void sata_rcar_qc_prep(struct ata_queued_cmd *qc) +{ + if (!(qc->flags & ATA_QCFLAG_DMAMAP)) + return; + + sata_rcar_bmdma_fill_sg(qc); +} + +static void sata_rcar_bmdma_setup(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int rw = qc->tf.flags & ATA_TFLAG_WRITE; + struct sata_rcar_priv *priv = ap->host->private_data; + void __iomem *base = priv->base; + u32 dmactl; + + /* load PRD table addr. */ + mb(); /* make sure PRD table writes are visible to controller */ + iowrite32(ap->bmdma_prd_dma, base + ATAPI_DTB_ADR_REG); + + /* specify data direction, triple-check start bit is clear */ + dmactl = ioread32(base + ATAPI_CONTROL1_REG); + dmactl &= ~(ATAPI_CONTROL1_RW | ATAPI_CONTROL1_STOP); + if (dmactl & ATAPI_CONTROL1_START) { + dmactl &= ~ATAPI_CONTROL1_START; + dmactl |= ATAPI_CONTROL1_STOP; + } + if (!rw) + dmactl |= ATAPI_CONTROL1_RW; + iowrite32(dmactl, base + ATAPI_CONTROL1_REG); + + /* issue r/w command */ + ap->ops->sff_exec_command(ap, &qc->tf); +} + +static void sata_rcar_bmdma_start(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct sata_rcar_priv *priv = ap->host->private_data; + void __iomem *base = priv->base; + u32 dmactl; + + /* start host DMA transaction */ + dmactl = ioread32(base + ATAPI_CONTROL1_REG); + dmactl &= ~ATAPI_CONTROL1_STOP; + dmactl |= ATAPI_CONTROL1_START; + iowrite32(dmactl, base + ATAPI_CONTROL1_REG); +} + +static void sata_rcar_bmdma_stop(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct sata_rcar_priv *priv = ap->host->private_data; + void __iomem *base = priv->base; + u32 dmactl; + + /* force termination of DMA transfer if active */ + dmactl = ioread32(base + ATAPI_CONTROL1_REG); + if (dmactl & ATAPI_CONTROL1_START) { + dmactl &= ~ATAPI_CONTROL1_START; + dmactl |= ATAPI_CONTROL1_STOP; + iowrite32(dmactl, base + ATAPI_CONTROL1_REG); + } + + /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ + ata_sff_dma_pause(ap); +} + +static u8 sata_rcar_bmdma_status(struct ata_port *ap) +{ + struct sata_rcar_priv *priv = ap->host->private_data; + u8 host_stat = 0; + u32 status; + + status = ioread32(priv->base + ATAPI_STATUS_REG); + if (status & ATAPI_STATUS_DEVINT) + host_stat |= ATA_DMA_INTR; + if (status & ATAPI_STATUS_ACT) + host_stat |= ATA_DMA_ACTIVE; + + return host_stat; +} + +static struct scsi_host_template sata_rcar_sht = { + ATA_BASE_SHT(DRV_NAME), + /* + * This controller allows transfer chunks up to 512MB which cross 64KB + * boundaries, therefore the DMA limits are more relaxed than standard + * ATA SFF. + */ + .sg_tablesize = ATA_MAX_PRD, + .dma_boundary = SATA_RCAR_DMA_BOUNDARY, +}; + +static struct ata_port_operations sata_rcar_port_ops = { + .inherits = &ata_bmdma_port_ops, + + .freeze = sata_rcar_freeze, + .thaw = sata_rcar_thaw, + .softreset = sata_rcar_softreset, + + .scr_read = sata_rcar_scr_read, + .scr_write = sata_rcar_scr_write, + + .sff_dev_select = sata_rcar_dev_select, + .sff_set_devctl = sata_rcar_set_devctl, + .sff_check_status = sata_rcar_check_status, + .sff_check_altstatus = sata_rcar_check_altstatus, + .sff_tf_load = sata_rcar_tf_load, + .sff_tf_read = sata_rcar_tf_read, + .sff_exec_command = sata_rcar_exec_command, + .sff_data_xfer = sata_rcar_data_xfer, + .sff_drain_fifo = sata_rcar_drain_fifo, + + .qc_prep = sata_rcar_qc_prep, + + .bmdma_setup = sata_rcar_bmdma_setup, + .bmdma_start = sata_rcar_bmdma_start, + .bmdma_stop = sata_rcar_bmdma_stop, + .bmdma_status = sata_rcar_bmdma_status, +}; + +static void sata_rcar_serr_interrupt(struct ata_port *ap) +{ + struct sata_rcar_priv *priv = ap->host->private_data; + struct ata_eh_info *ehi = &ap->link.eh_info; + int freeze = 0; + u32 serror; + + serror = ioread32(priv->base + SCRSERR_REG); + if (!serror) + return; + + DPRINTK("SError @host_intr: 0x%x\n", serror); + + /* first, analyze and record host port events */ + ata_ehi_clear_desc(ehi); + + if (serror & (SERR_DEV_XCHG | SERR_PHYRDY_CHG)) { + /* Setup a soft-reset EH action */ + ata_ehi_hotplugged(ehi); + ata_ehi_push_desc(ehi, "%s", "hotplug"); + + freeze = serror & SERR_COMM_WAKE ? 0 : 1; + } + + /* freeze or abort */ + if (freeze) + ata_port_freeze(ap); + else + ata_port_abort(ap); +} + +static void sata_rcar_ata_interrupt(struct ata_port *ap) +{ + struct ata_queued_cmd *qc; + int handled = 0; + + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (qc) + handled |= ata_bmdma_port_intr(ap, qc); + + /* be sure to clear ATA interrupt */ + if (!handled) + sata_rcar_check_status(ap); +} + +static irqreturn_t sata_rcar_interrupt(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; + unsigned int handled = 0; + struct ata_port *ap; + u32 sataintstat; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + sataintstat = ioread32(base + SATAINTSTAT_REG); + sataintstat &= SATA_RCAR_INT_MASK; + if (!sataintstat) + goto done; + /* ack */ + iowrite32(~sataintstat & 0x7ff, base + SATAINTSTAT_REG); + + ap = host->ports[0]; + + if (sataintstat & SATAINTSTAT_ATA) + sata_rcar_ata_interrupt(ap); + + if (sataintstat & SATAINTSTAT_SERR) + sata_rcar_serr_interrupt(ap); + + handled = 1; +done: + spin_unlock_irqrestore(&host->lock, flags); + + return IRQ_RETVAL(handled); +} + +static void sata_rcar_setup_port(struct ata_host *host) +{ + struct ata_port *ap = host->ports[0]; + struct ata_ioports *ioaddr = &ap->ioaddr; + struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; + + ap->ops = &sata_rcar_port_ops; + ap->pio_mask = ATA_PIO4; + ap->udma_mask = ATA_UDMA6; + ap->flags |= ATA_FLAG_SATA; + + ioaddr->cmd_addr = base + SDATA_REG; + ioaddr->ctl_addr = base + SSDEVCON_REG; + ioaddr->scr_addr = base + SCRSSTS_REG; + ioaddr->altstatus_addr = ioaddr->ctl_addr; + + ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << 2); + ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << 2); + ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << 2); + ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << 2); + ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << 2); + ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << 2); + ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << 2); + ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << 2); + ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << 2); + ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << 2); +} + +static void sata_rcar_init_controller(struct ata_host *host) +{ + struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; + u32 val; + + /* reset and setup phy */ + switch (priv->type) { + case RCAR_GEN1_SATA: + sata_rcar_gen1_phy_init(priv); + break; + case RCAR_GEN2_SATA: + sata_rcar_gen2_phy_init(priv); + break; + default: + dev_warn(host->dev, "SATA phy is not initialized\n"); + break; + } + + /* SATA-IP reset state */ + val = ioread32(base + ATAPI_CONTROL1_REG); + val |= ATAPI_CONTROL1_RESET; + iowrite32(val, base + ATAPI_CONTROL1_REG); + + /* ISM mode, PRD mode, DTEND flag at bit 0 */ + val = ioread32(base + ATAPI_CONTROL1_REG); + val |= ATAPI_CONTROL1_ISM; + val |= ATAPI_CONTROL1_DESE; + val |= ATAPI_CONTROL1_DTA32M; + iowrite32(val, base + ATAPI_CONTROL1_REG); + + /* Release the SATA-IP from the reset state */ + val = ioread32(base + ATAPI_CONTROL1_REG); + val &= ~ATAPI_CONTROL1_RESET; + iowrite32(val, base + ATAPI_CONTROL1_REG); + + /* ack and mask */ + iowrite32(0, base + SATAINTSTAT_REG); + iowrite32(0x7ff, base + SATAINTMASK_REG); + /* enable interrupts */ + iowrite32(ATAPI_INT_ENABLE_SATAINT, base + ATAPI_INT_ENABLE_REG); +} + +static struct of_device_id sata_rcar_match[] = { + { + /* Deprecated by "renesas,sata-r8a7779" */ + .compatible = "renesas,rcar-sata", + .data = (void *)RCAR_GEN1_SATA, + }, + { + .compatible = "renesas,sata-r8a7779", + .data = (void *)RCAR_GEN1_SATA, + }, + { + .compatible = "renesas,sata-r8a7790", + .data = (void *)RCAR_GEN2_SATA + }, + { + .compatible = "renesas,sata-r8a7791", + .data = (void *)RCAR_GEN2_SATA + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sata_rcar_match); + +static const struct platform_device_id sata_rcar_id_table[] = { + { "sata_rcar", RCAR_GEN1_SATA }, /* Deprecated by "sata-r8a7779" */ + { "sata-r8a7779", RCAR_GEN1_SATA }, + { "sata-r8a7790", RCAR_GEN2_SATA }, + { "sata-r8a7791", RCAR_GEN2_SATA }, + { }, +}; +MODULE_DEVICE_TABLE(platform, sata_rcar_id_table); + +static int sata_rcar_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + struct ata_host *host; + struct sata_rcar_priv *priv; + struct resource *mem; + int irq; + int ret = 0; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct sata_rcar_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + of_id = of_match_device(sata_rcar_match, &pdev->dev); + if (of_id) + priv->type = (enum sata_rcar_type)of_id->data; + else + priv->type = platform_get_device_id(pdev)->driver_data; + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "failed to get access to sata clock\n"); + return PTR_ERR(priv->clk); + } + clk_prepare_enable(priv->clk); + + host = ata_host_alloc(&pdev->dev, 1); + if (!host) { + dev_err(&pdev->dev, "ata_host_alloc failed\n"); + ret = -ENOMEM; + goto cleanup; + } + + host->private_data = priv; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(priv->base)) { + ret = PTR_ERR(priv->base); + goto cleanup; + } + + /* setup port */ + sata_rcar_setup_port(host); + + /* initialize host controller */ + sata_rcar_init_controller(host); + + ret = ata_host_activate(host, irq, sata_rcar_interrupt, 0, + &sata_rcar_sht); + if (!ret) + return 0; + +cleanup: + clk_disable_unprepare(priv->clk); + + return ret; +} + +static int sata_rcar_remove(struct platform_device *pdev) +{ + struct ata_host *host = platform_get_drvdata(pdev); + struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; + + ata_host_detach(host); + + /* disable interrupts */ + iowrite32(0, base + ATAPI_INT_ENABLE_REG); + /* ack and mask */ + iowrite32(0, base + SATAINTSTAT_REG); + iowrite32(0x7ff, base + SATAINTMASK_REG); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sata_rcar_suspend(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; + int ret; + + ret = ata_host_suspend(host, PMSG_SUSPEND); + if (!ret) { + /* disable interrupts */ + iowrite32(0, base + ATAPI_INT_ENABLE_REG); + /* mask */ + iowrite32(0x7ff, base + SATAINTMASK_REG); + + clk_disable_unprepare(priv->clk); + } + + return ret; +} + +static int sata_rcar_resume(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; + + clk_prepare_enable(priv->clk); + + /* ack and mask */ + iowrite32(0, base + SATAINTSTAT_REG); + iowrite32(0x7ff, base + SATAINTMASK_REG); + /* enable interrupts */ + iowrite32(ATAPI_INT_ENABLE_SATAINT, base + ATAPI_INT_ENABLE_REG); + + ata_host_resume(host); + + return 0; +} + +static const struct dev_pm_ops sata_rcar_pm_ops = { + .suspend = sata_rcar_suspend, + .resume = sata_rcar_resume, +}; +#endif + +static struct platform_driver sata_rcar_driver = { + .probe = sata_rcar_probe, + .remove = sata_rcar_remove, + .id_table = sata_rcar_id_table, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = sata_rcar_match, +#ifdef CONFIG_PM_SLEEP + .pm = &sata_rcar_pm_ops, +#endif + }, +}; + +module_platform_driver(sata_rcar_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("Renesas R-Car SATA controller low level driver"); diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 9dfb40b8c2c..40b76b2d18c 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -1,7 +1,7 @@ /* * sata_sil.c - Silicon Image SATA * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -113,7 +112,7 @@ enum { }; static int sil_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sil_pci_device_resume(struct pci_dev *pdev); #endif static void sil_dev_config(struct ata_device *dev); @@ -157,6 +156,7 @@ static const struct sil_drivelist { { "ST380011ASL", SIL_QUIRK_MOD15WRITE }, { "ST3120022ASL", SIL_QUIRK_MOD15WRITE }, { "ST3160021ASL", SIL_QUIRK_MOD15WRITE }, + { "TOSHIBA MK2561GSYN", SIL_QUIRK_MOD15WRITE }, { "Maxtor 4D060H3", SIL_QUIRK_UDMA5MAX }, { } }; @@ -166,7 +166,7 @@ static struct pci_driver sil_pci_driver = { .id_table = sil_pci_tbl, .probe = sil_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = sil_pci_device_resume, #endif @@ -802,10 +802,10 @@ static int sil_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) &sil_sht); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sil_pci_device_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); int rc; rc = ata_pci_device_do_resume(pdev); @@ -819,16 +819,4 @@ static int sil_pci_device_resume(struct pci_dev *pdev) } #endif -static int __init sil_init(void) -{ - return pci_register_driver(&sil_pci_driver); -} - -static void __exit sil_exit(void) -{ - pci_unregister_driver(&sil_pci_driver); -} - - -module_init(sil_init); -module_exit(sil_exit); +module_pci_driver(sil_pci_driver); diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index e7e610aa9a7..0534890f118 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -353,8 +353,10 @@ static void sil24_error_handler(struct ata_port *ap); static void sil24_post_internal_cmd(struct ata_queued_cmd *qc); static int sil24_port_start(struct ata_port *ap); static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sil24_pci_device_resume(struct pci_dev *pdev); +#endif +#ifdef CONFIG_PM static int sil24_port_resume(struct ata_port *ap); #endif @@ -375,7 +377,7 @@ static struct pci_driver sil24_pci_driver = { .id_table = sil24_pci_tbl, .probe = sil24_init_one, .remove = ata_pci_remove_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = sil24_pci_device_resume, #endif @@ -506,8 +508,6 @@ static int sil24_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val) void __iomem *scr_addr = sil24_port_base(link->ap) + PORT_SCONTROL; if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { - void __iomem *addr; - addr = scr_addr + sil24_scr_map[sc_reg] * 4; *val = readl(scr_addr + sil24_scr_map[sc_reg] * 4); return 0; } @@ -519,8 +519,6 @@ static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val) void __iomem *scr_addr = sil24_port_base(link->ap) + PORT_SCONTROL; if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { - void __iomem *addr; - addr = scr_addr + sil24_scr_map[sc_reg] * 4; writel(val, scr_addr + sil24_scr_map[sc_reg] * 4); return 0; } @@ -1354,10 +1352,10 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) &sil24_sht); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sil24_pci_device_resume(struct pci_dev *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = pci_get_drvdata(pdev); void __iomem *host_base = host->iomap[SIL24_HOST_BAR]; int rc; @@ -1374,7 +1372,9 @@ static int sil24_pci_device_resume(struct pci_dev *pdev) return 0; } +#endif +#ifdef CONFIG_PM static int sil24_port_resume(struct ata_port *ap) { sil24_config_pmp(ap, ap->nr_pmp_links); @@ -1382,20 +1382,9 @@ static int sil24_port_resume(struct ata_port *ap) } #endif -static int __init sil24_init(void) -{ - return pci_register_driver(&sil24_pci_driver); -} - -static void __exit sil24_exit(void) -{ - pci_unregister_driver(&sil24_pci_driver); -} +module_pci_driver(sil24_pci_driver); MODULE_AUTHOR("Tejun Heo"); MODULE_DESCRIPTION("Silicon Image 3124/3132 SATA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, sil24_pci_tbl); - -module_init(sil24_init); -module_exit(sil24_exit); diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index 95ec435f0eb..d1637ac40a7 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -33,7 +33,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -83,6 +82,10 @@ static struct pci_driver sis_pci_driver = { .id_table = sis_pci_tbl, .probe = sis_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM_SLEEP + .suspend = ata_pci_device_suspend, + .resume = ata_pci_device_resume, +#endif }; static struct scsi_host_template sis_sht = { @@ -308,15 +311,4 @@ static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) IRQF_SHARED, &sis_sht); } -static int __init sis_init(void) -{ - return pci_register_driver(&sis_pci_driver); -} - -static void __exit sis_exit(void) -{ - pci_unregister_driver(&sis_pci_driver); -} - -module_init(sis_init); -module_exit(sis_exit); +module_pci_driver(sis_pci_driver); diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index c646118943f..c630fa81262 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -39,7 +39,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -142,6 +141,39 @@ static int k2_sata_scr_write(struct ata_link *link, return 0; } +static int k2_sata_softreset(struct ata_link *link, + unsigned int *class, unsigned long deadline) +{ + u8 dmactl; + void __iomem *mmio = link->ap->ioaddr.bmdma_addr; + + dmactl = readb(mmio + ATA_DMA_CMD); + + /* Clear the start bit */ + if (dmactl & ATA_DMA_START) { + dmactl &= ~ATA_DMA_START; + writeb(dmactl, mmio + ATA_DMA_CMD); + } + + return ata_sff_softreset(link, class, deadline); +} + +static int k2_sata_hardreset(struct ata_link *link, + unsigned int *class, unsigned long deadline) +{ + u8 dmactl; + void __iomem *mmio = link->ap->ioaddr.bmdma_addr; + + dmactl = readb(mmio + ATA_DMA_CMD); + + /* Clear the start bit */ + if (dmactl & ATA_DMA_START) { + dmactl &= ~ATA_DMA_START; + writeb(dmactl, mmio + ATA_DMA_CMD); + } + + return sata_sff_hardreset(link, class, deadline); +} static void k2_sata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) { @@ -289,23 +321,11 @@ static u8 k2_stat_check_status(struct ata_port *ap) } #ifdef CONFIG_PPC_OF -/* - * k2_sata_proc_info - * inout : decides on the direction of the dataflow and the meaning of the - * variables - * buffer: If inout==FALSE data is being written to it else read from it - * *start: If inout==FALSE start of the valid data in the buffer - * offset: If inout==FALSE offset from the beginning of the imaginary file - * from which we start writing into the buffer - * length: If inout==FALSE max number of bytes to be written into the buffer - * else number of bytes in the buffer - */ -static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start, - off_t offset, int count, int inout) +static int k2_sata_show_info(struct seq_file *m, struct Scsi_Host *shost) { struct ata_port *ap; struct device_node *np; - int len, index; + int index; /* Find the ata_port */ ap = ata_shost_to_port(shost); @@ -323,15 +343,12 @@ static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start, const u32 *reg = of_get_property(np, "reg", NULL); if (!reg) continue; - if (index == *reg) + if (index == *reg) { + seq_printf(m, "devspec: %s\n", np->full_name); break; + } } - if (np == NULL) - return 0; - - len = sprintf(page, "devspec: %s\n", np->full_name); - - return len; + return 0; } #endif /* CONFIG_PPC_OF */ @@ -339,13 +356,15 @@ static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start, static struct scsi_host_template k2_sata_sht = { ATA_BMDMA_SHT(DRV_NAME), #ifdef CONFIG_PPC_OF - .proc_info = k2_sata_proc_info, + .show_info = k2_sata_show_info, #endif }; static struct ata_port_operations k2_sata_ops = { .inherits = &ata_bmdma_port_ops, + .softreset = k2_sata_softreset, + .hardreset = k2_sata_hardreset, .sff_tf_load = k2_sata_tf_load, .sff_tf_read = k2_sata_tf_read, .sff_check_status = k2_stat_check_status, @@ -525,21 +544,10 @@ static struct pci_driver k2_sata_pci_driver = { .remove = ata_pci_remove_one, }; -static int __init k2_sata_init(void) -{ - return pci_register_driver(&k2_sata_pci_driver); -} - -static void __exit k2_sata_exit(void) -{ - pci_unregister_driver(&k2_sata_pci_driver); -} +module_pci_driver(k2_sata_pci_driver); MODULE_AUTHOR("Benjamin Herrenschmidt"); MODULE_DESCRIPTION("low-level driver for K2 SATA controller"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, k2_sata_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(k2_sata_init); -module_exit(k2_sata_exit); diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index cdaebbe3d18..39b5de60a1f 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -1,7 +1,7 @@ /* * sata_sx4.c - Promise SATA * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -82,7 +82,6 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -315,9 +314,8 @@ static int pdc_port_start(struct ata_port *ap) return 0; } -static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf, - unsigned int portno, - unsigned int total_len) +static inline void pdc20621_ata_sg(u8 *buf, unsigned int portno, + unsigned int total_len) { u32 addr; unsigned int dw = PDC_DIMM_APKT_PRD >> 2; @@ -337,9 +335,8 @@ static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf, buf32[dw], buf32[dw + 1]); } -static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf, - unsigned int portno, - unsigned int total_len) +static inline void pdc20621_host_sg(u8 *buf, unsigned int portno, + unsigned int total_len) { u32 addr; unsigned int dw = PDC_DIMM_HPKT_PRD >> 2; @@ -486,10 +483,10 @@ static void pdc20621_dma_prep(struct ata_queued_cmd *qc) /* * Build ATA, host DMA packets */ - pdc20621_host_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); + pdc20621_host_sg(&pp->dimm_buf[0], portno, total_len); pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno); - pdc20621_ata_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); + pdc20621_ata_sg(&pp->dimm_buf[0], portno, total_len); i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno); if (qc->tf.flags & ATA_TFLAG_LBA48) @@ -1023,8 +1020,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, idx++; dist = ((long) (window_size - (offset + size))) >= 0 ? size : (long) (window_size - offset); - memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4), - dist); + memcpy_fromio(psource, dimm_mmio + offset / 4, dist); psource += dist; size -= dist; @@ -1033,8 +1029,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, readl(mmio + PDC_GENERAL_CTLR); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), - window_size / 4); + memcpy_fromio(psource, dimm_mmio, window_size / 4); psource += window_size; size -= window_size; idx++; @@ -1045,8 +1040,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, readl(mmio + PDC_GENERAL_CTLR); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), - size / 4); + memcpy_fromio(psource, dimm_mmio, size / 4); } } #endif @@ -1498,24 +1492,10 @@ static int pdc_sata_init_one(struct pci_dev *pdev, IRQF_SHARED, &pdc_sata_sht); } - -static int __init pdc_sata_init(void) -{ - return pci_register_driver(&pdc_sata_pci_driver); -} - - -static void __exit pdc_sata_exit(void) -{ - pci_unregister_driver(&pdc_sata_pci_driver); -} - +module_pci_driver(pdc_sata_pci_driver); MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("Promise SATA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(pdc_sata_init); -module_exit(pdc_sata_exit); diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index b54ebfcdda3..08f98c3ed5c 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -28,7 +28,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -243,16 +242,4 @@ static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) IRQF_SHARED, &uli_sht); } -static int __init uli_init(void) -{ - return pci_register_driver(&uli_pci_driver); -} - -static void __exit uli_exit(void) -{ - pci_unregister_driver(&uli_pci_driver); -} - - -module_init(uli_init); -module_exit(uli_exit); +module_pci_driver(uli_pci_driver); diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index f93e43b0ccd..47bf89464ce 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -1,7 +1,7 @@ /* * sata_via.c - VIA Serial ATA controllers * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -36,7 +36,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> @@ -104,7 +103,7 @@ static struct pci_driver svia_pci_driver = { .name = DRV_NAME, .id_table = svia_pci_tbl, .probe = svia_init_one, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, #endif @@ -655,15 +654,4 @@ static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) IRQF_SHARED, &svia_sht); } -static int __init svia_init(void) -{ - return pci_register_driver(&svia_pci_driver); -} - -static void __exit svia_exit(void) -{ - pci_unregister_driver(&svia_pci_driver); -} - -module_init(svia_init); -module_exit(svia_exit); +module_pci_driver(svia_pci_driver); diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index 6135a528869..29e847aac34 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -312,8 +311,7 @@ static struct ata_port_operations vsc_sata_ops = { .scr_write = vsc_sata_scr_write, }; -static void __devinit vsc_sata_setup_port(struct ata_ioports *port, - void __iomem *base) +static void vsc_sata_setup_port(struct ata_ioports *port, void __iomem *base) { port->cmd_addr = base + VSC_SATA_TF_CMD_OFFSET; port->data_addr = base + VSC_SATA_TF_DATA_OFFSET; @@ -335,8 +333,8 @@ static void __devinit vsc_sata_setup_port(struct ata_ioports *port, } -static int __devinit vsc_sata_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int vsc_sata_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static const struct ata_port_info pi = { .flags = ATA_FLAG_SATA, @@ -436,21 +434,10 @@ static struct pci_driver vsc_sata_pci_driver = { .remove = ata_pci_remove_one, }; -static int __init vsc_sata_init(void) -{ - return pci_register_driver(&vsc_sata_pci_driver); -} - -static void __exit vsc_sata_exit(void) -{ - pci_unregister_driver(&vsc_sata_pci_driver); -} +module_pci_driver(vsc_sata_pci_driver); MODULE_AUTHOR("Jeremy Higdon"); MODULE_DESCRIPTION("low-level driver for Vitesse VSC7174 SATA controller"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, vsc_sata_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(vsc_sata_init); -module_exit(vsc_sata_exit); |
