* [PATCH 6/6] Implement the AHCI suspend/resume
@ 2006-06-02 7:46 zhao, forrest
2006-06-02 8:21 ` Hannes Reinecke
2006-06-03 13:35 ` Tejun Heo
0 siblings, 2 replies; 12+ messages in thread
From: zhao, forrest @ 2006-06-02 7:46 UTC (permalink / raw)
To: jeff, hare, axboe, htejun, jeremy, lkml; +Cc: linux-ide
This patch implements the AHCI suspend/resume.
It puts the port suspend/resume operations in
ahci_pci_device_suspend/resume(), which is in conformance with
Jeff's idea of host<->bus<->device suspend/resume sequence.
Signed-off-by: Forrest Zhao <forrest.zhao@intel.com>
---
drivers/scsi/ahci.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 156 insertions(+), 1 deletions(-)
a68fb19b6f0d03d4052d02f1288ccb3e5902c50e
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 8928170..b2c94ed 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -188,6 +188,7 @@ struct ahci_host_priv {
unsigned long flags;
u32 cap; /* cache of HOST_CAP register */
u32 port_map; /* cache of HOST_PORTS_IMPL reg */
+ u32 dev_map; /* connected devices */
};
struct ahci_port_priv {
@@ -217,6 +218,8 @@ static void ahci_port_stop(struct ata_po
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_port_disable(struct ata_port *ap);
static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
static void ahci_qc_prep(struct ata_queued_cmd *qc);
static u8 ahci_check_status(struct ata_port *ap);
@@ -224,6 +227,8 @@ 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 void ahci_remove_one (struct pci_dev *pdev);
static struct scsi_host_template ahci_sht = {
@@ -242,10 +247,12 @@ static struct scsi_host_template ahci_sh
.dma_boundary = AHCI_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static const struct ata_port_operations ahci_ops = {
- .port_disable = ata_port_disable,
+ .port_disable = ahci_port_disable,
.check_status = ahci_check_status,
.check_altstatus = ahci_check_status,
@@ -346,6 +353,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,
};
@@ -482,6 +491,81 @@ 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);
+
+ if ((hpriv->dev_map >> (ap->port_no + 1)) == 0) {
+ /*
+ * Enable interrupts if this was the last port
+ */
+ ata_port_printk(ap, KERN_INFO, "Enable interrupts\n");
+
+ 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);
+ (void) readl(mmio + HOST_CTL);
+ }
+ }
+
+ /*
+ * 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 void ahci_port_disable(struct ata_port *ap)
+{
+ struct ahci_host_priv *hpriv = ap->host_set->private_data;
+
+ ata_port_disable(ap);
+
+ hpriv->dev_map &= ~(1 << ap->port_no);
+}
+
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
{
unsigned int sc_reg;
@@ -945,6 +1029,7 @@ static int ahci_hardreset(struct ata_por
static void ahci_postreset(struct ata_port *ap, unsigned int *class)
{
void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ struct ahci_host_priv *hpriv = ap->host_set->private_data;
u32 new_tmp, tmp;
ata_std_postreset(ap, class);
@@ -959,6 +1044,75 @@ static void ahci_postreset(struct ata_po
writel(new_tmp, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD); /* flush */
}
+
+ if (*class != ATA_DEV_NONE)
+ hpriv->dev_map |= (1 << ap->port_no);
+}
+
+int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ 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;
+ int rc, i;
+ u32 tmp;
+ struct ata_port *ap;
+
+ /* First suspend all ports */
+ for (i = 0; i < host_set->n_ports; i++) {
+ ap = host_set->ports[i];
+
+ rc = ahci_port_suspend(ap, state);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * 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 */
+
+ return ata_pci_device_suspend(pdev, state);
+}
+
+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;
+ u32 tmp;
+ struct ata_port *ap;
+ int rc = 0, i;
+
+ /*
+ * Enabling AHCI mode
+ */
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_AHCI_EN)) {
+ tmp |= HOST_AHCI_EN;
+ writel(tmp, mmio + HOST_CTL);
+ tmp = readl(mmio + HOST_CTL);
+ }
+
+ rc = ata_pci_device_resume(pdev);
+ if (rc)
+ return rc;
+
+ /* Resume all ports */
+ for (i = 0; i < host_set->n_ports; i++) {
+ ap = host_set->ports[i];
+ rc = ahci_port_resume(ap);
+ if (rc)
+ break;
+ }
+
+ return rc;
}
static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes)
@@ -1353,6 +1507,7 @@ static int ahci_host_init(struct ata_pro
hpriv->cap = readl(mmio + HOST_CAP);
hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
+ hpriv->dev_map = 0;
probe_ent->n_ports = (hpriv->cap & 0x1f) + 1;
VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n",
--
1.2.6
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 6/6] Implement the AHCI suspend/resume
2006-06-02 7:46 [PATCH 6/6] Implement the AHCI suspend/resume zhao, forrest
@ 2006-06-02 8:21 ` Hannes Reinecke
2006-06-02 8:34 ` Tejun Heo
2006-06-03 13:35 ` Tejun Heo
1 sibling, 1 reply; 12+ messages in thread
From: Hannes Reinecke @ 2006-06-02 8:21 UTC (permalink / raw)
To: zhao, forrest; +Cc: jeff, axboe, htejun, jeremy, lkml, linux-ide
zhao, forrest wrote:
> This patch implements the AHCI suspend/resume.
> It puts the port suspend/resume operations in
> ahci_pci_device_suspend/resume(), which is in conformance with
> Jeff's idea of host<->bus<->device suspend/resume sequence.
>
>
> Signed-off-by: Forrest Zhao <forrest.zhao@intel.com>
>
There is one quirky bit in here: we have to enable interrupts for the
_adapter_ after all _devices_ have been re-initialized.
This breaks the host<->bus<->device initialization sequence.
Is there any chance libata can add some hooks for that sort of thing?
The current approach (using a device map and run the host initialisation
after the last device is done) is a bit hackish.
I'd rather like to see the upper layers being able to deal with such
devices. But until then:
Signed-off-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke hare@suse.de
SuSE Linux Products GmbH S390 & zSeries
Maxfeldstraße 5 +49 911 74053 688
90409 Nürnberg http://www.suse.de
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 6/6] Implement the AHCI suspend/resume
2006-06-02 8:21 ` Hannes Reinecke
@ 2006-06-02 8:34 ` Tejun Heo
0 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2006-06-02 8:34 UTC (permalink / raw)
To: Hannes Reinecke; +Cc: zhao, forrest, jeff, axboe, jeremy, lkml, linux-ide
Hannes Reinecke wrote:
> zhao, forrest wrote:
>> This patch implements the AHCI suspend/resume.
>> It puts the port suspend/resume operations in
>> ahci_pci_device_suspend/resume(), which is in conformance with
>> Jeff's idea of host<->bus<->device suspend/resume sequence.
>>
>>
>> Signed-off-by: Forrest Zhao <forrest.zhao@intel.com>
>>
> There is one quirky bit in here: we have to enable interrupts for the
> _adapter_ after all _devices_ have been re-initialized.
> This breaks the host<->bus<->device initialization sequence.
> Is there any chance libata can add some hooks for that sort of thing?
> The current approach (using a device map and run the host initialisation
> after the last device is done) is a bit hackish.
> I'd rather like to see the upper layers being able to deal with such
> devices. But until then:
>
Working on it. After quite some number of saying 'oh.. it's gonna be
ready in a week' and then taking more than a month, I've pretty much
given up specifying due date. But, I'm on it. :)
--
tejun
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 6/6] Implement the AHCI suspend/resume
2006-06-02 7:46 [PATCH 6/6] Implement the AHCI suspend/resume zhao, forrest
2006-06-02 8:21 ` Hannes Reinecke
@ 2006-06-03 13:35 ` Tejun Heo
2006-06-05 5:05 ` zhao, forrest
1 sibling, 1 reply; 12+ messages in thread
From: Tejun Heo @ 2006-06-03 13:35 UTC (permalink / raw)
To: zhao, forrest; +Cc: jeff, hare, axboe, jeremy, lkml, linux-ide
On Fri, Jun 02, 2006 at 03:46:19PM +0800, zhao, forrest wrote:
> --- a/drivers/scsi/ahci.c
> +++ b/drivers/scsi/ahci.c
> @@ -188,6 +188,7 @@ struct ahci_host_priv {
> unsigned long flags;
> u32 cap; /* cache of HOST_CAP register */
> u32 port_map; /* cache of HOST_PORTS_IMPL reg */
> + u32 dev_map; /* connected devices */
I'm having difficult time understanding the usage of dev_map. Why is
it needed?
> +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);
> +
> + if ((hpriv->dev_map >> (ap->port_no + 1)) == 0) {
> + /*
> + * Enable interrupts if this was the last port
> + */
> + ata_port_printk(ap, KERN_INFO, "Enable interrupts\n");
> +
> + 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);
> + (void) readl(mmio + HOST_CTL);
> + }
Can't above enable-IRQ block be moved into ahci_pci_device_resume()
after resuming all ports?
--
tejun
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 6/6] Implement the AHCI suspend/resume
2006-06-03 13:35 ` Tejun Heo
@ 2006-06-05 5:05 ` zhao, forrest
0 siblings, 0 replies; 12+ messages in thread
From: zhao, forrest @ 2006-06-05 5:05 UTC (permalink / raw)
To: Tejun Heo; +Cc: jeff, hare, axboe, jeremy, lkml, linux-ide
On Sat, 2006-06-03 at 22:35 +0900, Tejun Heo wrote:
> > + */
> > + 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);
> > +
> > + if ((hpriv->dev_map >> (ap->port_no + 1)) == 0) {
> > + /*
> > + * Enable interrupts if this was the last port
> > + */
> > + ata_port_printk(ap, KERN_INFO, "Enable interrupts\n");
> > +
> > + 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);
> > + (void) readl(mmio + HOST_CTL);
> > + }
>
> Can't above enable-IRQ block be moved into ahci_pci_device_resume()
> after resuming all ports?
Tejun,
I think you're right, the "dev_map" is an ugly hack. I'll move above
enable-IRQ block to ahci_pci_device_resume(), thus removing "dev_map" in
the next round of updated patches.
Thanks,
Forrest
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 6/6] Implement the AHCI suspend/resume
@ 2006-06-06 10:17 zhao, forrest
0 siblings, 0 replies; 12+ messages in thread
From: zhao, forrest @ 2006-06-06 10:17 UTC (permalink / raw)
To: jeff, hare, axboe, htejun, jeremy, lkml; +Cc: linux-ide
This patch implements the AHCI suspend/resume.
It puts the port suspend/resume operations in
ahci_pci_device_suspend/resume(), which is in conformance with
Jeff's idea of host<->bus<->device suspend/resume sequence.
Signed-off-by: Forrest Zhao <forrest.zhaot@intel.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Jens Axboe <axboe@suse.de>
---
drivers/scsi/ahci.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 137 insertions(+), 0 deletions(-)
471c6e864d2b016917d54bddf4d023fa2df33423
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 4175ff6..ac41678 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -216,6 +216,7 @@ static void ahci_port_stop(struct ata_po
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_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
static void ahci_qc_prep(struct ata_queued_cmd *qc);
static u8 ahci_check_status(struct ata_port *ap);
@@ -223,6 +224,8 @@ 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 void ahci_remove_one (struct pci_dev *pdev);
static struct scsi_host_template ahci_sht = {
@@ -241,6 +244,8 @@ static struct scsi_host_template ahci_sh
.dma_boundary = AHCI_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
};
static const struct ata_port_operations ahci_ops = {
@@ -345,6 +350,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,
};
@@ -481,6 +488,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;
@@ -935,6 +993,85 @@ static void ahci_postreset(struct ata_po
}
}
+int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ 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;
+ int rc, i;
+ u32 tmp;
+ struct ata_port *ap;
+
+ /* First suspend all ports */
+ for (i = 0; i < host_set->n_ports; i++) {
+ ap = host_set->ports[i];
+
+ rc = ahci_port_suspend(ap, state);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * 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 */
+
+ return ata_pci_device_suspend(pdev, state);
+}
+
+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;
+ u32 tmp, irq_stat;
+ struct ata_port *ap;
+ int rc = 0, i;
+
+ /*
+ * Enabling AHCI mode
+ */
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_AHCI_EN)) {
+ tmp |= HOST_AHCI_EN;
+ writel(tmp, mmio + HOST_CTL);
+ tmp = readl(mmio + HOST_CTL);
+ }
+
+ rc = ata_pci_device_resume(pdev);
+ if (rc)
+ return rc;
+
+ /* Resume all ports */
+ for (i = 0; i < host_set->n_ports; i++) {
+ ap = host_set->ports[i];
+ rc = ahci_port_resume(ap);
+ if (rc)
+ break;
+ }
+
+ /*
+ * 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 int ahci_probe_reset(struct ata_port *ap, unsigned int *classes)
{
if ((ap->flags & AHCI_FLAG_RESET_NEEDS_CLO) &&
--
1.2.6
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 6/6] Implement the AHCI suspend/resume
@ 2006-06-29 8:21 zhao, forrest
0 siblings, 0 replies; 12+ messages in thread
From: zhao, forrest @ 2006-06-29 8:21 UTC (permalink / raw)
To: jgarzik, htejun, hare, axboe; +Cc: linux-ide
This patch implements the AHCI suspend/resume.
It integrates the suspend/resume operations with Power Management
framework.
Signed-off-by: Forrest Zhao <forrest.zhaot@intel.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Jens Axboe <axboe@suse.de>
---
drivers/scsi/ahci.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 127 insertions(+), 0 deletions(-)
ff254029d59b7cd674e3ad1f4709791b80838218
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 7bf9b6d..f7d9596 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -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 = {
@@ -271,6 +276,8 @@ static const struct ata_port_operations
.port_start = ahci_port_start,
.port_stop = ahci_port_stop,
+
+ .port_suspend = ahci_port_suspend,
};
static const struct ata_port_info ahci_port_info[] = {
@@ -371,6 +378,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 +516,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;
@@ -1022,6 +1082,73 @@ 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 rc = 0;
+
+ /* First suspend all ports */
+ rc = ata_host_set_suspend(host_set, state);
+ if (rc) return rc;
+
+ /*
+ * 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);
+ }
+
+ ata_host_set_resume(host_set);
+
+ return 0;
+}
+
static void ahci_qc_prep(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
--
1.2.6
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 6/6] Implement the AHCI suspend/resume
@ 2006-07-10 3:35 zhao, forrest
2006-07-10 7:34 ` zhao, forrest
0 siblings, 1 reply; 12+ messages in thread
From: zhao, forrest @ 2006-07-10 3:35 UTC (permalink / raw)
To: jgarzik, htejun, hare, axboe; +Cc: linux-ide
This patch implements the AHCI suspend/resume.
It integrates the suspend/resume operations with Power Management
framework.
Signed-off-by: Forrest Zhao <forrest.zhaot@intel.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Jens Axboe <axboe@suse.de>
---
drivers/scsi/ahci.c | 129 ++++++++++++++++++++++++++++++++++++++++++++
+++++++
1 files changed, 129 insertions(+), 0 deletions(-)
6fd581dceefd2f40212c468a3e0c7c49c60c0500
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index dc76798..28e1419 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -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;
@@ -1022,6 +1080,77 @@ 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);
+ }
+
+ ata_host_set_resume(host_set);
+
+ return 0;
+}
+
static void ahci_qc_prep(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
--
1.2.6
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 6/6] Implement the AHCI suspend/resume
2006-07-10 3:35 zhao, forrest
@ 2006-07-10 7:34 ` zhao, forrest
0 siblings, 0 replies; 12+ messages in thread
From: zhao, forrest @ 2006-07-10 7:34 UTC (permalink / raw)
To: jgarzik; +Cc: htejun, hare, axboe, linux-ide
On Mon, 2006-07-10 at 11:35 +0800, zhao, forrest wrote:
> This patch implements the AHCI suspend/resume.
> It integrates the suspend/resume operations with Power Management
> framework.
>
> Signed-off-by: Forrest Zhao <forrest.zhaot@intel.com>
> Signed-off-by: Hannes Reinecke <hare@suse.de>
> Signed-off-by: Jens Axboe <axboe@suse.de>
>
>
Please forget this patch, I'll send out the correct one.
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 6/6] Implement the AHCI suspend/resume
@ 2006-07-10 7:34 zhao, forrest
0 siblings, 0 replies; 12+ messages in thread
From: zhao, forrest @ 2006-07-10 7:34 UTC (permalink / raw)
To: jgarzik, htejun, hare, axboe; +Cc: linux-ide
This patch implements the AHCI suspend/resume.
Signed-off-by: Forrest Zhao <forrest.zhaot@intel.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Jens Axboe <axboe@suse.de>
---
drivers/scsi/ahci.c | 127 ++++++++++++++++++++++++++++++++++++++++++++
+++++++
1 files changed, 127 insertions(+), 0 deletions(-)
6fd581dceefd2f40212c468a3e0c7c49c60c0500
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index dc76798..28e1419 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -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;
@@ -1022,6 +1080,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;
--
1.2.6
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 6/6] Implement the AHCI suspend/resume
@ 2006-07-11 6:42 zhao, forrest
0 siblings, 0 replies; 12+ messages in thread
From: zhao, forrest @ 2006-07-11 6:42 UTC (permalink / raw)
To: jgarzik, htejun, hare, axboe; +Cc: linux-ide
This patch implements the AHCI suspend/resume.
Signed-off-by: Forrest Zhao <forrest.zhaot@intel.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Jens Axboe <axboe@suse.de>
---
drivers/scsi/ahci.c | 127 ++++++++++++++++++++++++++++++++++++++++++++
+++++++
1 files changed, 127 insertions(+), 0 deletions(-)
6fd581dceefd2f40212c468a3e0c7c49c60c0500
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index dc76798..28e1419 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -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;
@@ -1022,6 +1080,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;
--
1.2.6
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 6/6] Implement the AHCI suspend/resume
@ 2006-07-13 5:39 zhao, forrest
0 siblings, 0 replies; 12+ messages in thread
From: zhao, forrest @ 2006-07-13 5:39 UTC (permalink / raw)
To: jgarzik, htejun, hare, axboe; +Cc: linux-ide
This patch implements the AHCI suspend/resume.
Signed-off-by: Forrest Zhao <forrest.zhao@intel.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Jens Axboe <axboe@suse.de>
---
drivers/scsi/ahci.c | 127 ++++++++++++++++++++++++++++++++++++++++++++
+++++++
1 files changed, 127 insertions(+), 0 deletions(-)
629bc20e8ac2f8b86e462a9be8e3c12b1978faae
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 5e04ef4..7c1b43d 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -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;
--
1.2.6
^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2006-07-13 5:53 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-06-02 7:46 [PATCH 6/6] Implement the AHCI suspend/resume zhao, forrest
2006-06-02 8:21 ` Hannes Reinecke
2006-06-02 8:34 ` Tejun Heo
2006-06-03 13:35 ` Tejun Heo
2006-06-05 5:05 ` zhao, forrest
-- strict thread matches above, loose matches on Subject: below --
2006-06-06 10:17 zhao, forrest
2006-06-29 8:21 zhao, forrest
2006-07-10 3:35 zhao, forrest
2006-07-10 7:34 ` zhao, forrest
2006-07-10 7:34 zhao, forrest
2006-07-11 6:42 zhao, forrest
2006-07-13 5:39 zhao, forrest
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).