Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3252

kernel-2.6.18-194.11.1.el5.src.rpm

From: zhao, forrest <forrest.zhao@intel.com>
Date: Thu, 13 Jul 2006 05:38:32 +0000 (+0800)
Subject: [PATCH] The redefinition of ahci_start_engine() and ahci_stop_engine()
X-Git-Tag: v2.6.12-rc2
X-Git-Url: http://git.kernel.org/git/?p=linux/kernel/git/jgarzik/libata-dev.git;a=commitdiff;h=5457f2194ad198a0aba4190ec99a6a81846fdca5

[PATCH] The redefinition of ahci_start_engine() and ahci_stop_engine()

- Make ahci_start_engine() and ahci_stop_engine() more consistent with
  AHCI spec 1.1
- Change their input parameter from ap to port_mmio
- Update the existing users of ahci_start_engine() and ahci_stop_engine()

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>
Signed-off-by: Jeff Garzik <jeff@garzik.org>

--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -205,6 +205,8 @@ static irqreturn_t ahci_interrupt (int i
 static void ahci_irq_clear(struct ata_port *ap);
 static int ahci_port_start(struct ata_port *ap);
 static void ahci_port_stop(struct ata_port *ap);
+static int ahci_start_engine(void __iomem *port_mmio);
+static int ahci_stop_engine(void __iomem *port_mmio);
 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);
@@ -508,41 +510,64 @@ static void ahci_scr_write (struct ata_p
 	writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4));
 }
 
-static int ahci_stop_engine(struct ata_port *ap)
+static int ahci_stop_engine(void __iomem *port_mmio)
 {
-	void __iomem *mmio = ap->host_set->mmio_base;
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-	int work;
 	u32 tmp;
 
 	tmp = readl(port_mmio + PORT_CMD);
+
+	/* Check if the HBA is idle */
+	if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
+		return 0;
+
+	/* Setting HBA to idle */
 	tmp &= ~PORT_CMD_START;
 	writel(tmp, port_mmio + PORT_CMD);
 
-	/* wait for engine to stop.  TODO: this could be
+	/* wait for engine to stop. This could be
 	 * as long as 500 msec
 	 */
-	work = 1000;
-	while (work-- > 0) {
-		tmp = readl(port_mmio + PORT_CMD);
-		if ((tmp & PORT_CMD_LIST_ON) == 0)
-			return 0;
-		udelay(10);
-	}
+	tmp = ata_wait_register(port_mmio + PORT_CMD,
+			        PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
+	if(tmp & PORT_CMD_LIST_ON)
+		return -EIO;
 
-	return -EIO;
+	return 0;
 }
 
-static void ahci_start_engine(struct ata_port *ap)
+static int ahci_start_engine(void __iomem *port_mmio)
 {
-	void __iomem *mmio = ap->host_set->mmio_base;
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 	u32 tmp;
 
+	/*
+	 * Get current status
+	 */
 	tmp = readl(port_mmio + PORT_CMD);
+
+	/*
+	 * AHCI rev 1.1 section 10.3.1:
+	 * Software shall not set PxCMD.ST to '1' until it verifies
+	 * that PxCMD.CR is '0' and has set PxCMD.FRE to '1'
+	 */
+	if ((tmp & PORT_CMD_FIS_RX) == 0)
+		return -EPERM;
+
+	/*
+	 * wait for engine to become idle.
+	 */
+	tmp = ata_wait_register(port_mmio + PORT_CMD,
+				PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500);
+	if(tmp & PORT_CMD_LIST_ON)
+		return -EBUSY;
+
+	/*
+	 * Start DMA
+	 */
 	tmp |= PORT_CMD_START;
 	writel(tmp, port_mmio + PORT_CMD);
 	readl(port_mmio + PORT_CMD); /* flush */
+
+	return 0;
 }
 
 static unsigned int ahci_dev_classify(struct ata_port *ap)
@@ -626,7 +651,7 @@ static int ahci_softreset(struct ata_por
 	}
 
 	/* prepare for SRST (AHCI-1.1 10.4.1) */
-	rc = ahci_stop_engine(ap);
+	rc = ahci_stop_engine(port_mmio);
 	if (rc) {
 		reason = "failed to stop engine";
 		goto fail_restart;
@@ -647,7 +672,7 @@ static int ahci_softreset(struct ata_por
 	}
 
 	/* restart engine */
-	ahci_start_engine(ap);
+	ahci_start_engine(port_mmio);
 
 	ata_tf_init(ap->device, &tf);
 	fis = pp->cmd_tbl;
@@ -706,7 +731,7 @@ static int ahci_softreset(struct ata_por
 	return 0;
 
  fail_restart:
-	ahci_start_engine(ap);
+	ahci_start_engine(port_mmio);
  fail:
 	ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason);
 	return rc;
@@ -717,11 +742,13 @@ static int ahci_hardreset(struct ata_por
 	struct ahci_port_priv *pp = ap->private_data;
 	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
 	struct ata_taskfile tf;
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 	int rc;
 
 	DPRINTK("ENTER\n");
 
-	ahci_stop_engine(ap);
+	ahci_stop_engine(port_mmio);
 
 	/* clear D2H reception area to properly wait for D2H FIS */
 	ata_tf_init(ap->device, &tf);
@@ -730,7 +757,7 @@ static int ahci_hardreset(struct ata_por
 
 	rc = sata_std_hardreset(ap, class);
 
-	ahci_start_engine(ap);
+	ahci_start_engine(port_mmio);
 
 	if (rc == 0 && ata_port_online(ap))
 		*class = ahci_dev_classify(ap);
@@ -1052,10 +1079,13 @@ static void ahci_thaw(struct ata_port *a
 
 static void ahci_error_handler(struct ata_port *ap)
 {
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+
 	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
 		/* restart engine */
-		ahci_stop_engine(ap);
-		ahci_start_engine(ap);
+		ahci_stop_engine(port_mmio);
+		ahci_start_engine(port_mmio);
 	}
 
 	/* perform recovery */
@@ -1066,14 +1096,16 @@ static void ahci_error_handler(struct at
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
 {
 	struct ata_port *ap = qc->ap;
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 
 	if (qc->flags & ATA_QCFLAG_FAILED)
 		qc->err_mask |= AC_ERR_OTHER;
 
 	if (qc->err_mask) {
 		/* make DMA engine forget about the failed command */
-		ahci_stop_engine(ap);
-		ahci_start_engine(ap);
+		ahci_stop_engine(port_mmio);
+		ahci_start_engine(port_mmio);
 	}
 }
 
* move ahci_port_start/stop() below EH functions.  This makes ahci
  more consistent with other drivers and makes prototypes for
  ahci_start/stop_engine() unnecessary.

* swap positions between ahci_start_engine() and ahci_stop_engine()
  for readability.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com>

0353fed85301f2f2af7fdce02f6b9c1f55160462
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index f1516ca..92e2b95 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -205,8 +205,6 @@ static irqreturn_t ahci_interrupt (int i
 static void ahci_irq_clear(struct ata_port *ap);
 static int ahci_port_start(struct ata_port *ap);
 static void ahci_port_stop(struct ata_port *ap);
-static int ahci_start_engine(void __iomem *port_mmio);
-static int ahci_stop_engine(void __iomem *port_mmio);
 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);
@@ -374,108 +372,6 @@ static inline void __iomem *ahci_port_ba
 	return (void __iomem *) ahci_port_base_ul((unsigned long)base, port);
 }
 
-static int ahci_port_start(struct ata_port *ap)
-{
-	struct device *dev = ap->host_set->dev;
-	struct ahci_host_priv *hpriv = ap->host_set->private_data;
-	struct ahci_port_priv *pp;
-	void __iomem *mmio = ap->host_set->mmio_base;
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-	void *mem;
-	dma_addr_t mem_dma;
-	int rc;
-
-	pp = kmalloc(sizeof(*pp), GFP_KERNEL);
-	if (!pp)
-		return -ENOMEM;
-	memset(pp, 0, sizeof(*pp));
-
-	rc = ata_pad_alloc(ap, dev);
-	if (rc) {
-		kfree(pp);
-		return rc;
-	}
-
-	mem = dma_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL);
-	if (!mem) {
-		ata_pad_free(ap, dev);
-		kfree(pp);
-		return -ENOMEM;
-	}
-	memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
-
-	/*
-	 * First item in chunk of DMA memory: 32-slot command table,
-	 * 32 bytes each in size
-	 */
-	pp->cmd_slot = mem;
-	pp->cmd_slot_dma = mem_dma;
-
-	mem += AHCI_CMD_SLOT_SZ;
-	mem_dma += AHCI_CMD_SLOT_SZ;
-
-	/*
-	 * Second item: Received-FIS area
-	 */
-	pp->rx_fis = mem;
-	pp->rx_fis_dma = mem_dma;
-
-	mem += AHCI_RX_FIS_SZ;
-	mem_dma += AHCI_RX_FIS_SZ;
-
-	/*
-	 * Third item: data area for storing a single command
-	 * and its scatter-gather table
-	 */
-	pp->cmd_tbl = mem;
-	pp->cmd_tbl_dma = mem_dma;
-
-	ap->private_data = pp;
-
-	if (hpriv->cap & HOST_CAP_64)
-		writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
-	writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
-	readl(port_mmio + PORT_LST_ADDR); /* flush */
-
-	if (hpriv->cap & HOST_CAP_64)
-		writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
-	writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
-	readl(port_mmio + PORT_FIS_ADDR); /* flush */
-
-	writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
-	       PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
-	       PORT_CMD_START, port_mmio + PORT_CMD);
-	readl(port_mmio + PORT_CMD); /* flush */
-
-	return 0;
-}
-
-
-static void ahci_port_stop(struct ata_port *ap)
-{
-	struct device *dev = ap->host_set->dev;
-	struct ahci_port_priv *pp = ap->private_data;
-	void __iomem *mmio = ap->host_set->mmio_base;
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-	u32 tmp;
-
-	tmp = readl(port_mmio + PORT_CMD);
-	tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX);
-	writel(tmp, port_mmio + PORT_CMD);
-	readl(port_mmio + PORT_CMD); /* flush */
-
-	/* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
-	 * this is slightly incorrect.
-	 */
-	msleep(500);
-
-	ap->private_data = NULL;
-	dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ,
-			  pp->cmd_slot, pp->cmd_slot_dma);
-	ata_pad_free(ap, dev);
-	kfree(pp);
-}
-
 static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
 {
 	unsigned int sc_reg;
@@ -510,31 +406,6 @@ static void ahci_scr_write (struct ata_p
 	writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4));
 }
 
-static int ahci_stop_engine(void __iomem *port_mmio)
-{
-	u32 tmp;
-
-	tmp = readl(port_mmio + PORT_CMD);
-
-	/* Check if the HBA is idle */
-	if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
-		return 0;
-
-	/* Setting HBA to idle */
-	tmp &= ~PORT_CMD_START;
-	writel(tmp, port_mmio + PORT_CMD);
-
-	/* wait for engine to stop. This could be
-	 * as long as 500 msec
-	 */
-	tmp = ata_wait_register(port_mmio + PORT_CMD,
-			        PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
-	if(tmp & PORT_CMD_LIST_ON)
-		return -EIO;
-
-	return 0;
-}
-
 static int ahci_start_engine(void __iomem *port_mmio)
 {
 	u32 tmp;
@@ -570,6 +441,31 @@ static int ahci_start_engine(void __iome
 	return 0;
 }
 
+static int ahci_stop_engine(void __iomem *port_mmio)
+{
+	u32 tmp;
+
+	tmp = readl(port_mmio + PORT_CMD);
+
+	/* Check if the HBA is idle */
+	if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
+		return 0;
+
+	/* Setting HBA to idle */
+	tmp &= ~PORT_CMD_START;
+	writel(tmp, port_mmio + PORT_CMD);
+
+	/* wait for engine to stop. This could be
+	 * as long as 500 msec
+	 */
+	tmp = ata_wait_register(port_mmio + PORT_CMD,
+			        PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
+	if(tmp & PORT_CMD_LIST_ON)
+		return -EIO;
+
+	return 0;
+}
+
 static unsigned int ahci_dev_classify(struct ata_port *ap)
 {
 	void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
@@ -1109,6 +1005,107 @@ static void ahci_post_internal_cmd(struc
 	}
 }
 
+static int ahci_port_start(struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	struct ahci_host_priv *hpriv = ap->host_set->private_data;
+	struct ahci_port_priv *pp;
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	void *mem;
+	dma_addr_t mem_dma;
+	int rc;
+
+	pp = kmalloc(sizeof(*pp), GFP_KERNEL);
+	if (!pp)
+		return -ENOMEM;
+	memset(pp, 0, sizeof(*pp));
+
+	rc = ata_pad_alloc(ap, dev);
+	if (rc) {
+		kfree(pp);
+		return rc;
+	}
+
+	mem = dma_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL);
+	if (!mem) {
+		ata_pad_free(ap, dev);
+		kfree(pp);
+		return -ENOMEM;
+	}
+	memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
+
+	/*
+	 * First item in chunk of DMA memory: 32-slot command table,
+	 * 32 bytes each in size
+	 */
+	pp->cmd_slot = mem;
+	pp->cmd_slot_dma = mem_dma;
+
+	mem += AHCI_CMD_SLOT_SZ;
+	mem_dma += AHCI_CMD_SLOT_SZ;
+
+	/*
+	 * Second item: Received-FIS area
+	 */
+	pp->rx_fis = mem;
+	pp->rx_fis_dma = mem_dma;
+
+	mem += AHCI_RX_FIS_SZ;
+	mem_dma += AHCI_RX_FIS_SZ;
+
+	/*
+	 * Third item: data area for storing a single command
+	 * and its scatter-gather table
+	 */
+	pp->cmd_tbl = mem;
+	pp->cmd_tbl_dma = mem_dma;
+
+	ap->private_data = pp;
+
+	if (hpriv->cap & HOST_CAP_64)
+		writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
+	writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
+	readl(port_mmio + PORT_LST_ADDR); /* flush */
+
+	if (hpriv->cap & HOST_CAP_64)
+		writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
+	writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
+	readl(port_mmio + PORT_FIS_ADDR); /* flush */
+
+	writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
+	       PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
+	       PORT_CMD_START, port_mmio + PORT_CMD);
+	readl(port_mmio + PORT_CMD); /* flush */
+
+	return 0;
+}
+
+static void ahci_port_stop(struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	struct ahci_port_priv *pp = ap->private_data;
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	u32 tmp;
+
+	tmp = readl(port_mmio + PORT_CMD);
+	tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX);
+	writel(tmp, port_mmio + PORT_CMD);
+	readl(port_mmio + PORT_CMD); /* flush */
+
+	/* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
+	 * this is slightly incorrect.
+	 */
+	msleep(500);
+
+	ap->private_data = NULL;
+	dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ,
+			  pp->cmd_slot, pp->cmd_slot_dma);
+	ata_pad_free(ap, dev);
+	kfree(pp);
+}
+
 static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
 			    unsigned int port_idx)
 {
* fascist-format comments according to comment style used in libata
  core layer.

* if() -> if ()

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com>

51d2cea03da874cd41cd7a29d1314ddbe46b5314
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 92e2b95..ee00aed 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -410,30 +410,23 @@ static int ahci_start_engine(void __iome
 {
 	u32 tmp;
 
-	/*
-	 * Get current status
-	 */
+	/* get current status */
 	tmp = readl(port_mmio + PORT_CMD);
 
-	/*
-	 * AHCI rev 1.1 section 10.3.1:
+	/* AHCI rev 1.1 section 10.3.1:
 	 * Software shall not set PxCMD.ST to '1' until it verifies
 	 * that PxCMD.CR is '0' and has set PxCMD.FRE to '1'
 	 */
 	if ((tmp & PORT_CMD_FIS_RX) == 0)
 		return -EPERM;
 
-	/*
-	 * wait for engine to become idle.
-	 */
+	/* wait for engine to become idle */
 	tmp = ata_wait_register(port_mmio + PORT_CMD,
 				PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500);
-	if(tmp & PORT_CMD_LIST_ON)
+	if (tmp & PORT_CMD_LIST_ON)
 		return -EBUSY;
 
-	/*
-	 * Start DMA
-	 */
+	/* start DMA */
 	tmp |= PORT_CMD_START;
 	writel(tmp, port_mmio + PORT_CMD);
 	readl(port_mmio + PORT_CMD); /* flush */
@@ -447,20 +440,18 @@ static int ahci_stop_engine(void __iomem
 
 	tmp = readl(port_mmio + PORT_CMD);
 
-	/* Check if the HBA is idle */
+	/* check if the HBA is idle */
 	if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
 		return 0;
 
-	/* Setting HBA to idle */
+	/* setting HBA to idle */
 	tmp &= ~PORT_CMD_START;
 	writel(tmp, port_mmio + PORT_CMD);
 
-	/* wait for engine to stop. This could be
-	 * as long as 500 msec
-	 */
+	/* wait for engine to stop. This could be as long as 500 msec */
 	tmp = ata_wait_register(port_mmio + PORT_CMD,
 			        PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
-	if(tmp & PORT_CMD_LIST_ON)
+	if (tmp & PORT_CMD_LIST_ON)
 		return -EIO;
 
 	return 0;
Simplify ahci_start_engine() by killing prerequisite condition checks.
Rationales are..

* No user checks error return from ahci_start_engine()

* Code flow guarantees the prerequisite conditions unless the
  controller is malfunctioning.  In such cases, the driver had chances
  to learn about the problem _before_ calling this function.

* Closely related to the above two, driver calls into this function
  even when prerequisites fail hoping for the best.

Basically, ahci_start_engine() should only do the operation itself.
It isn't the right place to check for prerequisites.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com>

e98289381268b565aadcda0e32e0bdd5fad5f9f4
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index ee00aed..e02b9c6 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -406,32 +406,15 @@ static void ahci_scr_write (struct ata_p
 	writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4));
 }
 
-static int ahci_start_engine(void __iomem *port_mmio)
+static void ahci_start_engine(void __iomem *port_mmio)
 {
 	u32 tmp;
 
-	/* get current status */
-	tmp = readl(port_mmio + PORT_CMD);
-
-	/* AHCI rev 1.1 section 10.3.1:
-	 * Software shall not set PxCMD.ST to '1' until it verifies
-	 * that PxCMD.CR is '0' and has set PxCMD.FRE to '1'
-	 */
-	if ((tmp & PORT_CMD_FIS_RX) == 0)
-		return -EPERM;
-
-	/* wait for engine to become idle */
-	tmp = ata_wait_register(port_mmio + PORT_CMD,
-				PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1,500);
-	if (tmp & PORT_CMD_LIST_ON)
-		return -EBUSY;
-
 	/* start DMA */
+	tmp = readl(port_mmio + PORT_CMD);
 	tmp |= PORT_CMD_START;
 	writel(tmp, port_mmio + PORT_CMD);
 	readl(port_mmio + PORT_CMD); /* flush */
-
-	return 0;
 }
 
 static int ahci_stop_engine(void __iomem *port_mmio)
Implement ahci_[de]init_port() and use it during initialization and
de-initialization.  ahci_[de]init_port() are supersets of what used to
be done during driver [de-]initialization.  This patch makes the
following behavior changes.

* Per-port IRQ mask is cleared on driver load as done in other
  drivers.  The mask will be configured properly during probe.

* During init_one(), HOST_IRQ_STAT is cleared after masking port IRQs
  such that there is no race window.

* CMD_SPIN_UP is cleared during init_one() instead of being set.  It
  is set in port_start().  This is more consistent with overall
  structure of initialization.  Note that CMD_SPIN_UP simply controls
  PHY activation.

* Slumber and staggered spin-up are handled properly.

* All init/deinit operations are done in step-by-step manner as
  described in the spec instead of issued as single merged command.

Original implementation is from Zhao, Forrest <forrest.zhao@intel.com>

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com>

d773d83a6f3c558226b24a09131b0afa83e0ac02
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index e02b9c6..fb71fa7 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -92,7 +92,9 @@ enum {
 	HOST_AHCI_EN		= (1 << 31), /* AHCI enabled */
 
 	/* HOST_CAP bits */
+	HOST_CAP_SSC		= (1 << 14), /* Slumber capable */
 	HOST_CAP_CLO		= (1 << 24), /* Command List Override support */
+	HOST_CAP_SSS		= (1 << 27), /* Staggered Spin-up */
 	HOST_CAP_NCQ		= (1 << 30), /* Native Command Queueing */
 	HOST_CAP_64		= (1 << 31), /* PCI DAC (64-bit DMA) support */
 
@@ -155,6 +157,7 @@ enum {
 	PORT_CMD_SPIN_UP	= (1 << 1), /* Spin up device */
 	PORT_CMD_START		= (1 << 0), /* Enable port DMA engine */
 
+	PORT_CMD_ICC_MASK	= (0xf << 28), /* i/f ICC state mask */
 	PORT_CMD_ICC_ACTIVE	= (0x1 << 28), /* Put i/f in active state */
 	PORT_CMD_ICC_PARTIAL	= (0x2 << 28), /* Put i/f in partial state */
 	PORT_CMD_ICC_SLUMBER	= (0x6 << 28), /* Put i/f in slumber state */
@@ -440,6 +443,135 @@ static int ahci_stop_engine(void __iomem
 	return 0;
 }
 
+static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap,
+			      dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma)
+{
+	u32 tmp;
+
+	/* set FIS registers */
+	if (cap & HOST_CAP_64)
+		writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
+	writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
+
+	if (cap & HOST_CAP_64)
+		writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
+	writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
+
+	/* enable FIS reception */
+	tmp = readl(port_mmio + PORT_CMD);
+	tmp |= PORT_CMD_FIS_RX;
+	writel(tmp, port_mmio + PORT_CMD);
+
+	/* flush */
+	readl(port_mmio + PORT_CMD);
+}
+
+static int ahci_stop_fis_rx(void __iomem *port_mmio)
+{
+	u32 tmp;
+
+	/* disable FIS reception */
+	tmp = readl(port_mmio + PORT_CMD);
+	tmp &= ~PORT_CMD_FIS_RX;
+	writel(tmp, port_mmio + PORT_CMD);
+
+	/* wait for completion, spec says 500ms, give it 1000 */
+	tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_FIS_ON,
+				PORT_CMD_FIS_ON, 10, 1000);
+	if (tmp & PORT_CMD_FIS_ON)
+		return -EBUSY;
+
+	return 0;
+}
+
+static void ahci_power_up(void __iomem *port_mmio, u32 cap)
+{
+	u32 cmd;
+
+	cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
+
+	/* spin up device */
+	if (cap & HOST_CAP_SSS) {
+		cmd |= PORT_CMD_SPIN_UP;
+		writel(cmd, port_mmio + PORT_CMD);
+	}
+
+	/* wake up link */
+	writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD);
+}
+
+static void ahci_power_down(void __iomem *port_mmio, u32 cap)
+{
+	u32 cmd, scontrol;
+
+	cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
+
+	if (cap & HOST_CAP_SSC) {
+		/* enable transitions to slumber mode */
+		scontrol = readl(port_mmio + PORT_SCR_CTL);
+		if ((scontrol & 0x0f00) > 0x100) {
+			scontrol &= ~0xf00;
+			writel(scontrol, port_mmio + PORT_SCR_CTL);
+		}
+
+		/* put device into slumber mode */
+		writel(cmd | PORT_CMD_ICC_SLUMBER, port_mmio + PORT_CMD);
+
+		/* wait for the transition to complete */
+		ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_ICC_SLUMBER,
+				  PORT_CMD_ICC_SLUMBER, 1, 50);
+	}
+
+	/* put device into listen mode */
+	if (cap & HOST_CAP_SSS) {
+		/* first set PxSCTL.DET to 0 */
+		scontrol = readl(port_mmio + PORT_SCR_CTL);
+		scontrol &= ~0xf;
+		writel(scontrol, port_mmio + PORT_SCR_CTL);
+
+		/* then set PxCMD.SUD to 0 */
+		cmd &= ~PORT_CMD_SPIN_UP;
+		writel(cmd, port_mmio + PORT_CMD);
+	}
+}
+
+static void ahci_init_port(void __iomem *port_mmio, u32 cap,
+			   dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma)
+{
+	/* power up */
+	ahci_power_up(port_mmio, cap);
+
+	/* enable FIS reception */
+	ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma);
+
+	/* enable DMA */
+	ahci_start_engine(port_mmio);
+}
+
+static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg)
+{
+	int rc;
+
+	/* disable DMA */
+	rc = ahci_stop_engine(port_mmio);
+	if (rc) {
+		*emsg = "failed to stop engine";
+		return rc;
+	}
+
+	/* disable FIS reception */
+	rc = ahci_stop_fis_rx(port_mmio);
+	if (rc) {
+		*emsg = "failed stop FIS RX";
+		return rc;
+	}
+
+	/* put device into slumber mode */
+	ahci_power_down(port_mmio, cap);
+
+	return 0;
+}
+
 static unsigned int ahci_dev_classify(struct ata_port *ap)
 {
 	void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
@@ -1037,20 +1169,8 @@ static int ahci_port_start(struct ata_po
 
 	ap->private_data = pp;
 
-	if (hpriv->cap & HOST_CAP_64)
-		writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
-	writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
-	readl(port_mmio + PORT_LST_ADDR); /* flush */
-
-	if (hpriv->cap & HOST_CAP_64)
-		writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
-	writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
-	readl(port_mmio + PORT_FIS_ADDR); /* flush */
-
-	writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
-	       PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
-	       PORT_CMD_START, port_mmio + PORT_CMD);
-	readl(port_mmio + PORT_CMD); /* flush */
+	/* initialize port */
+	ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
 
 	return 0;
 }
@@ -1058,20 +1178,17 @@ static int ahci_port_start(struct ata_po
 static void ahci_port_stop(struct ata_port *ap)
 {
 	struct device *dev = ap->host_set->dev;
+	struct ahci_host_priv *hpriv = ap->host_set->private_data;
 	struct ahci_port_priv *pp = ap->private_data;
 	void __iomem *mmio = ap->host_set->mmio_base;
 	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-	u32 tmp;
-
-	tmp = readl(port_mmio + PORT_CMD);
-	tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX);
-	writel(tmp, port_mmio + PORT_CMD);
-	readl(port_mmio + PORT_CMD); /* flush */
+	const char *emsg = NULL;
+	int rc;
 
-	/* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
-	 * this is slightly incorrect.
-	 */
-	msleep(500);
+	/* de-initialize port */
+	rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+	if (rc)
+		ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc);
 
 	ap->private_data = NULL;
 	dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ,
@@ -1099,7 +1216,7 @@ static int ahci_host_init(struct ata_pro
 	struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
 	void __iomem *mmio = probe_ent->mmio_base;
 	u32 tmp, cap_save;
-	unsigned int i, j, using_dac;
+	unsigned int i, using_dac;
 	int rc;
 	void __iomem *port_mmio;
 
@@ -1175,6 +1292,8 @@ static int ahci_host_init(struct ata_pro
 	}
 
 	for (i = 0; i < probe_ent->n_ports; i++) {
+		const char *emsg = NULL;
+
 #if 0 /* BIOSen initialize this incorrectly */
 		if (!(hpriv->port_map & (1 << i)))
 			continue;
@@ -1187,43 +1306,24 @@ #endif
 				(unsigned long) mmio, i);
 
 		/* make sure port is not active */
-		tmp = readl(port_mmio + PORT_CMD);
-		VPRINTK("PORT_CMD 0x%x\n", tmp);
-		if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
-			   PORT_CMD_FIS_RX | PORT_CMD_START)) {
-			tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
-				 PORT_CMD_FIS_RX | PORT_CMD_START);
-			writel(tmp, port_mmio + PORT_CMD);
-			readl(port_mmio + PORT_CMD); /* flush */
-
-			/* spec says 500 msecs for each bit, so
-			 * this is slightly incorrect.
-			 */
-			msleep(500);
-		}
-
-		writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
-
-		j = 0;
-		while (j < 100) {
-			msleep(10);
-			tmp = readl(port_mmio + PORT_SCR_STAT);
-			if ((tmp & 0xf) == 0x3)
-				break;
-			j++;
-		}
+		rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+		if (rc)
+			dev_printk(KERN_WARNING, &pdev->dev,
+				   "%s (%d)\n", emsg, rc);
 
+		/* clear SError */
 		tmp = readl(port_mmio + PORT_SCR_ERR);
 		VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
 		writel(tmp, port_mmio + PORT_SCR_ERR);
 
-		/* ack any pending irq events for this port */
+		/* clear & turn off port IRQ */
 		tmp = readl(port_mmio + PORT_IRQ_STAT);
 		VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
 		if (tmp)
 			writel(tmp, port_mmio + PORT_IRQ_STAT);
 
 		writel(1 << i, mmio + HOST_IRQ_STAT);
+		writel(0, port_mmio + PORT_IRQ_MASK);
 	}
 
 	tmp = readl(mmio + HOST_CTL);
Separate out ahci_reset_controller() and ahci_init_controller() from
ata_host_init().  These will be used by PM callbacks.  This patch
doesn't introduce any behavior change.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com>

b2e525899eb80c1433cc21af33430574e15c4d94
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index fb71fa7..a9e0c5f 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -572,6 +572,94 @@ static int ahci_deinit_port(void __iomem
 	return 0;
 }
 
+static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
+{
+	u32 cap_save, tmp;
+
+	cap_save = readl(mmio + HOST_CAP);
+	cap_save &= ( (1<<28) | (1<<17) );
+	cap_save |= (1 << 27);
+
+	/* global controller reset */
+	tmp = readl(mmio + HOST_CTL);
+	if ((tmp & HOST_RESET) == 0) {
+		writel(tmp | HOST_RESET, mmio + HOST_CTL);
+		readl(mmio + HOST_CTL); /* flush */
+	}
+
+	/* reset must complete within 1 second, or
+	 * the hardware should be considered fried.
+	 */
+	ssleep(1);
+
+	tmp = readl(mmio + HOST_CTL);
+	if (tmp & HOST_RESET) {
+		dev_printk(KERN_ERR, &pdev->dev,
+			   "controller reset failed (0x%x)\n", tmp);
+		return -EIO;
+	}
+
+	writel(HOST_AHCI_EN, mmio + HOST_CTL);
+	(void) readl(mmio + HOST_CTL);	/* flush */
+	writel(cap_save, mmio + HOST_CAP);
+	writel(0xf, mmio + HOST_PORTS_IMPL);
+	(void) readl(mmio + HOST_PORTS_IMPL);	/* flush */
+
+	if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
+		u16 tmp16;
+
+		/* configure PCS */
+		pci_read_config_word(pdev, 0x92, &tmp16);
+		tmp16 |= 0xf;
+		pci_write_config_word(pdev, 0x92, tmp16);
+	}
+
+	return 0;
+}
+
+static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev,
+				 int n_ports, u32 cap)
+{
+	int i, rc;
+	u32 tmp;
+
+	for (i = 0; i < n_ports; i++) {
+		void __iomem *port_mmio = ahci_port_base(mmio, i);
+		const char *emsg = NULL;
+
+#if 0 /* BIOSen initialize this incorrectly */
+		if (!(hpriv->port_map & (1 << i)))
+			continue;
+#endif
+
+		/* make sure port is not active */
+		rc = ahci_deinit_port(port_mmio, cap, &emsg);
+		if (rc)
+			dev_printk(KERN_WARNING, &pdev->dev,
+				   "%s (%d)\n", emsg, rc);
+
+		/* clear SError */
+		tmp = readl(port_mmio + PORT_SCR_ERR);
+		VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
+		writel(tmp, port_mmio + PORT_SCR_ERR);
+
+		/* clear & turn off port IRQ */
+		tmp = readl(port_mmio + PORT_IRQ_STAT);
+		VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
+		if (tmp)
+			writel(tmp, port_mmio + PORT_IRQ_STAT);
+
+		writel(1 << i, mmio + HOST_IRQ_STAT);
+		writel(0, port_mmio + PORT_IRQ_MASK);
+	}
+
+	tmp = readl(mmio + HOST_CTL);
+	VPRINTK("HOST_CTL 0x%x\n", tmp);
+	writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
+	tmp = readl(mmio + HOST_CTL);
+	VPRINTK("HOST_CTL 0x%x\n", tmp);
+}
+
 static unsigned int ahci_dev_classify(struct ata_port *ap)
 {
 	void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
@@ -1215,47 +1303,12 @@ static int ahci_host_init(struct ata_pro
 	struct ahci_host_priv *hpriv = probe_ent->private_data;
 	struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
 	void __iomem *mmio = probe_ent->mmio_base;
-	u32 tmp, cap_save;
 	unsigned int i, using_dac;
 	int rc;
-	void __iomem *port_mmio;
-
-	cap_save = readl(mmio + HOST_CAP);
-	cap_save &= ( (1<<28) | (1<<17) );
-	cap_save |= (1 << 27);
-
-	/* global controller reset */
-	tmp = readl(mmio + HOST_CTL);
-	if ((tmp & HOST_RESET) == 0) {
-		writel(tmp | HOST_RESET, mmio + HOST_CTL);
-		readl(mmio + HOST_CTL); /* flush */
-	}
-
-	/* reset must complete within 1 second, or
-	 * the hardware should be considered fried.
-	 */
-	ssleep(1);
-
-	tmp = readl(mmio + HOST_CTL);
-	if (tmp & HOST_RESET) {
-		dev_printk(KERN_ERR, &pdev->dev,
-			   "controller reset failed (0x%x)\n", tmp);
-		return -EIO;
-	}
 
-	writel(HOST_AHCI_EN, mmio + HOST_CTL);
-	(void) readl(mmio + HOST_CTL);	/* flush */
-	writel(cap_save, mmio + HOST_CAP);
-	writel(0xf, mmio + HOST_PORTS_IMPL);
-	(void) readl(mmio + HOST_PORTS_IMPL);	/* flush */
-
-	if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
-		u16 tmp16;
-
-		pci_read_config_word(pdev, 0x92, &tmp16);
-		tmp16 |= 0xf;
-		pci_write_config_word(pdev, 0x92, tmp16);
-	}
+	rc = ahci_reset_controller(mmio, pdev);
+	if (rc)
+		return rc;
 
 	hpriv->cap = readl(mmio + HOST_CAP);
 	hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
@@ -1291,46 +1344,10 @@ static int ahci_host_init(struct ata_pro
 		}
 	}
 
-	for (i = 0; i < probe_ent->n_ports; i++) {
-		const char *emsg = NULL;
-
-#if 0 /* BIOSen initialize this incorrectly */
-		if (!(hpriv->port_map & (1 << i)))
-			continue;
-#endif
+	for (i = 0; i < probe_ent->n_ports; i++)
+		ahci_setup_port(&probe_ent->port[i], (unsigned long) mmio, i);
 
-		port_mmio = ahci_port_base(mmio, i);
-		VPRINTK("mmio %p  port_mmio %p\n", mmio, port_mmio);
-
-		ahci_setup_port(&probe_ent->port[i],
-				(unsigned long) mmio, i);
-
-		/* make sure port is not active */
-		rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
-		if (rc)
-			dev_printk(KERN_WARNING, &pdev->dev,
-				   "%s (%d)\n", emsg, rc);
-
-		/* clear SError */
-		tmp = readl(port_mmio + PORT_SCR_ERR);
-		VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
-		writel(tmp, port_mmio + PORT_SCR_ERR);
-
-		/* clear & turn off port IRQ */
-		tmp = readl(port_mmio + PORT_IRQ_STAT);
-		VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
-		if (tmp)
-			writel(tmp, port_mmio + PORT_IRQ_STAT);
-
-		writel(1 << i, mmio + HOST_IRQ_STAT);
-		writel(0, port_mmio + PORT_IRQ_MASK);
-	}
-
-	tmp = readl(mmio + HOST_CTL);
-	VPRINTK("HOST_CTL 0x%x\n", tmp);
-	writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
-	tmp = readl(mmio + HOST_CTL);
-	VPRINTK("HOST_CTL 0x%x\n", tmp);
+	ahci_init_controller(mmio, pdev, probe_ent->n_ports, hpriv->cap);
 
 	pci_set_master(pdev);
 
Implement power management support.

Original implementation is from Zhao, Forrest <forrest.zhao@intel.com>

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com>

4513d4761b48bca3025af0832d517f6b913061f9
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index a9e0c5f..909a4dc 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -215,6 +215,10 @@ 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_port_suspend(struct ata_port *ap, pm_message_t mesg);
+static int ahci_port_resume(struct ata_port *ap);
+static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
+static int ahci_pci_device_resume(struct pci_dev *pdev);
 static void ahci_remove_one (struct pci_dev *pdev);
 
 static struct scsi_host_template ahci_sht = {
@@ -234,6 +238,8 @@ static struct scsi_host_template ahci_sh
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.suspend		= ata_scsi_device_suspend,
+	.resume			= ata_scsi_device_resume,
 };
 
 static const struct ata_port_operations ahci_ops = {
@@ -260,6 +266,9 @@ static const struct ata_port_operations 
 	.error_handler		= ahci_error_handler,
 	.post_internal_cmd	= ahci_post_internal_cmd,
 
+	.port_suspend		= ahci_port_suspend,
+	.port_resume		= ahci_port_resume,
+
 	.port_start		= ahci_port_start,
 	.port_stop		= ahci_port_stop,
 };
@@ -361,6 +370,8 @@ static struct pci_driver ahci_pci_driver
 	.name			= DRV_NAME,
 	.id_table		= ahci_pci_tbl,
 	.probe			= ahci_init_one,
+	.suspend		= ahci_pci_device_suspend,
+	.resume			= ahci_pci_device_resume,
 	.remove			= ahci_remove_one,
 };
 
@@ -1199,6 +1210,79 @@ static void ahci_post_internal_cmd(struc
 	}
 }
 
+static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
+{
+	struct ahci_host_priv *hpriv = ap->host_set->private_data;
+	struct ahci_port_priv *pp = ap->private_data;
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	const char *emsg = NULL;
+	int rc;
+
+	rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+	if (rc) {
+		ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc);
+		ahci_init_port(port_mmio, hpriv->cap,
+			       pp->cmd_slot_dma, pp->rx_fis_dma);
+	}
+
+	return rc;
+}
+
+static int ahci_port_resume(struct ata_port *ap)
+{
+	struct ahci_port_priv *pp = ap->private_data;
+	struct ahci_host_priv *hpriv = ap->host_set->private_data;
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+
+	ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
+
+	return 0;
+}
+
+static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
+{
+	struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+	void __iomem *mmio = host_set->mmio_base;
+	u32 ctl;
+
+	if (mesg.event == PM_EVENT_SUSPEND) {
+		/* AHCI spec rev1.1 section 8.3.3:
+		 * Software must disable interrupts prior to requesting a
+		 * transition of the HBA to D3 state.
+		 */
+		ctl = readl(mmio + HOST_CTL);
+		ctl &= ~HOST_IRQ_EN;
+		writel(ctl, mmio + HOST_CTL);
+		readl(mmio + HOST_CTL); /* flush */
+	}
+
+	return ata_pci_device_suspend(pdev, mesg);
+}
+
+static int ahci_pci_device_resume(struct pci_dev *pdev)
+{
+	struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+	struct ahci_host_priv *hpriv = host_set->private_data;
+	void __iomem *mmio = host_set->mmio_base;
+	int rc;
+
+	ata_pci_device_do_resume(pdev);
+
+	if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+		rc = ahci_reset_controller(mmio, pdev);
+		if (rc)
+			return rc;
+
+		ahci_init_controller(mmio, pdev, host_set->n_ports, hpriv->cap);
+	}
+
+	ata_host_set_resume(host_set);
+
+	return 0;
+}
+
 static int ahci_port_start(struct ata_port *ap)
 {
 	struct device *dev = ap->host_set->dev;