WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-changelog

[Xen-changelog] [linux-2.6.18-xen] This patch adds the power management

To: xen-changelog@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-changelog] [linux-2.6.18-xen] This patch adds the power management support to ahci driver.
From: "Xen patchbot-linux-2.6.18-xen" <patchbot-linux-2.6.18-xen@xxxxxxxxxxxxxxxxxxx>
Date: Fri, 10 Oct 2008 10:10:08 -0700
Delivery-date: Fri, 10 Oct 2008 10:10:06 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-changelog-request@lists.xensource.com?subject=help>
List-id: BK change log <xen-changelog.lists.xensource.com>
List-post: <mailto:xen-changelog@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=unsubscribe>
Reply-to: xen-devel@xxxxxxxxxxxxxxxxxxx
Sender: xen-changelog-bounces@xxxxxxxxxxxxxxxxxxx
# 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

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-changelog] [linux-2.6.18-xen] This patch adds the power management support to ahci driver., Xen patchbot-linux-2.6.18-xen <=