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 ++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 70 deletions(-) (limited to 'drivers/ata/libata-core.c') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c04ad68cb602..04c208e3cec6 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 -- 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/ata/libata-core.c') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 04c208e3cec6..15a3d4deb272 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 7ae1e771d950..508a60bfe5c1 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 3ceb3d94be26..9a7f0ea565df 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 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/ata/libata-core.c') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 15a3d4deb272..d5935e4f3529 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 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/ata/libata-core.c') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d5935e4f3529..ba1ed9b3acc3 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/ata/libata-core.c') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index ba1ed9b3acc3..11c9aea4f4f7 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 3a25179e068b2fae8df7a7ab63a2a5ed71a2e927 Mon Sep 17 00:00:00 2001 From: Vladimir LAVALLADE Date: Sun, 8 Jan 2012 13:50:13 +0100 Subject: [libata] add DVRTD08A and DVR-215 to NOSETXFER device quirk list Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/ata/libata-core.c') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 11c9aea4f4f7..c06e0ec11556 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4125,6 +4125,8 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { * device and controller are SATA. */ { "PIONEER DVD-RW DVRTD08", NULL, ATA_HORKAGE_NOSETXFER }, + { "PIONEER DVD-RW DVRTD08A", NULL, ATA_HORKAGE_NOSETXFER }, + { "PIONEER DVD-RW DVR-215", NULL, ATA_HORKAGE_NOSETXFER }, { "PIONEER DVD-RW DVR-212D", NULL, ATA_HORKAGE_NOSETXFER }, { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER }, -- cgit v1.2.3