forked from Minki/linux
[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_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;
|
||||
|
Loading…
Reference in New Issue
Block a user