[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:
Tejun Heo 2006-07-26 15:59:26 +09:00 committed by Jeff Garzik
parent d91542c11f
commit c1332875cb
1 changed files with 84 additions and 0 deletions

View File

@ -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;