[PATCH 2/5] ahci: definition of ahci_port_standby() and ahci_port_spinup()

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

 



Put port power management and staggered-spinup related operations during
the power state transition into ahci_port_standby() and ahci_port_spinup
().


Signed-off-by: Forrest Zhao <forrest.zhao@xxxxxxxxx>
Signed-off-by: Hannes Reinecke <hare@xxxxxxx>
Signed-off-by: Jens Axboe <axboe@xxxxxxx>


---

 drivers/scsi/ahci.c |   98 ++++++++++++++++++++++++++++++++++++++++++++
+++++++
 1 files changed, 98 insertions(+), 0 deletions(-)

9ea9213fa3b37b84727d2ab1d911a84f8e205412
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 5bed7c3..f4386c5 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 */
@@ -218,6 +221,8 @@ static void ahci_freeze(struct ata_port 
 static void ahci_thaw(struct ata_port *ap);
 static void ahci_error_handler(struct ata_port *ap);
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
+static int ahci_port_standby(void __iomem *port_mmio, u32 cap);
+static int ahci_port_spinup(void __iomem *port_mmio, u32 cap);
 static void ahci_remove_one (struct pci_dev *pdev);
 
 static struct scsi_host_template ahci_sht = {
@@ -645,6 +650,99 @@ static void ahci_start_fis_rx(void __iom
        readl(port_mmio + PORT_CMD); /* flush */
 }
 
+static int ahci_port_standby(void __iomem *port_mmio, u32 cap)
+{
+       u32 tmp, scontrol, sstatus;
+
+       tmp = readl(port_mmio + PORT_CMD);
+       /*
+        * AHCI Rev1.1 Section 5.3.2.3:
+        * Software is only allowed to program the PxCMD.FRE,
+        * PxCMD.POD, PxSCTL.DET, and PxCMD.SUD register bits
+        * when PxCMD.ST is set to '0'
+        */
+       if (tmp & PORT_CMD_START)
+               return -EBUSY;
+
+       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
+                */
+               tmp |= PORT_CMD_ICC_SLUMBER;
+               writel(tmp, port_mmio + PORT_CMD);
+               tmp = readl(port_mmio + PORT_CMD);
+
+               /*
+                * Actually, we should wait for the device to
+                * enter slumber mode by checking
+                * sstatus & 0xf00 == 6
+                */
+               sstatus = readl(port_mmio + PORT_SCR_STAT);
+       }
+
+       /*
+        * 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
+                */
+               tmp = readl(port_mmio + PORT_CMD);
+               tmp &= ~PORT_CMD_SPIN_UP;
+               writel(tmp, port_mmio + PORT_CMD);
+               readl(port_mmio + PORT_CMD); /* flush */
+       }
+
+       return 0;
+}
+
+static int ahci_port_spinup(void __iomem *port_mmio, u32 cap)
+{
+       u32 tmp;
+
+       tmp = readl(port_mmio + PORT_CMD);
+       /*
+        * AHCI Rev1.1 Section 5.3.2.3:
+        * Software is only allowed to program the PxCMD.FRE,
+        * PxCMD.POD, PxSCTL.DET, and PxCMD.SUD register bits
+        * when PxCMD.ST is set to '0'
+        */
+       if (tmp & PORT_CMD_START)
+               return -EBUSY;
+
+       /*
+        * Spin up device
+        */
+       if (cap & HOST_CAP_SSS) {
+               tmp |= PORT_CMD_SPIN_UP;
+               writel(tmp, port_mmio + PORT_CMD);
+               tmp = readl(port_mmio + PORT_CMD);
+       }
+
+       if ((tmp & PORT_CMD_ICC_MASK) != PORT_CMD_ICC_ACTIVE) {
+               tmp |= PORT_CMD_ICC_ACTIVE;
+               writel(tmp, port_mmio + PORT_CMD);
+               tmp = readl(port_mmio + PORT_CMD);
+       }
+
+       return 0;
+}
+
 static unsigned int ahci_dev_classify(struct ata_port *ap)
 {
        void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
-- 
1.2.6
-
: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux