Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17ab594f authored by Brian Norris's avatar Brian Norris Committed by Jeff Garzik
Browse files

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: default avatarBrian Norris <computersforpeace@gmail.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 80a9c430
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
@@ -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,
};
+2 −0
Original line number Diff line number Diff line
@@ -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;