linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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).