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

[cdi-devel] [PATCH] ahci: Initialisation/Reset improvements



Surprisingly, real hardware works much better when the disk gets power
and is spun up. Also disable any automatic transition to power
management modes just to be on the safe side.

Before resetting a port, the processing of the command list must be
stopped.

Signed-off-by: Kevin Wolf <kevin@xxxxxxxxxx>
---
 ahci/ahci.h | 10 +++++++++-
 ahci/main.c | 32 +++++++++++++++++++++++++++++---
 2 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/ahci/ahci.h b/ahci/ahci.h
index 24a830b..2515f5a 100644
--- a/ahci/ahci.h
+++ b/ahci/ahci.h
@@ -94,9 +94,14 @@ enum {
 
 enum {
     PxCMD_ST    = (1 <<  0), /* Start */
+    PxCMD_SUD   = (1 <<  1), /* Spin-Up Device */
+    PxCMD_POD   = (1 <<  2), /* Power On Device */
     PxCMD_FRE   = (1 <<  4), /* FIS Receive Enable */
     PxCMD_FR    = (1 << 14), /* FIS Receive Running */
     PxCMD_CR    = (1 << 15), /* Command List Running */
+
+    PxCMD_ICC_MASK      = (0xf << 28),
+    PxCMD_ICC_ACTIVE    = (0x1 << 28),
 };
 
 enum {
@@ -114,7 +119,10 @@ enum {
 };
 
 enum {
-    PxSCTL_DET_MASK     = (0xf << 0),
+    PxSCTL_DET_MASK     = (0xf << 0),   /* Device Detection Initialization */
+    PxSCTL_IPM_MASK     = (0xf << 8),   /* Interface Power Management
+                                           Transitions Allowed */
+    PxSCTL_IPM_NONE     = (0x3 << 8),
 };
 
 /* SATA 2.6: "11.3 Software reset protocol" */
diff --git a/ahci/main.c b/ahci/main.c
index 1afc03c..714a96f 100644
--- a/ahci/main.c
+++ b/ahci/main.c
@@ -46,7 +46,7 @@ static int ahci_init_hardware(struct ahci_device* ahci)
 {
     int port;
     bool all_idle;
-    uint32_t cmd, ssts, sig;
+    uint32_t cmd, ssts, sig, sctl;
     int retries;
 
     /* HBA reset (see AHCI 1.3, chapter 10.4.3) */
@@ -95,15 +95,35 @@ static int ahci_init_hardware(struct ahci_device* ahci)
             continue;
         }
 
-        /* If there is a device attached, register it */
-        ssts = pxreg_inl(ahci, port, REG_PxSSTS);
+        /* Power On Device, Spin-Up Device, Link Active */
+        cmd = pxreg_inl(ahci, port, REG_PxCMD);
+        cmd &= ~PxCMD_ICC_MASK;
+        cmd |=  PxCMD_POD | PxCMD_SUD | PxCMD_ICC_ACTIVE;
+        pxreg_outl(ahci, port, REG_PxCMD, cmd);
+
+        retries = 10;
+        do {
+            ssts = pxreg_inl(ahci, port, REG_PxSSTS);
+            if ((ssts & PxSSTS_DET_MASK) == PxSSTS_DET_PRESENT) {
+                break;
+            } else {
+                cdi_sleep_ms(100);
+            }
+        } while (--retries);
 
+        /* If there is a device attached, register it */
         if ((ssts & PxSSTS_DET_MASK) != PxSSTS_DET_PRESENT ||
             (ssts & PxSSTS_IPM_MASK) != PxSSTS_IPM_ACTIVE)
         {
             continue;
         }
 
+        /* Forbid transition to any power mangement states */
+        sctl = pxreg_inl(ahci, port, REG_PxSCTL);
+        sctl &= ~PxSCTL_IPM_MASK;
+        sctl |= PxSCTL_IPM_NONE;
+        pxreg_outl(ahci, port, REG_PxSCTL, sctl);
+
         /* Create a device with the right driver */
         sig = pxreg_inl(ahci, port, REG_PxSIG);
 
@@ -145,6 +165,10 @@ static int ahci_init_hardware(struct ahci_device* ahci)
 void ahci_port_comreset(struct ahci_device* ahci, int port)
 {
     uint32_t sctl = pxreg_inl(ahci, port, REG_PxSCTL);
+    uint32_t cmd = pxreg_inl(ahci, port, REG_PxCMD);
+
+    pxreg_outl(ahci, port, REG_PxCMD, cmd & ~PxCMD_ST);
+    while (pxreg_inl(ahci, port, REG_PxCMD) & PxCMD_CR);
 
     sctl &= ~PxSCTL_DET_MASK;
     sctl |= 0x1;
@@ -154,6 +178,8 @@ void ahci_port_comreset(struct ahci_device* ahci, int port)
 
     sctl &= ~PxSCTL_DET_MASK;
     pxreg_outl(ahci, port, REG_PxSCTL, sctl);
+
+    pxreg_outl(ahci, port, REG_PxCMD, cmd);
 }
 
 static void irq_handler(struct cdi_device* dev)
-- 
2.1.4