[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [linux-2.6.18-xen] This patch adds the power management support to ahci driver.



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1223629130 -3600
# Node ID a4b25d49459d85e8c94c286ab8e10b0b9c62a30d
# Parent  2b5cc22ab4063a1edf1a78348a916b0f116afbda
This patch adds the power management support to ahci driver.
And it is necessary for S3.

It is back-ported from linux kernel mainline tree.
More precisely, the patch is the diff between the commit
c1332875cbe0c148c7f200d4f9b36b64e34d9872 and tag v2.8.18.

The authorship of the overall changes go to:

=============
commit c1332875cbe0c148c7f200d4f9b36b64e34d9872
Author: Tejun Heo <htejun@xxxxxxxxx>
Date:   Wed Jul 26 15:59:26 2006 +0900

    [PATCH] ahci: implement Power Management support

    Implement power management support.

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

    Signed-off-by: Tejun Heo <htejun@xxxxxxxxx>
    Signed-off-by: Zhao, Forrest <forrest.zhao@xxxxxxxxx>
    Signed-off-by: Jeff Garzik <jeff@xxxxxxxxxx>

commit d91542c11f3981768367815cf087ad36e792ea4a
Author: Tejun Heo <htejun@xxxxxxxxx>
Date:   Wed Jul 26 15:59:26 2006 +0900

    [PATCH] ahci: separate out ahci_reset_controller() and
    ahci_init_controller()

    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@xxxxxxxxx>
    Signed-off-by: Zhao, Forrest <forrest.zhao@xxxxxxxxx>
    Signed-off-by: Jeff Garzik <jeff@xxxxxxxxxx>

commit 0be0aa98985dfec42502c0d0af2a1baff9bdb19f
Author: Tejun Heo <htejun@xxxxxxxxx>
Date:   Wed Jul 26 15:59:26 2006 +0900

    [PATCH] libata: improve driver initialization and deinitialization

    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@xxxxxxxxx>

    Signed-off-by: Tejun Heo <htejun@xxxxxxxxx>
    Signed-off-by: Zhao, Forrest <forrest.zhao@xxxxxxxxx>
    Signed-off-by: Jeff Garzik <jeff@xxxxxxxxxx>

commit 9f5920567bfabbd1be26112a31c44652b6587394
Author: Tejun Heo <htejun@xxxxxxxxx>
Date:   Wed Jul 26 15:59:26 2006 +0900

    [PATCH] ahci: simplify ahci_start_engine()

    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@xxxxxxxxx>
    Signed-off-by: Zhao, Forrest <forrest.zhao@xxxxxxxxx>
    Signed-off-by: Jeff Garzik <jeff@xxxxxxxxxx>

commit d8fcd116d203dfe2f6c272d0cd67724b172f1bc2
Author: Tejun Heo <htejun@xxxxxxxxx>
Date:   Wed Jul 26 15:59:25 2006 +0900

    [PATCH] ahci: cosmetic changes to ahci_start/stop_engine()

    * fascist-format comments according to comment style used in
      libata
      core layer.

    * if() -> if ()

    Signed-off-by: Tejun Heo <htejun@xxxxxxxxx>
    Signed-off-by: Zhao, Forrest <forrest.zhao@xxxxxxxxx>
    Signed-off-by: Jeff Garzik <jeff@xxxxxxxxxx>

commit 254950cd56fee220c9d548f3e57211b95976ba64
Author: Tejun Heo <htejun@xxxxxxxxx>
Date:   Wed Jul 26 15:59:25 2006 +0900

    [PATCH] ahci: relocate several internal functions

    * 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@xxxxxxxxx>
    Signed-off-by: Zhao, Forrest <forrest.zhao@xxxxxxxxx>
    Signed-off-by: Jeff Garzik <jeff@xxxxxxxxxx>

commit 5457f2194ad198a0aba4190ec99a6a81846fdca5
Author: zhao, forrest <forrest.zhao@xxxxxxxxx>
Date:   Thu Jul 13 13:38:32 2006 +0800

    [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@xxxxxxxxx>
    Signed-off-by: Hannes Reinecke <hare@xxxxxxx>
    Signed-off-by: Jens Axboe <axboe@xxxxxxx>
    Signed-off-by: Jeff Garzik <jeff@xxxxxxxxxx>
=============

Some of the commits above may not be directly related to the ahci pm
problem, but they lay the basic ground for the final commit.

Signed-off-by: Guanqun Lu <guanqun.lu@xxxxxxxxx>
---
 drivers/scsi/ahci.c |  690 +++++++++++++++++++++++++++++++++-------------------
 1 files changed, 450 insertions(+), 240 deletions(-)

diff -r 2b5cc22ab406 -r a4b25d49459d drivers/scsi/ahci.c
--- a/drivers/scsi/ahci.c       Thu Oct 09 11:10:43 2008 +0100
+++ b/drivers/scsi/ahci.c       Fri Oct 10 09:58:50 2008 +0100
@@ -93,7 +93,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 */
 
@@ -156,6 +158,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 */
@@ -213,6 +216,10 @@ static void ahci_thaw(struct ata_port *a
 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 = {
@@ -232,6 +239,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 = {
@@ -257,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,
@@ -411,6 +423,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,
 };
 
@@ -423,108 +437,6 @@ static inline void __iomem *ahci_port_ba
 static inline void __iomem *ahci_port_base (void __iomem *base, unsigned int 
port)
 {
        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)
@@ -561,41 +473,255 @@ 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)
-{
-       void __iomem *mmio = ap->host_set->mmio_base;
-       void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-       int work;
+static void ahci_start_engine(void __iomem *port_mmio)
+{
        u32 tmp;
 
-       tmp = readl(port_mmio + PORT_CMD);
-       tmp &= ~PORT_CMD_START;
-       writel(tmp, port_mmio + PORT_CMD);
-
-       /* wait for engine to stop.  TODO: 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);
-       }
-
-       return -EIO;
-}
-
-static void ahci_start_engine(struct ata_port *ap)
-{
-       void __iomem *mmio = ap->host_set->mmio_base;
-       void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-       u32 tmp;
-
+       /* start DMA */
        tmp = readl(port_mmio + PORT_CMD);
        tmp |= PORT_CMD_START;
        writel(tmp, port_mmio + PORT_CMD);
        readl(port_mmio + PORT_CMD); /* flush */
+}
+
+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 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 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)
@@ -679,7 +805,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;
@@ -700,7 +826,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;
@@ -759,7 +885,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;
@@ -770,11 +896,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);
@@ -783,7 +911,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);
@@ -993,8 +1121,14 @@ static void ahci_host_intr(struct ata_po
                return;
 
        /* ignore interim PIO setup fis interrupts */
-       if (ata_tag_valid(ap->active_tag) && (status & PORT_IRQ_PIOS_FIS)) 
-               return;
+       if (ata_tag_valid(ap->active_tag)) {
+               struct ata_queued_cmd *qc =
+                       ata_qc_from_tag(ap, ap->active_tag);
+
+               if (qc && qc->tf.protocol == ATA_PROT_PIO &&
+                   (status & PORT_IRQ_PIOS_FIS))
+                       return;
+       }
 
        if (ata_ratelimit())
                ata_port_printk(ap, KERN_INFO, "spurious interrupt "
@@ -1099,10 +1233,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 */
@@ -1113,15 +1250,176 @@ static void ahci_post_internal_cmd(struc
 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);
+       }
+}
+
+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;
+       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;
+
+       /* initialize port */
+       ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
+
+       return 0;
+}
+
+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);
+       const char *emsg = NULL;
+       int rc;
+
+       /* 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,
+                         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,
@@ -1142,47 +1440,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, j, using_dac;
+       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);
@@ -1218,63 +1481,10 @@ static int ahci_host_init(struct ata_pro
                }
        }
 
-       for (i = 0; i < probe_ent->n_ports; i++) {
-#if 0 /* BIOSen initialize this incorrectly */
-               if (!(hpriv->port_map & (1 << i)))
-                       continue;
-#endif
-
-               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 */
-               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++;
-               }
-
-               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 */
-               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);
-       }
-
-       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);
+       for (i = 0; i < probe_ent->n_ports; i++)
+               ahci_setup_port(&probe_ent->port[i], (unsigned long) mmio, i);
+
+       ahci_init_controller(mmio, pdev, probe_ent->n_ports, hpriv->cap);
 
        pci_set_master(pdev);
 

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.