From 7faa33da9b7add01db9f1ad92c6a5d9145e940a7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 22 Jul 2011 11:41:26 +0200 Subject: ahci: start engine only during soft/hard resets This is another attempt at fixing the same problem that 270dac35c2 (libata: ahci_start_engine compliant to AHCI spec) tried to solve. Unfortunately, 270dac35c2 created regressions for a lot more common controllers and got reverted. This specific AHCI IP block becomes a brick if the DMA engine is started while DRQ is set. It is not possible to avoid the condition completely but the most common occurrence is caused by spurious use of ahci_start_engine() from ahci_start_port() during init sequence. DMA engine is started after both soft and hard resets and ahci_start_port() is always followed by resets, so there is no reason to start DMA engine from ahci_start_port(). This patch removes ahci_start_engine() invocation from ahci_start_port(). This change makes failure path of ahci_port_suspend() leave engine stopped without following resets. This is resolved by replacing ahci_start_port() call with ata_port_freeze() which forces resets afterwards, which is the better behavior anyway. Signed-off-by: Tejun Heo Reported-by: Brian Norris Reported-by: Jian Peng Signed-off-by: Jeff Garzik --- drivers/ata/libahci.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 3c92dbd751e..a72bfd0ecfe 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -746,9 +746,6 @@ static void ahci_start_port(struct ata_port *ap) /* enable FIS reception */ ahci_start_fis_rx(ap); - /* enable DMA */ - ahci_start_engine(ap); - /* turn on LEDs */ if (ap->flags & ATA_FLAG_EM) { ata_for_each_link(link, ap, EDGE) { @@ -2022,7 +2019,7 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) ahci_power_down(ap); else { ata_port_err(ap, "%s (%d)\n", emsg, rc); - ahci_start_port(ap); + ata_port_freeze(ap); } return rc; -- cgit v1.2.3 From 9a6d6a2ddabbd32c07f6a38b659e5f3db319fa5a Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 5 Dec 2011 09:20:23 +0800 Subject: ata: make ata port as parent device of scsi host Currently, the device tree of ata port and scsi host looks as below, /sys/devices/pci0000:00/0000:00:1f.2 (ahci controller) |-- ata1 (ata port) |-- host0 (scsi host) |-- target0:0:0 (scsi target) |-- 0:0:0:0 (disk) This patch makes ata port as parent device of scsi host, then it becomes /sys/devices/pci0000:00/0000:00:1f.2 (ahci controller) |-- ata1 (ata port) |-- host0 (scsi host) |-- target0:0:0 (scsi target) |-- 0:0:0:0 (disk) With this change, the ata port runtime PM is easier. For example, the ata port runtime suspend will happen as, disk suspend --> scsi target suspend --> scsi host suspend --> ata port suspend. Acked-by: Tejun Heo Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 2a5412e7e9c..7ae1e771d95 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3398,7 +3398,7 @@ 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->host->dev); + rc = scsi_add_host(ap->scsi_host, &ap->tdev); if (rc) goto err_add; } -- cgit v1.2.3 From ae0751ffc77e7f21629970fdab5528c573e637f8 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 5 Dec 2011 09:20:24 +0800 Subject: [SCSI] add flag to skip the runtime PM calls on the host With previous change, now the ata port runtime suspend will happen as: disk suspend --> scsi target suspend --> scsi host suspend --> ata port suspend ata port(parent device) suspend need to schedule scsi EH which will resume scsi host(child device). Then the child device resume will in turn make parent device resume first. This is kind of recursive. This patch adds a new flag Scsi_Host::eh_noresume. ata port will set this flag to skip the runtime PM calls on scsi host. Acked-by: Alan Stern Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/scsi/scsi_error.c | 5 +++-- include/scsi/scsi_host.h | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index dc6131e6a1b..5f84a148eb1 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -1812,7 +1812,7 @@ int scsi_error_handler(void *data) * what we need to do to get it up and online again (if we can). * If we fail, we end up taking the thing offline. */ - if (scsi_autopm_get_host(shost) != 0) { + if (!shost->eh_noresume && scsi_autopm_get_host(shost) != 0) { SCSI_LOG_ERROR_RECOVERY(1, printk(KERN_ERR "Error handler scsi_eh_%d " "unable to autoresume\n", @@ -1833,7 +1833,8 @@ int scsi_error_handler(void *data) * which are still online. */ scsi_restart_operations(shost); - scsi_autopm_put_host(shost); + if (!shost->eh_noresume) + scsi_autopm_put_host(shost); set_current_state(TASK_INTERRUPTIBLE); } __set_current_state(TASK_RUNNING); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 50266c9405f..5f7d5b3b1c6 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -669,6 +669,9 @@ struct Scsi_Host { /* Asynchronous scan in progress */ unsigned async_scan:1; + /* Don't resume host in EH */ + unsigned eh_noresume:1; + /* * Optional work queue to be utilized by the transport */ -- cgit v1.2.3 From 286405167e815adc4ae0132214246a350b0208bc Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 5 Dec 2011 09:20:25 +0800 Subject: [SCSI] check runtime PM status in system PM The only high-level SCSI driver that currently implements runtime PM is sd, and sd treats runtime suspend exactly the same as the SUSPEND and HIBERNATE stages of system sleep, but not the same as the FREEZE stage. Therefore, when entering the SUSPEND or HIBERNATE stages of system sleep, we can skip the callback to the driver if the device is already in runtime suspend. When entering the FREEZE stage, however, we should first issue a runtime resume. The overhead of doing this is negligible, because a suspended drive would be spun up during the THAW stage of hibernation anyway. Signed-off-by: Lin Ming Signed-off-by: Alan Stern Signed-off-by: Jeff Garzik --- drivers/scsi/scsi_pm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index d329f8b12e2..a633076e562 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -49,8 +49,22 @@ static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg) { int err = 0; - if (scsi_is_sdev_device(dev)) + if (scsi_is_sdev_device(dev)) { + /* + * sd is the only high-level SCSI driver to implement runtime + * PM, and sd treats runtime suspend, system suspend, and + * system hibernate identically (but not system freeze). + */ + if (pm_runtime_suspended(dev)) { + if (msg.event == PM_EVENT_SUSPEND || + msg.event == PM_EVENT_HIBERNATE) + return 0; /* already suspended */ + + /* wake up device so that FREEZE will succeed */ + pm_runtime_resume(dev); + } err = scsi_dev_type_suspend(dev, msg); + } return err; } -- cgit v1.2.3 From 54f57588463db1105f4a93b2902a6f95cb8f796a Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 5 Dec 2011 09:20:26 +0800 Subject: [SCSI] sd: check runtime PM status in sd_shutdown sd_shutdown is called during reboot/poweroff. It may fail if parent device, for example, ata port, was runtime suspended. Fix it by checking runtime PM status of sd. Exit immediately if sd was runtime suspended already. Acked-by: Alan Stern Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/scsi/sd.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index fa3a5918009..7b3f8075e2a 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -2741,6 +2742,9 @@ static void sd_shutdown(struct device *dev) if (!sdkp) return; /* this can happen */ + if (pm_runtime_suspended(dev)) + goto exit; + if (sdkp->WCE) { sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n"); sd_sync_cache(sdkp); @@ -2751,6 +2755,7 @@ static void sd_shutdown(struct device *dev) sd_start_stop_device(sdkp, 0); } +exit: scsi_disk_put(sdkp); } -- cgit v1.2.3 From 5ef41082912bdfcb33fa53b8dba2ad17dea2ef90 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 5 Dec 2011 09:20:27 +0800 Subject: ata: add ata port system PM callbacks Change ata_host_request_pm to ata_port_request_pm which performs port suspend/resume. Add ata port type driver which implements port PM callbacks. Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 144 +++++++++++++++++++++-------------------- drivers/ata/libata-transport.c | 1 + drivers/ata/libata.h | 1 + 3 files changed, 76 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c04ad68cb60..04c208e3cec 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5234,112 +5234,116 @@ bool ata_link_offline(struct ata_link *link) } #ifdef CONFIG_PM -static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg, +static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, unsigned int action, unsigned int ehi_flags, int wait) { + struct ata_link *link; unsigned long flags; - int i, rc; - - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - struct ata_link *link; + int rc; - /* Previous resume operation might still be in - * progress. Wait for PM_PENDING to clear. - */ - if (ap->pflags & ATA_PFLAG_PM_PENDING) { - ata_port_wait_eh(ap); - WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); - } + /* Previous resume operation might still be in + * progress. Wait for PM_PENDING to clear. + */ + if (ap->pflags & ATA_PFLAG_PM_PENDING) { + ata_port_wait_eh(ap); + WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); + } - /* request PM ops to EH */ - spin_lock_irqsave(ap->lock, flags); + /* request PM ops to EH */ + spin_lock_irqsave(ap->lock, flags); - ap->pm_mesg = mesg; - if (wait) { - rc = 0; - ap->pm_result = &rc; - } + 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; - link->eh_info.flags |= ehi_flags; - } + ap->pflags |= ATA_PFLAG_PM_PENDING; + ata_for_each_link(link, ap, HOST_FIRST) { + link->eh_info.action |= action; + link->eh_info.flags |= ehi_flags; + } - ata_port_schedule_eh(ap); + ata_port_schedule_eh(ap); - spin_unlock_irqrestore(ap->lock, flags); + spin_unlock_irqrestore(ap->lock, flags); - /* wait and check result */ - if (wait) { - ata_port_wait_eh(ap); - WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); - if (rc) - return rc; - } + /* wait and check result */ + if (wait) { + ata_port_wait_eh(ap); + WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); } - return 0; + return rc; } +#define to_ata_port(d) container_of(d, struct ata_port, tdev) + +static int ata_port_suspend_common(struct device *dev) +{ + struct ata_port *ap = to_ata_port(dev); + int rc; + + rc = ata_port_request_pm(ap, PMSG_SUSPEND, 0, ATA_EHI_QUIET, 1); + return rc; +} + +static int ata_port_suspend(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + + return ata_port_suspend_common(dev); +} + +static int ata_port_resume(struct device *dev) +{ + struct ata_port *ap = to_ata_port(dev); + int rc; + + rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET, + ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 1); + return rc; +} + +static const struct dev_pm_ops ata_port_pm_ops = { + .suspend = ata_port_suspend, + .resume = ata_port_resume, +}; + /** * ata_host_suspend - suspend host * @host: host to suspend * @mesg: PM message * - * Suspend @host. Actual operation is performed by EH. This - * function requests EH to perform PM operations and waits for EH - * to finish. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno on failure. + * Suspend @host. Actual operation is performed by port suspend. */ int ata_host_suspend(struct ata_host *host, pm_message_t mesg) { - 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; - - rc = ata_host_request_pm(host, mesg, 0, ehi_flags, 1); - if (rc == 0) - host->dev->power.power_state = mesg; - return rc; + host->dev->power.power_state = mesg; + return 0; } /** * ata_host_resume - resume host * @host: host to resume * - * Resume @host. Actual operation is performed by EH. This - * function requests EH to perform PM operations and returns. - * Note that all resume operations are performed parallelly. - * - * LOCKING: - * Kernel thread context (may sleep). + * Resume @host. Actual operation is performed by port resume. */ void ata_host_resume(struct ata_host *host) { - ata_host_request_pm(host, PMSG_ON, ATA_EH_RESET, - ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); host->dev->power.power_state = PMSG_ON; } #endif +struct device_type ata_port_type = { + .name = "ata_port", +#ifdef CONFIG_PM + .pm = &ata_port_pm_ops, +#endif +}; + /** * ata_dev_init - Initialize an ata_device structure * @dev: Device structure to initialize diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index ce9dc6207f3..3ceb3d94be2 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -279,6 +279,7 @@ int ata_tport_add(struct device *parent, struct device *dev = &ap->tdev; device_initialize(dev); + dev->type = &ata_port_type; dev->parent = get_device(parent); dev->release = ata_tport_release; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 773de97988a..814486d35c4 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -58,6 +58,7 @@ extern int atapi_passthru16; extern int libata_fua; extern int libata_noacpi; extern int libata_allow_tpm; +extern struct device_type ata_port_type; extern struct ata_link *ata_dev_phys_link(struct ata_device *dev); extern void ata_force_cbl(struct ata_port *ap); extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); -- cgit v1.2.3 From 9ee4f3933930abf5cc34f8e9d69fe0e08c18f602 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 5 Dec 2011 09:20:28 +0800 Subject: ata: add ata port runtime PM callbacks Add ata port runtime suspend/resume/idle callbacks. Set ->eh_noresume to skip the runtime PM calls on scsi host in the error handler to avoid dead lock. Acked-by: Tejun Heo Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 10 ++++++++++ drivers/ata/libata-scsi.c | 1 + drivers/ata/libata-transport.c | 4 ++++ 3 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 04c208e3cec..15a3d4deb27 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -66,6 +66,7 @@ #include #include #include +#include #include "libata.h" #include "libata-transport.h" @@ -5307,9 +5308,18 @@ static int ata_port_resume(struct device *dev) return rc; } +static int ata_port_runtime_idle(struct device *dev) +{ + return pm_runtime_suspend(dev); +} + static const struct dev_pm_ops ata_port_pm_ops = { .suspend = ata_port_suspend, .resume = ata_port_resume, + + .runtime_suspend = ata_port_suspend_common, + .runtime_resume = ata_port_resume, + .runtime_idle = ata_port_runtime_idle, }; /** diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 7ae1e771d95..508a60bfe5c 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3381,6 +3381,7 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) if (!shost) goto err_alloc; + shost->eh_noresume = 1; *(struct ata_port **)&shost->hostdata[0] = ap; ap->scsi_host = shost; diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 3ceb3d94be2..9a7f0ea565d 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "libata.h" #include "libata-transport.h" @@ -290,6 +291,9 @@ int ata_tport_add(struct device *parent, goto tport_err; } + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + transport_add_device(dev); transport_configure_device(dev); -- cgit v1.2.3 From 002ae08448e241ea8e304008f9cb4bc836f9ff16 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 2 Dec 2011 18:39:53 +0300 Subject: libata-sff: use ATAPI_{COD|IO} atapi_pio_bytes() uses bare numbers for the ATAPI interrupt reason bits despite these are #define'd in . Signed-off-by: Sergei Shtylyov Signed-off-by: Jeff Garzik --- drivers/ata/libata-sff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 4cadfa28f94..9691dd0966d 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -929,11 +929,11 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc) bytes = (bc_hi << 8) | bc_lo; /* shall be cleared to zero, indicating xfer of data */ - if (unlikely(ireason & (1 << 0))) + if (unlikely(ireason & ATAPI_COD)) goto atapi_check; /* make sure transfer direction matches expected */ - i_write = ((ireason & (1 << 1)) == 0) ? 1 : 0; + i_write = ((ireason & ATAPI_IO) == 0) ? 1 : 0; if (unlikely(do_write != i_write)) goto atapi_check; -- cgit v1.2.3 From d63e94a442df14c4e7b02763c3230374218b41f1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 13 Oct 2011 13:05:24 +0200 Subject: pata_cs5536: forward port changes from cs5536 * Fix cable detection to also account for the slave device cable bit. * Disable UDMA when programming MWDMA in cs5536_set_dmamode(). * Don't change UDMA settings in cs5536_set_piomode(). * Add cs5536_program_dtc() helper. * Cleanup and uninline cs5536_[read,write]() methods. Cc: Martin K. Petersen Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Jeff Garzik --- drivers/ata/pata_cs5536.c | 99 ++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c index 628c8fae593..7a402c75ab9 100644 --- a/drivers/ata/pata_cs5536.c +++ b/drivers/ata/pata_cs5536.c @@ -1,6 +1,7 @@ /* * pata_cs5536.c - CS5536 PATA for new ATA layer * (C) 2007 Martin K. Petersen + * (C) 2011 Bartlomiej Zolnierkiewicz * * 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 @@ -55,24 +56,16 @@ MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)"); #define DRV_VERSION "0.0.8" enum { - CFG = 0, - DTC = 1, - CAST = 2, - ETC = 3, - - MSR_IDE_BASE = 0x51300000, - MSR_IDE_CFG = (MSR_IDE_BASE + 0x10), - MSR_IDE_DTC = (MSR_IDE_BASE + 0x12), - MSR_IDE_CAST = (MSR_IDE_BASE + 0x13), - MSR_IDE_ETC = (MSR_IDE_BASE + 0x14), - + MSR_IDE_CFG = 0x51300010, PCI_IDE_CFG = 0x40, - PCI_IDE_DTC = 0x48, - PCI_IDE_CAST = 0x4c, - PCI_IDE_ETC = 0x50, - IDE_CFG_CHANEN = 0x2, - IDE_CFG_CABLE = 0x10000, + CFG = 0, + DTC = 2, + CAST = 3, + ETC = 4, + + IDE_CFG_CHANEN = (1 << 1), + IDE_CFG_CABLE = (1 << 17) | (1 << 16), IDE_D0_SHIFT = 24, IDE_D1_SHIFT = 16, @@ -84,45 +77,50 @@ enum { IDE_CAST_CMD_MASK = 0xff, IDE_CAST_CMD_SHIFT = 24, - IDE_ETC_NODMA = 0x03, -}; - -static const u32 msr_reg[4] = { - MSR_IDE_CFG, MSR_IDE_DTC, MSR_IDE_CAST, MSR_IDE_ETC, -}; - -static const u8 pci_reg[4] = { - PCI_IDE_CFG, PCI_IDE_DTC, PCI_IDE_CAST, PCI_IDE_ETC, + IDE_ETC_UDMA_MASK = 0xc0, }; -static inline int cs5536_read(struct pci_dev *pdev, int reg, u32 *val) +static int cs5536_read(struct pci_dev *pdev, int reg, u32 *val) { if (unlikely(use_msr)) { u32 dummy __maybe_unused; - rdmsr(msr_reg[reg], *val, dummy); + rdmsr(MSR_IDE_CFG + reg, *val, dummy); return 0; } - return pci_read_config_dword(pdev, pci_reg[reg], val); + return pci_read_config_dword(pdev, PCI_IDE_CFG + reg * 4, val); } -static inline int cs5536_write(struct pci_dev *pdev, int reg, int val) +static int cs5536_write(struct pci_dev *pdev, int reg, int val) { if (unlikely(use_msr)) { - wrmsr(msr_reg[reg], val, 0); + wrmsr(MSR_IDE_CFG + reg, val, 0); return 0; } - return pci_write_config_dword(pdev, pci_reg[reg], val); + return pci_write_config_dword(pdev, PCI_IDE_CFG + reg * 4, val); +} + +static void cs5536_program_dtc(struct ata_device *adev, u8 tim) +{ + struct pci_dev *pdev = to_pci_dev(adev->link->ap->host->dev); + int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; + u32 dtc; + + cs5536_read(pdev, DTC, &dtc); + dtc &= ~(IDE_DRV_MASK << dshift); + dtc |= tim << dshift; + cs5536_write(pdev, DTC, dtc); } /** * cs5536_cable_detect - detect cable type * @ap: Port to detect on * - * Perform cable detection for ATA66 capable cable. Return a libata - * cable type. + * Perform cable detection for ATA66 capable cable. + * + * Returns a cable type. */ static int cs5536_cable_detect(struct ata_port *ap) @@ -132,7 +130,7 @@ static int cs5536_cable_detect(struct ata_port *ap) cs5536_read(pdev, CFG, &cfg); - if (cfg & (IDE_CFG_CABLE << ap->port_no)) + if (cfg & IDE_CFG_CABLE) return ATA_CBL_PATA80; else return ATA_CBL_PATA40; @@ -162,19 +160,15 @@ static void cs5536_set_piomode(struct ata_port *ap, struct ata_device *adev) struct ata_device *pair = ata_dev_pair(adev); int mode = adev->pio_mode - XFER_PIO_0; int cmdmode = mode; - int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; int cshift = adev->devno ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT; - u32 dtc, cast, etc; + u32 cast; if (pair) cmdmode = min(mode, pair->pio_mode - XFER_PIO_0); - cs5536_read(pdev, DTC, &dtc); - cs5536_read(pdev, CAST, &cast); - cs5536_read(pdev, ETC, &etc); + cs5536_program_dtc(adev, drv_timings[mode]); - dtc &= ~(IDE_DRV_MASK << dshift); - dtc |= drv_timings[mode] << dshift; + cs5536_read(pdev, CAST, &cast); cast &= ~(IDE_CAST_DRV_MASK << cshift); cast |= addr_timings[mode] << cshift; @@ -182,12 +176,7 @@ static void cs5536_set_piomode(struct ata_port *ap, struct ata_device *adev) cast &= ~(IDE_CAST_CMD_MASK << IDE_CAST_CMD_SHIFT); cast |= cmd_timings[cmdmode] << IDE_CAST_CMD_SHIFT; - etc &= ~(IDE_DRV_MASK << dshift); - etc |= IDE_ETC_NODMA << dshift; - - cs5536_write(pdev, DTC, dtc); cs5536_write(pdev, CAST, cast); - cs5536_write(pdev, ETC, etc); } /** @@ -208,25 +197,21 @@ static void cs5536_set_dmamode(struct ata_port *ap, struct ata_device *adev) }; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u32 dtc, etc; + u32 etc; int mode = adev->dma_mode; int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; - if (mode >= XFER_UDMA_0) { - cs5536_read(pdev, ETC, &etc); + cs5536_read(pdev, ETC, &etc); + if (mode >= XFER_UDMA_0) { etc &= ~(IDE_DRV_MASK << dshift); etc |= udma_timings[mode - XFER_UDMA_0] << dshift; - - cs5536_write(pdev, ETC, etc); } else { /* MWDMA */ - cs5536_read(pdev, DTC, &dtc); - - dtc &= ~(IDE_DRV_MASK << dshift); - dtc |= mwdma_timings[mode - XFER_MW_DMA_0] << dshift; - - cs5536_write(pdev, DTC, dtc); + etc &= ~(IDE_ETC_UDMA_MASK << dshift); + cs5536_program_dtc(adev, mwdma_timings[mode - XFER_MW_DMA_0]); } + + cs5536_write(pdev, ETC, etc); } static struct scsi_host_template cs5536_sht = { -- cgit v1.2.3 From 99c8ea3e57e7b5551ffd9cd401c4bf302b5664e3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Nov 2011 14:44:26 +0800 Subject: SATA/PATA: convert drivers/ata/* to use module_platform_driver() This patch converts the drivers in drivers/ata/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Cc: Viresh Kumar Cc: Jeff Garzik Cc: Arnaud Patard Cc: Alessandro Zummo Cc: Sylvain Munaut Cc: Anton Vorontsov Cc: Marek Vasut Cc: Florian Fainelli Cc: Mark Miesfeld Cc: Ashish Kalra Cc: Paul Mundt Signed-off-by: Axel Lin Signed-off-by: Jeff Garzik --- drivers/ata/pata_arasan_cf.c | 12 +----------- drivers/ata/pata_at91.c | 15 +-------------- drivers/ata/pata_imx.c | 12 +----------- drivers/ata/pata_ixp4xx_cf.c | 13 +------------ drivers/ata/pata_mpc52xx.c | 21 +-------------------- drivers/ata/pata_of_platform.c | 12 +----------- drivers/ata/pata_palmld.c | 13 +------------ drivers/ata/pata_platform.c | 12 +----------- drivers/ata/pata_pxa.c | 13 +------------ drivers/ata/pata_rb532_cf.c | 21 ++------------------- drivers/ata/sata_dwc_460ex.c | 13 +------------ drivers/ata/sata_fsl.c | 14 +------------- 12 files changed, 13 insertions(+), 158 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index e8574bba3ee..048589fad2c 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -963,17 +963,7 @@ static struct platform_driver arasan_cf_driver = { }, }; -static int __init arasan_cf_init(void) -{ - return platform_driver_register(&arasan_cf_driver); -} -module_init(arasan_cf_init); - -static void __exit arasan_cf_exit(void) -{ - platform_driver_unregister(&arasan_cf_driver); -} -module_exit(arasan_cf_exit); +module_platform_driver(arasan_cf_driver); MODULE_AUTHOR("Viresh Kumar "); MODULE_DESCRIPTION("Arasan ATA Compact Flash driver"); diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index a76f24a8e5d..d56dd45adf2 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -454,20 +454,7 @@ static struct platform_driver pata_at91_driver = { }, }; -static int __init pata_at91_init(void) -{ - return platform_driver_register(&pata_at91_driver); -} - -static void __exit pata_at91_exit(void) -{ - platform_driver_unregister(&pata_at91_driver); -} - - -module_init(pata_at91_init); -module_exit(pata_at91_exit); - +module_platform_driver(pata_at91_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Driver for CF in True IDE mode on AT91SAM9260 SoC"); diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c index ca9d9caedfa..c5af97f5107 100644 --- a/drivers/ata/pata_imx.c +++ b/drivers/ata/pata_imx.c @@ -235,17 +235,7 @@ static struct platform_driver pata_imx_driver = { }, }; -static int __init pata_imx_init(void) -{ - return platform_driver_register(&pata_imx_driver); -} - -static void __exit pata_imx_exit(void) -{ - platform_driver_unregister(&pata_imx_driver); -} -module_init(pata_imx_init); -module_exit(pata_imx_exit); +module_platform_driver(pata_imx_driver); MODULE_AUTHOR("Arnaud Patard "); MODULE_DESCRIPTION("low-level driver for iMX PATA"); diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index 15b64311fe0..badb1789a91 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -205,21 +205,10 @@ static struct platform_driver ixp4xx_pata_platform_driver = { .remove = __devexit_p(ixp4xx_pata_remove), }; -static int __init ixp4xx_pata_init(void) -{ - return platform_driver_register(&ixp4xx_pata_platform_driver); -} - -static void __exit ixp4xx_pata_exit(void) -{ - platform_driver_unregister(&ixp4xx_pata_platform_driver); -} +module_platform_driver(ixp4xx_pata_platform_driver); MODULE_AUTHOR("Alessandro Zummo "); MODULE_DESCRIPTION("low-level driver for ixp4xx Compact Flash PATA"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); MODULE_ALIAS("platform:" DRV_NAME); - -module_init(ixp4xx_pata_init); -module_exit(ixp4xx_pata_exit); diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 3e1746314f2..00748ae1a01 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -897,26 +897,7 @@ static struct platform_driver mpc52xx_ata_of_platform_driver = { }, }; - -/* ======================================================================== */ -/* Module */ -/* ======================================================================== */ - -static int __init -mpc52xx_ata_init(void) -{ - printk(KERN_INFO "ata: MPC52xx IDE/ATA libata driver\n"); - return platform_driver_register(&mpc52xx_ata_of_platform_driver); -} - -static void __exit -mpc52xx_ata_exit(void) -{ - platform_driver_unregister(&mpc52xx_ata_of_platform_driver); -} - -module_init(mpc52xx_ata_init); -module_exit(mpc52xx_ata_exit); +module_platform_driver(mpc52xx_ata_of_platform_driver); MODULE_AUTHOR("Sylvain Munaut "); MODULE_DESCRIPTION("Freescale MPC52xx IDE/ATA libata driver"); diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c index 2a472c5bb7d..9f11c35418c 100644 --- a/drivers/ata/pata_of_platform.c +++ b/drivers/ata/pata_of_platform.c @@ -101,17 +101,7 @@ static struct platform_driver pata_of_platform_driver = { .remove = __devexit_p(pata_of_platform_remove), }; -static int __init pata_of_platform_init(void) -{ - return platform_driver_register(&pata_of_platform_driver); -} -module_init(pata_of_platform_init); - -static void __exit pata_of_platform_exit(void) -{ - platform_driver_unregister(&pata_of_platform_driver); -} -module_exit(pata_of_platform_exit); +module_platform_driver(pata_of_platform_driver); MODULE_DESCRIPTION("OF-platform PATA driver"); MODULE_AUTHOR("Anton Vorontsov "); diff --git a/drivers/ata/pata_palmld.c b/drivers/ata/pata_palmld.c index b86d7e22595..5ff31b68135 100644 --- a/drivers/ata/pata_palmld.c +++ b/drivers/ata/pata_palmld.c @@ -132,20 +132,9 @@ static struct platform_driver palmld_pata_platform_driver = { .remove = __devexit_p(palmld_pata_remove), }; -static int __init palmld_pata_init(void) -{ - return platform_driver_register(&palmld_pata_platform_driver); -} - -static void __exit palmld_pata_exit(void) -{ - platform_driver_unregister(&palmld_pata_platform_driver); -} +module_platform_driver(palmld_pata_platform_driver); MODULE_AUTHOR("Marek Vasut "); MODULE_DESCRIPTION("PalmLD PATA driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); - -module_init(palmld_pata_init); -module_exit(palmld_pata_exit); diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 2067308f683..f1848aeda78 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -256,17 +256,7 @@ static struct platform_driver pata_platform_driver = { }, }; -static int __init pata_platform_init(void) -{ - return platform_driver_register(&pata_platform_driver); -} - -static void __exit pata_platform_exit(void) -{ - platform_driver_unregister(&pata_platform_driver); -} -module_init(pata_platform_init); -module_exit(pata_platform_exit); +module_platform_driver(pata_platform_driver); module_param(pio_mask, int, 0); diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index b4ede40f8ae..0bb0fb7b26b 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -390,18 +390,7 @@ static struct platform_driver pxa_ata_driver = { }, }; -static int __init pxa_ata_init(void) -{ - return platform_driver_register(&pxa_ata_driver); -} - -static void __exit pxa_ata_exit(void) -{ - platform_driver_unregister(&pxa_ata_driver); -} - -module_init(pxa_ata_init); -module_exit(pxa_ata_exit); +module_platform_driver(pxa_ata_driver); MODULE_AUTHOR("Marek Vasut "); MODULE_DESCRIPTION("DMA-capable driver for PATA on PXA CPU"); diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c index 1b9d10d9c5d..9417101bd5c 100644 --- a/drivers/ata/pata_rb532_cf.c +++ b/drivers/ata/pata_rb532_cf.c @@ -188,9 +188,6 @@ static __devexit int rb532_pata_driver_remove(struct platform_device *pdev) return 0; } -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:" DRV_NAME); - static struct platform_driver rb532_pata_platform_driver = { .probe = rb532_pata_driver_probe, .remove = __devexit_p(rb532_pata_driver_remove), @@ -200,27 +197,13 @@ static struct platform_driver rb532_pata_platform_driver = { }, }; -/* ------------------------------------------------------------------------ */ - #define DRV_INFO DRV_DESC " version " DRV_VERSION -static int __init rb532_pata_module_init(void) -{ - printk(KERN_INFO DRV_INFO "\n"); - - return platform_driver_register(&rb532_pata_platform_driver); -} - -static void __exit rb532_pata_module_exit(void) -{ - platform_driver_unregister(&rb532_pata_platform_driver); -} +module_platform_driver(rb532_pata_platform_driver); MODULE_AUTHOR("Gabor Juhos "); MODULE_AUTHOR("Florian Fainelli "); MODULE_DESCRIPTION(DRV_DESC); MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); - -module_init(rb532_pata_module_init); -module_exit(rb532_pata_module_exit); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 5c4237452f5..69f7cde49c6 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1777,18 +1777,7 @@ static struct platform_driver sata_dwc_driver = { .remove = sata_dwc_remove, }; -static int __init sata_dwc_init(void) -{ - return platform_driver_register(&sata_dwc_driver); -} - -static void __exit sata_dwc_exit(void) -{ - platform_driver_unregister(&sata_dwc_driver); -} - -module_init(sata_dwc_init); -module_exit(sata_dwc_exit); +module_platform_driver(sata_dwc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Miesfeld "); diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 78ae7b67b09..5a2c95ba050 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1452,21 +1452,9 @@ static struct platform_driver fsl_sata_driver = { #endif }; -static int __init sata_fsl_init(void) -{ - platform_driver_register(&fsl_sata_driver); - return 0; -} - -static void __exit sata_fsl_exit(void) -{ - platform_driver_unregister(&fsl_sata_driver); -} +module_platform_driver(fsl_sata_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ashish Kalra, Freescale Semiconductor"); MODULE_DESCRIPTION("Freescale 3.0Gbps SATA controller low level driver"); MODULE_VERSION("1.10"); - -module_init(sata_fsl_init); -module_exit(sata_fsl_exit); -- cgit v1.2.3 From d0643aa16688131b4dfbd03797ad73bab4ff29bf Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 22 Dec 2011 15:07:00 -0500 Subject: pata_of_platform: remove direct dependency on OF_IRQ CONFIG_OF_IRQ is not available on some platforms and using of_irq_* breaks the build. Since resources are already populated in the platform device, get the irq from there instead. Reported-by: David S. Miller Signed-off-by: Rob Herring Signed-off-by: Jeff Garzik --- drivers/ata/Kconfig | 2 +- drivers/ata/pata_of_platform.c | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index cf047c406d9..6bdedd7cca2 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -820,7 +820,7 @@ config PATA_PLATFORM config PATA_OF_PLATFORM tristate "OpenFirmware platform device PATA support" - depends on PATA_PLATFORM && OF && OF_IRQ + depends on PATA_PLATFORM && OF help This option enables support for generic directly connected ATA devices commonly found on embedded systems with OpenFirmware diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c index 9f11c35418c..1654dc27e7f 100644 --- a/drivers/ata/pata_of_platform.c +++ b/drivers/ata/pata_of_platform.c @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include static int __devinit pata_of_platform_probe(struct platform_device *ofdev) @@ -22,7 +21,7 @@ static int __devinit pata_of_platform_probe(struct platform_device *ofdev) struct device_node *dn = ofdev->dev.of_node; struct resource io_res; struct resource ctl_res; - struct resource irq_res; + struct resource *irq_res; unsigned int reg_shift = 0; int pio_mode = 0; int pio_mask; @@ -51,11 +50,9 @@ static int __devinit pata_of_platform_probe(struct platform_device *ofdev) } } - ret = of_irq_to_resource(dn, 0, &irq_res); - if (!ret) - irq_res.start = irq_res.end = 0; - else - irq_res.flags = 0; + irq_res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0); + if (irq_res) + irq_res->flags = 0; prop = of_get_property(dn, "reg-shift", NULL); if (prop) @@ -75,7 +72,7 @@ static int __devinit pata_of_platform_probe(struct platform_device *ofdev) pio_mask = 1 << pio_mode; pio_mask |= (1 << pio_mode) - 1; - return __pata_platform_probe(&ofdev->dev, &io_res, &ctl_res, &irq_res, + return __pata_platform_probe(&ofdev->dev, &io_res, &ctl_res, irq_res, reg_shift, pio_mask); } -- cgit v1.2.3 From 80a9c430007b489823bc1b5300f4a1ba39ca1ef9 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Thu, 22 Dec 2011 22:41:32 +0300 Subject: libata-core: kill duplicate statement in ata_do_set_mode() Commit b3a706014e56b1356e7b275fd25b833c63175bf0 (libata: Add a drivers/ide style DMA disable) neglected to remove the line in ata_do_set_mode() it has obviously made useless/duplicated. Do this now, and make a line added back then wrapped properly... Signed-off-by: Sergei Shtylyov Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 15a3d4deb27..d5935e4f352 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3249,10 +3249,10 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) ata_force_xfermask(dev); pio_mask = ata_pack_xfermask(dev->pio_mask, 0, 0); - dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask); if (libata_dma_mask & mode_mask) - dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask); + dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, + dev->udma_mask); else dma_mask = 0; -- cgit v1.2.3 From 17ab594fa5f6c766a3daf304d2e6b643f863c24c Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2011 11:10:10 -0800 Subject: ahci: platform support for suspend/resume Add platform hooks for custom suspend() and resume() functions. The generic suspend/resume code in drivers/ata/ahci_platform.c is adapted from the PCI version in drivers/ata/ahci.c. Signed-off-by: Brian Norris Signed-off-by: Jeff Garzik --- drivers/ata/ahci_platform.c | 68 +++++++++++++++++++++++++++++++++++++++++++ include/linux/ahci_platform.h | 2 ++ 2 files changed, 70 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 43b875810d1..48be4e18916 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -202,6 +202,71 @@ static int __devexit ahci_remove(struct platform_device *pdev) 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 const struct of_device_id ahci_of_match[] = { { .compatible = "calxeda,hb-ahci", }, {}, @@ -214,6 +279,9 @@ static struct platform_driver ahci_driver = { .name = "ahci", .owner = THIS_MODULE, .of_match_table = ahci_of_match, +#ifdef CONFIG_PM + .pm = &ahci_pm_ops, +#endif }, .id_table = ahci_devtype, }; diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index be3d9a77d6e..73a25005d88 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -23,6 +23,8 @@ struct ata_port_info; struct ahci_platform_data { int (*init)(struct device *dev, void __iomem *addr); void (*exit)(struct device *dev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); const struct ata_port_info *ata_port_info; unsigned int force_port_map; unsigned int mask_port_map; -- cgit v1.2.3 From 28fd00d42cca178638f51c08efa986a777c24a4b Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Thu, 22 Dec 2011 14:50:47 +0800 Subject: [SCSI] runtime resume parent for child's system-resume [Patch description from Alan Stern] If a child device was runtime-suspended when a system suspend began, then there will be nothing to prevent its parent from runtime-suspending as soon as it is woken up during the system resume. Then when the time comes to resume the child, the resume will fail because the parent is already back at low power. On the other hand, there are some devices which should remain at low power across an entire suspend-resume cycle. The details depend on the device and the platform. This suggests that the PM core is not the right place to solve the problem. One possible solution is for the subsystem or device driver to call pm_runtime_get_sync(dev->parent) at the start of the system-resume procedure and pm_runtime_put_sync(dev->parent) at the end. Acked-by: Alan Stern Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/scsi/scsi_pm.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index a633076e562..bf8bf79e6a1 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -72,8 +72,17 @@ static int scsi_bus_resume_common(struct device *dev) { int err = 0; - if (scsi_is_sdev_device(dev)) + if (scsi_is_sdev_device(dev)) { + /* + * Parent device may have runtime suspended as soon as + * it is woken up during the system resume. + * + * Resume it on behalf of child. + */ + pm_runtime_get_sync(dev->parent); err = scsi_dev_type_resume(dev); + pm_runtime_put_sync(dev->parent); + } if (err == 0) { pm_runtime_disable(dev); -- cgit v1.2.3 From e90b1e5a6e04c8892007ff8db20ef6d4fbdb5402 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Thu, 22 Dec 2011 14:50:48 +0800 Subject: ata: update ata port's runtime status during system resume The ata port is brought back to full power state during system resume. So its runtime PM status will have to be updated to reflect the actual post-system sleep status. This also fixes below warning during system suspend/resume. WARNING: at /work/linux/drivers/ata/libata-eh.c:4034 ata_scsi_port_error_handler+0x89/0x557() 4034 WARN_ON(!(ap->pflags & ATA_PFLAG_SUSPENDED)); Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d5935e4f352..ba1ed9b3acc 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5298,7 +5298,7 @@ static int ata_port_suspend(struct device *dev) return ata_port_suspend_common(dev); } -static int ata_port_resume(struct device *dev) +static int ata_port_resume_common(struct device *dev) { struct ata_port *ap = to_ata_port(dev); int rc; @@ -5308,6 +5308,20 @@ static int ata_port_resume(struct device *dev) return rc; } +static int ata_port_resume(struct device *dev) +{ + int rc; + + rc = ata_port_resume_common(dev); + if (!rc) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return rc; +} + static int ata_port_runtime_idle(struct device *dev) { return pm_runtime_suspend(dev); @@ -5318,7 +5332,7 @@ static const struct dev_pm_ops ata_port_pm_ops = { .resume = ata_port_resume, .runtime_suspend = ata_port_suspend_common, - .runtime_resume = ata_port_resume, + .runtime_resume = ata_port_resume_common, .runtime_idle = ata_port_runtime_idle, }; -- cgit v1.2.3 From 33574d68ae41ccbc6686cfabd965c685285c58a0 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Thu, 22 Dec 2011 14:50:49 +0800 Subject: ata: add ata port hibernate callbacks Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index ba1ed9b3acc..11c9aea4f4f 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5281,12 +5281,24 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, #define to_ata_port(d) container_of(d, struct ata_port, tdev) -static int ata_port_suspend_common(struct device *dev) +static int ata_port_suspend_common(struct device *dev, pm_message_t mesg) { struct ata_port *ap = to_ata_port(dev); + unsigned int ehi_flags = ATA_EHI_QUIET; int rc; - rc = ata_port_request_pm(ap, PMSG_SUSPEND, 0, ATA_EHI_QUIET, 1); + /* + * 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; + + rc = ata_port_request_pm(ap, mesg, 0, ehi_flags, 1); return rc; } @@ -5295,7 +5307,23 @@ static int ata_port_suspend(struct device *dev) if (pm_runtime_suspended(dev)) return 0; - return ata_port_suspend_common(dev); + return ata_port_suspend_common(dev, PMSG_SUSPEND); +} + +static int ata_port_do_freeze(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + pm_runtime_resume(dev); + + return ata_port_suspend_common(dev, PMSG_FREEZE); +} + +static int ata_port_poweroff(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + + return ata_port_suspend_common(dev, PMSG_HIBERNATE); } static int ata_port_resume_common(struct device *dev) @@ -5330,8 +5358,12 @@ static int ata_port_runtime_idle(struct device *dev) 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_common, + .runtime_suspend = ata_port_suspend, .runtime_resume = ata_port_resume_common, .runtime_idle = ata_port_runtime_idle, }; -- cgit v1.2.3 From 909fefc2511120ec71178f752c195c7b0b30269e Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 6 Jan 2012 20:45:45 +0300 Subject: pata_bf54x: fix BMIDE status register emulation The author of this driver clearly wasn't familiar with the BMIDE specification (also known as SFF-8038i) when he implemented the bmdma_status() method: first, the interrupt bit of the BMIDE status register corresponds to nothing else but INTRQ signal (ATAPI_DEV_INT here); second, the error bit is only set if the controller encounters issue doing the bus master transfers, not on the IDE DMA burst termination interrupts like here (moreover, setting the error bit doesn't cause an interrupt). We now need to disable all those unused interrupts... (The only thing I couldn't figure out is how to flush the FIFO to memory once the interrupt happens as required by the mentioned spec.) Signed-off-by: Sergei Shtylyov Signed-off-by: Sonic Zhang Signed-off-by: Jeff Garzik --- drivers/ata/pata_bf54x.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index bd987bb082e..d6a4677fdf7 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -418,14 +418,6 @@ static void bfin_set_dmamode(struct ata_port *ap, struct ata_device *adev) (tcyc_tdvs<<8 | tdvs)); ATAPI_SET_ULTRA_TIM_2(base, (tmli<<8 | tss)); ATAPI_SET_ULTRA_TIM_3(base, (trp<<8 | tzah)); - - /* Enable host ATAPI Untra DMA interrupts */ - ATAPI_SET_INT_MASK(base, - ATAPI_GET_INT_MASK(base) - | UDMAIN_DONE_MASK - | UDMAOUT_DONE_MASK - | UDMAIN_TERM_MASK - | UDMAOUT_TERM_MASK); } } } @@ -470,10 +462,6 @@ static void bfin_set_dmamode(struct ata_port *ap, struct ata_device *adev) ATAPI_SET_MULTI_TIM_0(base, (tm<<8 | td)); ATAPI_SET_MULTI_TIM_1(base, (tkr<<8 | tkw)); ATAPI_SET_MULTI_TIM_2(base, (teoc<<8 | th)); - - /* Enable host ATAPI Multi DMA interrupts */ - ATAPI_SET_INT_MASK(base, ATAPI_GET_INT_MASK(base) - | MULTI_DONE_MASK | MULTI_TERM_MASK); SSYNC(); } } @@ -1153,15 +1141,11 @@ static unsigned char bfin_bmdma_status(struct ata_port *ap) { unsigned char host_stat = 0; void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; - unsigned short int_status = ATAPI_GET_INT_STATUS(base); - if (ATAPI_GET_STATUS(base) & (MULTI_XFER_ON|ULTRA_XFER_ON)) + if (ATAPI_GET_STATUS(base) & (MULTI_XFER_ON | ULTRA_XFER_ON)) host_stat |= ATA_DMA_ACTIVE; - if (int_status & (MULTI_DONE_INT|UDMAIN_DONE_INT|UDMAOUT_DONE_INT| - ATAPI_DEV_INT)) + if (ATAPI_GET_INT_STATUS(base) & ATAPI_DEV_INT) host_stat |= ATA_DMA_INTR; - if (int_status & (MULTI_TERM_INT|UDMAIN_TERM_INT|UDMAOUT_TERM_INT)) - host_stat |= ATA_DMA_ERR|ATA_DMA_INTR; dev_dbg(ap->dev, "ATAPI: host_stat=0x%x\n", host_stat); -- cgit v1.2.3 From 318893e1429a9d50569a0379d1e20b0ecc45c555 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Fri, 6 Jan 2012 13:33:39 +0100 Subject: ahci: support the STA2X11 I/O Hub The AHCI controller found in the STA2X11 chip uses BAR number 0 instead of 5. Also, the chip's fixup code sets a special DMA mask for all of its PCI functions, and the mask must be preserved here. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Cc: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index cf26222a93c..d07bf0366d9 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -52,7 +52,8 @@ #define DRV_VERSION "3.0" enum { - AHCI_PCI_BAR = 5, + AHCI_PCI_BAR_STA2X11 = 0, + AHCI_PCI_BAR_STANDARD = 5, }; enum board_ids { @@ -375,6 +376,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 968 */ { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */ + /* ST Microelectronics */ + { PCI_VDEVICE(STMICRO, 0xCC06), board_ahci }, /* ST ConneXt */ + /* Marvell */ { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */ { PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv }, /* 6121 */ @@ -622,6 +626,13 @@ static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) { int rc; + /* + * If the device fixup already set the dma_mask to some non-standard + * value, don't extend it here. This happens on STA2X11, for example. + */ + if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32)) + return 0; + if (using_dac && !pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); @@ -1026,6 +1037,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) struct ahci_host_priv *hpriv; struct ata_host *host; int n_ports, i, rc; + int ahci_pci_bar = AHCI_PCI_BAR_STANDARD; VPRINTK("ENTER\n"); @@ -1057,6 +1069,10 @@ 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 */ + if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06) + ahci_pci_bar = AHCI_PCI_BAR_STA2X11; + /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) @@ -1065,7 +1081,7 @@ 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); + rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) @@ -1108,7 +1124,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) pci_intx(pdev, 1); - hpriv->mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; + hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; /* save initial config */ ahci_pci_save_initial_config(pdev, hpriv); @@ -1172,8 +1188,8 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - ata_port_pbar_desc(ap, AHCI_PCI_BAR, -1, "abar"); - ata_port_pbar_desc(ap, AHCI_PCI_BAR, + ata_port_pbar_desc(ap, ahci_pci_bar, -1, "abar"); + ata_port_pbar_desc(ap, ahci_pci_bar, 0x100 + ap->port_no * 0x80, "port"); /* set enclosure management message type */ -- cgit v1.2.3