[PATCH] ahci: implement Power Management support
Implement power management support. Original implementation is from Zhao, Forrest <forrest.zhao@intel.com> Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
d91542c11f
commit
c1332875cb
|
@ -215,6 +215,10 @@ static void ahci_freeze(struct ata_port *ap);
|
||||||
static void ahci_thaw(struct ata_port *ap);
|
static void ahci_thaw(struct ata_port *ap);
|
||||||
static void ahci_error_handler(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 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 void ahci_remove_one (struct pci_dev *pdev);
|
||||||
|
|
||||||
static struct scsi_host_template ahci_sht = {
|
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_configure = ata_scsi_slave_config,
|
||||||
.slave_destroy = ata_scsi_slave_destroy,
|
.slave_destroy = ata_scsi_slave_destroy,
|
||||||
.bios_param = ata_std_bios_param,
|
.bios_param = ata_std_bios_param,
|
||||||
|
.suspend = ata_scsi_device_suspend,
|
||||||
|
.resume = ata_scsi_device_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct ata_port_operations ahci_ops = {
|
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,
|
.error_handler = ahci_error_handler,
|
||||||
.post_internal_cmd = ahci_post_internal_cmd,
|
.post_internal_cmd = ahci_post_internal_cmd,
|
||||||
|
|
||||||
|
.port_suspend = ahci_port_suspend,
|
||||||
|
.port_resume = ahci_port_resume,
|
||||||
|
|
||||||
.port_start = ahci_port_start,
|
.port_start = ahci_port_start,
|
||||||
.port_stop = ahci_port_stop,
|
.port_stop = ahci_port_stop,
|
||||||
};
|
};
|
||||||
|
@ -361,6 +370,8 @@ static struct pci_driver ahci_pci_driver = {
|
||||||
.name = DRV_NAME,
|
.name = DRV_NAME,
|
||||||
.id_table = ahci_pci_tbl,
|
.id_table = ahci_pci_tbl,
|
||||||
.probe = ahci_init_one,
|
.probe = ahci_init_one,
|
||||||
|
.suspend = ahci_pci_device_suspend,
|
||||||
|
.resume = ahci_pci_device_resume,
|
||||||
.remove = ahci_remove_one,
|
.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)
|
static int ahci_port_start(struct ata_port *ap)
|
||||||
{
|
{
|
||||||
struct device *dev = ap->host_set->dev;
|
struct device *dev = ap->host_set->dev;
|
||||||
|
|
Loading…
Reference in a new issue