]> Zhao Yanbai Git Server - minix.git/commitdiff
ahci: more robustness against flaky devices
authorDavid van Moolenbroek <david@minix3.org>
Wed, 13 Feb 2013 14:29:44 +0000 (14:29 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Wed, 13 Feb 2013 14:29:44 +0000 (14:29 +0000)
- if supported, override BSY/DRQ flags and start the port anyway if it
  times out with DET=3h status during device detection;
- no longer rely on the device signature to be set; unless a valid
  signature is obtained, try both ATA and ATAPI device identification.

drivers/ahci/ahci.c
drivers/ahci/ahci.h

index cf2b628e14006bc58c832b26bee5e973e60d8657..033a76423ee635d4f04a57057c8e2d26069694db 100644 (file)
@@ -127,6 +127,7 @@ static struct {
        int nr_ports;           /* addressable number of ports (1..NR_PORTS) */
        int nr_cmds;            /* maximum number of commands per port */
        int has_ncq;            /* NCQ support flag */
+       int has_clo;            /* CLO support flag */
 
        int irq;                /* IRQ number */
        int hook_id;            /* IRQ hook ID */
@@ -1232,6 +1233,25 @@ static void port_hardreset(struct port_state *ps)
        port_write(ps, AHCI_PORT_SCTL, AHCI_PORT_SCTL_DET_NONE);
 }
 
+/*===========================================================================*
+ *                             port_override                                *
+ *===========================================================================*/
+static void port_override(struct port_state *ps)
+{
+       /* Override the port's BSY and/or DRQ flags. This may only be done
+        * prior to starting the port.
+        */
+       u32_t cmd;
+
+       cmd = port_read(ps, AHCI_PORT_CMD);
+       port_write(ps, AHCI_PORT_CMD, cmd | AHCI_PORT_CMD_CLO);
+
+       SPIN_UNTIL(!(port_read(ps, AHCI_PORT_CMD) & AHCI_PORT_CMD_CLO),
+               PORTREG_DELAY);
+
+       dprintf(V_INFO, ("%s: overridden\n", ahci_portname(ps)));
+}
+
 /*===========================================================================*
  *                             port_start                                   *
  *===========================================================================*/
@@ -1350,9 +1370,21 @@ static void port_id_check(struct port_state *ps, int success)
        ps->flags &= ~FLAG_BUSY;
        cancel_timer(&ps->cmd_info[0].timer);
 
-       if (!success)
+       if (!success) {
+               if (!(ps->flags & FLAG_ATAPI) &&
+                               port_read(ps, AHCI_PORT_SIG) != ATA_SIG_ATA) {
+                       dprintf(V_INFO, ("%s: may not be ATA, trying ATAPI\n",
+                               ahci_portname(ps)));
+
+                       ps->flags |= FLAG_ATAPI;
+
+                       (void) gen_identify(ps, FALSE /*blocking*/);
+                       return;
+               }
+
                dprintf(V_ERR,
                        ("%s: unable to identify\n", ahci_portname(ps)));
+       }
 
        /* If the identify command itself succeeded, check the results and
         * store some properties.
@@ -1436,24 +1468,6 @@ static void port_connect(struct port_state *ps)
                return;
        }
 
-       /* Check the port's signature. We only support the normal ATA and ATAPI
-        * signatures. We ignore devices reporting anything else.
-        */
-       sig = port_read(ps, AHCI_PORT_SIG);
-
-       if (sig != ATA_SIG_ATA && sig != ATA_SIG_ATAPI) {
-               dprintf(V_ERR, ("%s: unsupported signature (%08x)\n",
-                       ahci_portname(ps), sig));
-
-               port_stop(ps);
-
-               ps->state = STATE_BAD_DEV;
-               port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PRCE);
-               ps->flags &= ~FLAG_BUSY;
-
-               return;
-       }
-
        /* Clear all state flags except the busy flag, which may be relevant if
         * a BDEV_OPEN call is waiting for the device to become ready; the
         * barrier flag, which prevents access to the device until it is
@@ -1461,6 +1475,12 @@ static void port_connect(struct port_state *ps)
         */
        ps->flags &= (FLAG_BUSY | FLAG_BARRIER | FLAG_SUSPENDED);
 
+       /* Check the port's signature. We only use the signature to speed up
+        * identification; we will try both ATA and ATAPI if the signature is
+        * neither ATA nor ATAPI.
+        */
+       sig = port_read(ps, AHCI_PORT_SIG);
+
        if (sig == ATA_SIG_ATAPI)
                ps->flags |= FLAG_ATAPI;
 
@@ -1548,6 +1568,18 @@ static void port_dev_check(struct port_state *ps)
         * device present at all. In all cases, we change to another state.
         */
        if (status == AHCI_PORT_SSTS_DET_PHY) {
+               /* Some devices may not correctly clear BSY/DRQ. Upon timeout,
+                * if we can override these flags, do so and start the
+                * identification process anyway.
+                */
+               if (hba_state.has_clo) {
+                       port_override(ps);
+
+                       port_connect(ps);
+
+                       return;
+               }
+
                /* A device is present and initialized, but not ready. */
                ps->state = STATE_BAD_DEV;
                port_write(ps, AHCI_PORT_IE, AHCI_PORT_IE_PRCE);
@@ -1569,6 +1601,7 @@ static void port_intr(struct port_state *ps)
        /* Process an interrupt on this port.
         */
        u32_t smask, emask;
+       int success;
 
        if (ps->state == STATE_NO_PORT) {
                dprintf(V_ERR, ("%s: interrupt for invalid port!\n",
@@ -1660,12 +1693,8 @@ static void port_intr(struct port_state *ps)
                 * later by obtaining per-command status results from the HBA.
                 */
 
-               /* If we were waiting for ID verification, check now. */
-               if (ps->state == STATE_WAIT_ID) {
-                       port_id_check(ps, !(port_read(ps, AHCI_PORT_TFD) &
-                               (AHCI_PORT_TFD_STS_ERR |
-                               AHCI_PORT_TFD_STS_DF)));
-               }
+               success = !(port_read(ps, AHCI_PORT_TFD) &
+                       (AHCI_PORT_TFD_STS_ERR | AHCI_PORT_TFD_STS_DF));
 
                /* Check now for failure. There are fatal failures, and there
                 * are failures that set the TFD.STS.ERR field using a D2H
@@ -1677,6 +1706,10 @@ static void port_intr(struct port_state *ps)
                        (smask & AHCI_PORT_IS_RESTART)) {
                                port_restart(ps);
                }
+
+               /* If we were waiting for ID verification, check now. */
+               if (ps->state == STATE_WAIT_ID)
+                       port_id_check(ps, success);
        }
 }
 
@@ -2087,6 +2120,7 @@ static void ahci_init(int devind)
        /* Note that we currently use only one command anyway. */
        cap = hba_read(AHCI_HBA_CAP);
        hba_state.has_ncq = !!(cap & AHCI_HBA_CAP_SNCQ);
+       hba_state.has_clo = !!(cap & AHCI_HBA_CAP_SCLO);
        hba_state.nr_cmds = MIN(NR_CMDS,
                ((cap >> AHCI_HBA_CAP_NCS_SHIFT) & AHCI_HBA_CAP_NCS_MASK) + 1);
 
index b1ccd664dedaa5c3080df5604476766adb5ad00b..5b77d9e63e2144577f21b0595bc9d0a509f8d685 100644 (file)
 #define        AHCI_PORT_CMD_CR        (1L << 15)      /* Cmd List Running */
 #define        AHCI_PORT_CMD_FR        (1L << 14)      /* FIS Recv Running */
 #define        AHCI_PORT_CMD_FRE       (1L <<  4)      /* FIS Recv Enabled */
+#define        AHCI_PORT_CMD_CLO       (1L <<  3)      /* Cmd List Override */
 #define        AHCI_PORT_CMD_SUD       (1L <<  1)      /* Spin-Up Device */
 #define        AHCI_PORT_CMD_ST        (1L <<  0)      /* Start */
 #define AHCI_PORT_TFD  8               /* Task File Data */