Add AHCI suspend support.

* debian/changelog: Update.
* debian/patches/features/all/drivers/scsi-ahci-*: Add.
* debian/patches/series/3: Add patches.

svn path=/dists/trunk/linux-2.6/; revision=7593
This commit is contained in:
Bastian Blank 2006-10-10 14:43:21 +00:00
parent 81c4ef9e2c
commit b6d38f3f57
9 changed files with 1209 additions and 1 deletions

3
debian/changelog vendored
View File

@ -4,6 +4,7 @@ linux-2.6 (2.6.18-3) UNRELEASED; urgency=low
* Fix home of patch apply script.
* Unify CPUSET option. (closes: #391931)
* Support xen version 3.0.3-1 and 3.0.3-rc1.
* Add AHCI suspend support.
[ Steve Langasek ]
* [alpha] restore alpha-prctl.patch, which keeps disappearing every time
@ -30,7 +31,7 @@ linux-2.6 (2.6.18-3) UNRELEASED; urgency=low
* bugfix/sky2-receive-FIFO-fix.patch: fix sky2 hangs on some chips
Thanks to Stephen Hemminger for the patch. (Closes: #391382)
-- Frederik Schüler <fs@debian.org> Tue, 10 Oct 2006 13:50:08 +0200
-- Bastian Blank <waldi@debian.org> Tue, 10 Oct 2006 16:40:26 +0200
linux-2.6 (2.6.18-2) unstable; urgency=low

View File

@ -0,0 +1,183 @@
# [PATCH] The redefinition of ahci_start_engine() and ahci_stop_engine()
# 5457f2194ad198a0aba4190ec99a6a81846fdca5
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 77e7202..f1516ca 100644
--- 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);
}
}

View File

@ -0,0 +1,297 @@
# [PATCH] ahci: relocate several internal functions
# 254950cd56fee220c9d548f3e57211b95976ba64
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)
{

View File

@ -0,0 +1,68 @@
# [PATCH] ahci: cosmetic changes to ahci_start/stop_engine()
# d8fcd116d203dfe2f6c272d0cd67724b172f1bc2
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;

View File

@ -0,0 +1,42 @@
# [PATCH] ahci: simplify ahci_start_engine()
# 9f5920567bfabbd1be26112a31c44652b6587394
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)

View File

@ -0,0 +1,281 @@
# [PATCH] libata: improve driver initialization and deinitialization
# 0be0aa98985dfec42502c0d0af2a1baff9bdb19f
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);

View File

@ -0,0 +1,203 @@
# [PATCH] ahci: separate out ahci_reset_controller() and ahci_init_controller()
# d91542c11f3981768367815cf087ad36e792ea4a
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);

View File

@ -0,0 +1,126 @@
# [PATCH] ahci: implement Power Management support
# c1332875cbe0c148c7f200d4f9b36b64e34d9872
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;

View File

@ -5,3 +5,10 @@
+ bugfix/mips/cobalt_hz.patch
+ bugfix/mips/real-cpu-cpuinfo.patch
+ bugfix/sky2-receive-FIFO-fix.patch
+ features/all/drivers/scsi-ahci-cleanup-1.patch
+ features/all/drivers/scsi-ahci-cleanup-2.patch
+ features/all/drivers/scsi-ahci-cleanup-3.patch
+ features/all/drivers/scsi-ahci-cleanup-4.patch
+ features/all/drivers/scsi-ahci-suspend-1.patch
+ features/all/drivers/scsi-ahci-suspend-2.patch
+ features/all/drivers/scsi-ahci-suspend-3.patch