diff options
author | Brian Norris <computersforpeace@gmail.com> | 2011-11-18 20:10:10 +0100 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2012-01-09 01:14:59 +0100 |
commit | 17ab594fa5f6c766a3daf304d2e6b643f863c24c (patch) | |
tree | fe562c7fcd1100a1bc3b90f912813b77e5b37972 /drivers/ata | |
parent | libata-core: kill duplicate statement in ata_do_set_mode() (diff) | |
download | linux-17ab594fa5f6c766a3daf304d2e6b643f863c24c.tar.xz linux-17ab594fa5f6c766a3daf304d2e6b643f863c24c.zip |
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 <computersforpeace@gmail.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/ahci_platform.c | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 43b875810d1b..48be4e189163 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, }; |