Skip to contentSkip to conversation

ahci: implement the AHCI suspend/resume

PATCH New zhao, forrest 1 participant 1 message Jul 20, 2006

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 5/5 ahci: implement the AHCI suspend/resume

This patch implements the AHCI suspend/resume.

---

 drivers/scsi/ahci.c |  127 ++++++++++++++++++++++++++++++++++++++++++++
+++++++
 1 files changed, 127 insertions(+), 0 deletions(-)

629bc20e8ac2f8b86e462a9be8e3c12b1978faae

drivers/scsi/ahci.c +1 -0
index 5e04ef4..7c1b43d 100644
@@ -221,9 +221,12 @@ static void ahci_freeze(struct ata_port 
 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_pci_device_suspend(struct pci_dev *pdev, pm_message_t

state);
+static int ahci_pci_device_resume(struct pci_dev *pdev);
static int ahci_port_standby(void __iomem *port_mmio, u32 cap); static int ahci_port_spinup(void __iomem *port_mmio, u32 cap); static int ahci_port_suspend(struct ata_port *ap, pm_message_t state);
+static int ahci_port_resume(struct ata_port *ap);
static void ahci_remove_one (struct pci_dev *pdev);

static struct scsi_host_template ahci_sht = {
@@ -243,6 +246,8 @@ static struct scsi_host_template ahci_sh

        .slave_configure        = ata_scsi_slave_config,
        .slave_destroy          = ata_scsi_slave_destroy,
        .bios_param             = ata_std_bios_param,
+       .resume                 = ata_scsi_device_resume,
+       .suspend                = ata_scsi_device_suspend,

};

static const struct ata_port_operations ahci_ops = {
@@ -371,6 +376,8 @@ static struct pci_driver ahci_pci_driver

        .id_table               = ahci_pci_tbl,
        .probe                  = ahci_init_one,
        .remove                 = ahci_remove_one,
+       .suspend                = ahci_pci_device_suspend,
+       .resume                 = ahci_pci_device_resume,

};

@@ -507,6 +514,57 @@ static int ahci_port_suspend(struct ata_

        return rc;

}

+static int ahci_port_resume(struct ata_port *ap)
+{

+       void __iomem *mmio = ap->host_set->mmio_base;
+       void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+       struct ahci_host_priv *hpriv = ap->host_set->private_data;
+       struct ahci_port_priv *pp = ap->private_data;
+       int rc;
+       u32 tmp;

+
+ /*
+ * Enable FIS reception
+ */
+ ahci_start_fis_rx(port_mmio, pp, hpriv);
+
+ rc = ahci_port_spinup(port_mmio, hpriv->cap);
+ if (rc)
+ ata_port_printk(ap, KERN_WARNING, "Could not spinup
device"
+ " (%d)\n", rc);
+
+ /*
+ * Clear error status
+ */

+       tmp = readl(port_mmio + PORT_SCR_ERR);
+       writel(tmp, port_mmio + PORT_SCR_ERR);
+       /*

+ * Clear interrupt status
+ */
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_IRQ_EN)) {
+ u32 irq_stat;
+

+               /* ack any pending irq events for this port */
+               irq_stat = readl(port_mmio + PORT_IRQ_STAT);
+               if (irq_stat)

+ writel(irq_stat, port_mmio + PORT_IRQ_STAT);
+
+ /* set irq mask (enables interrupts) */
+ writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
+ }
+
+ /*
+ * Enable DMA
+ */
+ rc = ahci_start_engine(port_mmio);
+ if (rc)
+ ata_port_printk(ap, KERN_WARNING, "Can't start DMA
engine"
+ " (%d)\n", rc);
+
+ return rc;
+}
+
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)

 {
        unsigned int sc_reg;
@@ -1024,6 +1082,75 @@ static unsigned int ahci_fill_sg(struct 
        return n_sg;

}

+int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t state) +{

+       struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+       void __iomem *mmio = host_set->mmio_base;
+       u32 tmp;
+       int i;

+
+ /* First suspend all ports */
+ for (i = 0; i < host_set->n_ports; i++) {
+ struct ata_port *ap;
+
+ ap = host_set->ports[i];
+ ahci_port_suspend(ap, state);
+ }
+
+ /*

+        * AHCI spec rev1.1 section 8.3.3:
+        * Software must disable interrupts prior to
+        * requesting a transition of the HBA to
+        * D3 state.
+        */
+       tmp = readl(mmio + HOST_CTL);
+       tmp &= ~HOST_IRQ_EN;
+       writel(tmp, mmio + HOST_CTL);
+       tmp = readl(mmio + HOST_CTL); /* flush */

+
+ ata_pci_device_do_suspend(pdev, state);
+
+ return 0;
+}
+
+int ahci_pci_device_resume(struct pci_dev *pdev)
+{

+       struct device *dev = pci_dev_to_dev(pdev);
+       struct ata_host_set *host_set = dev_get_drvdata(dev);
+       void __iomem *mmio = host_set->mmio_base;
+       struct ata_port *ap;
+       u32 i, tmp, irq_stat;
+
+       tmp = readl(mmio + HOST_CTL);
+       if (!(tmp & HOST_AHCI_EN)) {
+               tmp |= HOST_AHCI_EN;
+               writel(tmp, mmio + HOST_CTL);
+               tmp = readl(mmio + HOST_CTL);

+ }
+
+ ata_pci_device_do_resume(pdev);
+
+ /* Resume all ports */
+ for (i = 0; i < host_set->n_ports; i++) {
+ ap = host_set->ports[i];
+ ahci_port_resume(ap);
+ }
+
+ /*
+ * Clear interrupt status and enable interrupt
+ */
+ if (!(tmp & HOST_IRQ_EN)) {
+ irq_stat = readl(mmio + HOST_IRQ_STAT);
+ if (irq_stat)
+ writel(irq_stat, mmio + HOST_IRQ_STAT);

+               tmp |= HOST_IRQ_EN;
+               writel(tmp, mmio + HOST_CTL);
+               tmp = readl(mmio + HOST_CTL);

+ }
+
+ return 0;
+}
+
static void ahci_qc_prep(struct ata_queued_cmd *qc)

 {
        struct ata_port *ap = qc->ap;
Signature · 1 line
1.2.6