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

Commit c1332875 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik
Browse files

[PATCH] ahci: implement Power Management support



Implement power management support.

Original implementation is from Zhao, Forrest <forrest.zhao@intel.com>

Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarZhao, Forrest <forrest.zhao@intel.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent d91542c1
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
@@ -215,6 +215,10 @@ static void ahci_freeze(struct ata_port *ap);
static void ahci_thaw(struct ata_port *ap);
static void ahci_error_handler(struct ata_port *ap);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
static int ahci_port_resume(struct ata_port *ap);
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
static int ahci_pci_device_resume(struct pci_dev *pdev);
static void ahci_remove_one (struct pci_dev *pdev);

static struct scsi_host_template ahci_sht = {
@@ -234,6 +238,8 @@ static struct scsi_host_template ahci_sht = {
	.slave_configure	= ata_scsi_slave_config,
	.slave_destroy		= ata_scsi_slave_destroy,
	.bios_param		= ata_std_bios_param,
	.suspend		= ata_scsi_device_suspend,
	.resume			= ata_scsi_device_resume,
};

static const struct ata_port_operations ahci_ops = {
@@ -260,6 +266,9 @@ static const struct ata_port_operations ahci_ops = {
	.error_handler		= ahci_error_handler,
	.post_internal_cmd	= ahci_post_internal_cmd,

	.port_suspend		= ahci_port_suspend,
	.port_resume		= ahci_port_resume,

	.port_start		= ahci_port_start,
	.port_stop		= ahci_port_stop,
};
@@ -361,6 +370,8 @@ static struct pci_driver ahci_pci_driver = {
	.name			= DRV_NAME,
	.id_table		= ahci_pci_tbl,
	.probe			= ahci_init_one,
	.suspend		= ahci_pci_device_suspend,
	.resume			= ahci_pci_device_resume,
	.remove			= ahci_remove_one,
};

@@ -1199,6 +1210,79 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
	}
}

static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
{
	struct ahci_host_priv *hpriv = ap->host_set->private_data;
	struct ahci_port_priv *pp = ap->private_data;
	void __iomem *mmio = ap->host_set->mmio_base;
	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
	const char *emsg = NULL;
	int rc;

	rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
	if (rc) {
		ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc);
		ahci_init_port(port_mmio, hpriv->cap,
			       pp->cmd_slot_dma, pp->rx_fis_dma);
	}

	return rc;
}

static int ahci_port_resume(struct ata_port *ap)
{
	struct ahci_port_priv *pp = ap->private_data;
	struct ahci_host_priv *hpriv = ap->host_set->private_data;
	void __iomem *mmio = ap->host_set->mmio_base;
	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);

	ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);

	return 0;
}

static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
	struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
	void __iomem *mmio = host_set->mmio_base;
	u32 ctl;

	if (mesg.event == PM_EVENT_SUSPEND) {
		/* 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 */
	}

	return ata_pci_device_suspend(pdev, mesg);
}

static int ahci_pci_device_resume(struct pci_dev *pdev)
{
	struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
	struct ahci_host_priv *hpriv = host_set->private_data;
	void __iomem *mmio = host_set->mmio_base;
	int rc;

	ata_pci_device_do_resume(pdev);

	if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
		rc = ahci_reset_controller(mmio, pdev);
		if (rc)
			return rc;

		ahci_init_controller(mmio, pdev, host_set->n_ports, hpriv->cap);
	}

	ata_host_set_resume(host_set);

	return 0;
}

static int ahci_port_start(struct ata_port *ap)
{
	struct device *dev = ap->host_set->dev;