summaryrefslogtreecommitdiff
path: root/drivers/ata
diff options
context:
space:
mode:
authorRichard Zhu <r65037@freescale.com>2012-07-10 10:31:06 +0800
committerTerry Lv <r65388@freescale.com>2012-07-25 13:10:41 +0800
commitea4f073f9f8d9d655bfb30d7f14606337c49a106 (patch)
tree106da9052fc7bce354adb369dcff0a3656569135 /drivers/ata
parent464758b2d3a1af8fad402acdbb5f3a157b4accab (diff)
ENGR00215810-1 AHCI: sata drops to 1.5Gbps after suspend/resume several times
Add the AHCI platform suspend/resume function callback to fix this issue. Signed-off-by: Richard Zhu <Hong-Xing.Zhu@freescale.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/ahci_platform.c66
1 files changed, 66 insertions, 0 deletions
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 6fef1fa75c54..936babd0fb9e 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -171,12 +171,78 @@ static int __devexit ahci_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int ahci_device_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct device *dev = &pdev->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, msg);
+ if (rc)
+ return rc;
+
+ if (pdata && pdata->suspend)
+ return pdata->suspend(dev);
+ return 0;
+}
+
+static int ahci_device_resume(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);
+ 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;
+}
+#endif /* CONFIG_PM */
+
static struct platform_driver ahci_driver = {
.remove = __devexit_p(ahci_remove),
.driver = {
.name = "ahci",
.owner = THIS_MODULE,
},
+#ifdef CONFIG_PM
+ .suspend = ahci_device_suspend,
+ .resume = ahci_device_resume,
+#endif
};
static int __init ahci_init(void)