SILI - Work around hardware LRAM bugs part 2/2.
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 17 Jun 2009 19:33:56 +0000 (12:33 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 17 Jun 2009 19:33:56 +0000 (12:33 -0700)
* Add ATA_F_AUTOSENSE and ATA_F_EXCLUSIVE to deal with accessing the
  D2H RFIS.

  ATA_F_AUTOSENSE tells sili.c that the caller wants to access the rfis.
  The sili core is then responsible for copying the rfis out of the LRAM
  and into host-memory.

  ATA_F_EXCLUSIVE tells sili.c that the caller wants to be the only
  command queued to the chip for that particular command.

* Currently ATA_F_AUTOSENSE implies ATA_F_EXCLUSIVE, but ultimately we will
  want to do the auto-sense as a separate step so things like ATAPI farms
  can run commands in parallel.

sys/dev/disk/sili/atascsi.h
sys/dev/disk/sili/sili.c
sys/dev/disk/sili/sili.h
sys/dev/disk/sili/sili_cam.c
sys/dev/disk/sili/sili_pm.c

index ae512ff..02ec3d7 100644 (file)
@@ -320,6 +320,8 @@ struct ata_xfer {
 #define ATA_F_TIMEOUT_RUNNING          (1<<7)
 #define ATA_F_TIMEOUT_DESIRED          (1<<8)
 #define ATA_F_TIMEOUT_EXPIRED          (1<<9)
+#define ATA_F_AUTOSENSE                        (1<<10)
+#define ATA_F_EXCLUSIVE                        (1<<11)
 #define ATA_FMT_FLAGS                  "\020"                          \
                                        "\012EXPIRED"                   \
                                        "\011DESIRED" "\010TRUNNING"    \
index 5860681..2a0ae9c 100644 (file)
@@ -73,8 +73,10 @@ static int sili_core_timeout(struct sili_ccb *ccb);
 void   sili_quick_timeout(struct sili_ccb *ccb);
 void   sili_check_active_timeouts(struct sili_port *ap);
 
+#if 0
 void   sili_beg_exclusive_access(struct sili_port *ap, struct ata_port *at);
 void   sili_end_exclusive_access(struct sili_port *ap, struct ata_port *at);
+#endif
 void   sili_issue_pending_commands(struct sili_port *ap, struct sili_ccb *ccb);
 
 int    sili_port_read_ncq_error(struct sili_port *, int);
@@ -281,7 +283,15 @@ sili_port_alloc(struct sili_softc *sc, u_int port)
                ccb->ccb_xa.fis = &ccb->ccb_prb->prb_h2d;
                prb = bus_space_kva(ap->ap_sc->sc_iot, ap->ap_ioh,
                                    SILI_PREG_LRAM_SLOT(i));
-               ccb->ccb_xa.rfis = &prb->prb_d2h;
+               ccb->ccb_prb_lram = prb;
+               /*
+                * Point our rfis to host-memory instead of the LRAM PRB.
+                * It will be copied back if ATA_F_AUTOSENSE is set.  The
+                * LRAM PRB is buggy.
+                */
+               /*ccb->ccb_xa.rfis = &prb->prb_d2h;*/
+               ccb->ccb_xa.rfis = (void *)ccb->ccb_xa.fis;
+
                ccb->ccb_xa.packetcmd = prb_packet(ccb->ccb_prb);
                ccb->ccb_xa.tag = i;
 
@@ -779,7 +789,9 @@ sili_port_state_machine(struct sili_port *ap, int initial)
                         */
                        if (at->at_probe != ATA_PROBE_FAILED &&
                            at->at_probe != ATA_PROBE_GOOD) {
+#if 0
                                sili_beg_exclusive_access(ap, at);
+#endif
                                if (at->at_probe == ATA_PROBE_NEED_INIT)
                                        sili_port_init(ap, at);
                                if (at->at_probe == ATA_PROBE_NEED_HARD_RESET)
@@ -788,7 +800,9 @@ sili_port_state_machine(struct sili_port *ap, int initial)
                                        sili_port_reset(ap, at, 0);
                                if (at->at_probe == ATA_PROBE_NEED_IDENT)
                                        sili_cam_probe(ap, at);
+#if 0
                                sili_end_exclusive_access(ap, at);
+#endif
                        }
 
                        /*
@@ -933,7 +947,7 @@ sili_port_softreset(struct sili_port *ap)
         */
        ccb = sili_get_err_ccb(ap);
        ccb->ccb_done = sili_empty_done;
-       ccb->ccb_xa.flags = ATA_F_POLL;
+       ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_AUTOSENSE | ATA_F_EXCLUSIVE;
        ccb->ccb_xa.complete = sili_dummy_done;
        ccb->ccb_xa.at = NULL;
 
@@ -1498,11 +1512,13 @@ sili_poll(struct sili_ccb *ccb, int timeout,
                case ATA_S_PENDING:
                        /*
                         * The packet can get stuck on the pending queue
-                        * if the port refuses to come ready.
+                        * if the port refuses to come ready.  XXX
                         */
-                       if (AP_F_EXCLUSIVE_ACCESS)
+#if 0
+                       if (xxx AP_F_EXCLUSIVE_ACCESS)
                                timeout -= sili_os_softsleep();
                        else
+#endif
                                sili_os_softsleep();
                        sili_check_active_timeouts(ap);
                        break;
@@ -1590,6 +1606,7 @@ sili_start(struct sili_ccb *ccb)
        sili_issue_pending_commands(ap, ccb);
 }
 
+#if 0
 /*
  * While holding the port lock acquire exclusive access to the port.
  *
@@ -1616,6 +1633,7 @@ sili_end_exclusive_access(struct sili_port *ap, struct ata_port *at)
        ap->ap_flags &= ~AP_F_EXCLUSIVE_ACCESS;
        sili_issue_pending_commands(ap, NULL);
 }
+#endif
 
 /*
  * If ccb is not NULL enqueue and/or issue it.
@@ -1642,7 +1660,7 @@ sili_issue_pending_commands(struct sili_port *ap, struct sili_ccb *ccb)
         */
        if (ccb) {
                TAILQ_INSERT_TAIL(&ap->ap_ccb_pending, ccb, ccb_entry);
-       } else if ((ap->ap_flags & AP_F_EXCLUSIVE_ACCESS) || ap->ap_expired) {
+       } else if (ap->ap_expired) {
                return;
        }
 
@@ -1654,6 +1672,9 @@ sili_issue_pending_commands(struct sili_port *ap, struct sili_ccb *ccb)
         * XXX limit ncqdepth for attached devices behind PM
         */
        while ((ccb = TAILQ_FIRST(&ap->ap_ccb_pending)) != NULL) {
+               /*
+                * Port may be wedged.
+                */
                if ((sili_pread(ap, SILI_PREG_STATUS) &
                    SILI_PREG_STATUS_READY) == 0) {
                        kprintf("%s: slot %d NOT READY\n",
@@ -1662,10 +1683,52 @@ sili_issue_pending_commands(struct sili_port *ap, struct sili_ccb *ccb)
                                    SILI_PREG_INT_READY);
                        break;
                }
+
+               /*
+                * Handle exclusivity requirements.  ATA_F_EXCLUSIVE is used
+                * when we may have to access the rfis which is stored in
+                * the LRAM PRB.  Unfortunately reading the LRAM PRB is
+                * highly problematic, so requests (like PM requests) which
+                * need to access the rfis use exclusive mode and then
+                * access the copy made by the port interrupt code back in
+                * host memory.
+                */
+               if (ap->ap_active & ~ap->ap_expired) {
+                       /*
+                        * There may be multiple ccb's already running,
+                        * but there will only be one if it is exclusive.
+                        * We can't queue a new command in that case.
+                        *
+                        * XXX Current AUTOSENSE code forces exclusivity
+                        *     to simplify the code.
+                        */
+                       KKASSERT(ap->ap_last_ccb);
+                       KKASSERT(ap->ap_active &
+                                (1 << ap->ap_last_ccb->ccb_slot));
+                       if (ap->ap_last_ccb->ccb_xa.flags &
+                           (ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE)) {
+                               break;
+                       }
+
+                       /*
+                        * If the ccb we want to run is exclusive and ccb's
+                        * are still active on the port, we can't queue it
+                        * yet.
+                        *
+                        * XXX Current AUTOSENSE code forces exclusivity
+                        *     to simplify the code.
+                        */
+                       if (ccb->ccb_xa.flags &
+                           (ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE)) {
+                               break;
+                       }
+               }
+
                TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
                ccb->ccb_xa.state = ATA_S_ONCHIP;
                ap->ap_active |= 1 << ccb->ccb_slot;
                ap->ap_active_cnt++;
+               ap->ap_last_ccb = ccb;
 
                /*
                 * We can't use the CMD_FIFO method because it requires us
@@ -2041,6 +2104,13 @@ fatal:
                 * Complete the ccb.  If the ccb was marked expired it
                 * may or may not have been cleared from the port,
                 * make sure we mark it as having timed out.
+                *
+                * In a normal completion if AUTOSENSE is set we copy
+                * the PRB LRAM rfis back to the rfis in host-memory.
+                *
+                * XXX Currently AUTOSENSE also forces exclusivity so we
+                *     can safely work around a hardware bug when reading
+                *     the LRAM.
                 */
                if (ap->ap_expired & (1 << ccb->ccb_slot)) {
                        ap->ap_expired &= ~(1 << ccb->ccb_slot);
@@ -2048,6 +2118,11 @@ fatal:
                        ccb->ccb_done(ccb);
                        ccb->ccb_xa.complete(&ccb->ccb_xa);
                } else {
+                       if (ccb->ccb_xa.flags & ATA_F_AUTOSENSE) {
+                               memcpy(ccb->ccb_xa.rfis,
+                                      &ccb->ccb_prb_lram->prb_d2h,
+                                      sizeof(ccb->ccb_prb_lram->prb_d2h));
+                       }
                        if (ccb->ccb_xa.state == ATA_S_ONCHIP)
                                ccb->ccb_xa.state = ATA_S_COMPLETE;
                        ccb->ccb_done(ccb);
index 85d0211..63c994a 100644 (file)
@@ -729,6 +729,7 @@ struct sili_ccb {
 
        bus_dmamap_t            ccb_dmamap;
        struct sili_prb         *ccb_prb;
+       struct sili_prb         *ccb_prb_lram;
        u_int64_t               ccb_prb_paddr;  /* phys addr of prb */
 
        void                    (*ccb_done)(struct sili_ccb *);
@@ -750,7 +751,7 @@ struct sili_port {
 #define AP_F_SCAN_REQUESTED    0x0010
 #define AP_F_SCAN_COMPLETED    0x0020
 #define AP_F_IGNORE_IFS                0x0040
-#define AP_F_EXCLUSIVE_ACCESS  0x0200
+#define AP_F_UNUSED0200                0x0200
 #define AP_F_ERR_CCB_RESERVED  0x0400
        int                     ap_signal;      /* os per-port thread sig */
        thread_t                ap_thread;      /* os per-port thread */
@@ -770,6 +771,7 @@ struct sili_port {
        u_int32_t               ap_expired;     /* deferred expired bmask */
        struct sili_ccb         *ap_ccbs;
        struct sili_ccb         *ap_err_ccb;    /* used to read LOG page  */
+       struct sili_ccb         *ap_last_ccb;   /* used to check excl mode*/
 
        TAILQ_HEAD(, sili_ccb)  ap_ccb_free;
        TAILQ_HEAD(, sili_ccb)  ap_ccb_pending;
index 180e562..519f7d3 100644 (file)
@@ -310,6 +310,7 @@ sili_cam_probe(struct sili_port *ap, struct ata_port *atx)
        xa->complete = sili_ata_dummy_done;
        xa->data = &at->at_identify;
        xa->datalen = sizeof(at->at_identify);
+       xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
        xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
 
        switch(at->at_type) {
@@ -319,6 +320,7 @@ sili_cam_probe(struct sili_port *ap, struct ata_port *atx)
                break;
        case ATA_PORT_T_ATAPI:
                xa->fis->command = ATA_C_ATAPI_IDENTIFY;
+               xa->flags |= ATA_F_AUTOSENSE;
                type = "ATAPI";
                break;
        default:
@@ -328,7 +330,6 @@ sili_cam_probe(struct sili_port *ap, struct ata_port *atx)
        }
        xa->fis->features = 0;
        xa->fis->device = 0;
-       xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
        xa->timeout = 1000;
 
        if (sili_ata_cmd(xa) != ATA_S_COMPLETE) {
@@ -1263,6 +1264,13 @@ sili_xpt_scsi_atapi_io(struct sili_port *ap, struct ata_port *atx,
        }
 
        /*
+        * Special handling to get the rfis back into host memory while
+        * still allowing the Sili chip to run commands in parallel to
+        * ATAPI devices behind a PM.
+        */
+       flags |= ATA_F_AUTOSENSE;
+
+       /*
         * The command has to fit in the packet command buffer.
         */
        if (csio->cdb_len < 6 || csio->cdb_len > 16) {
index da3868a..3fe5c6f 100644 (file)
@@ -272,7 +272,7 @@ sili_pm_softreset(struct sili_port *ap, int target)
         */
        ccb = sili_get_err_ccb(ap);
        ccb->ccb_done = sili_pm_empty_done;
-       ccb->ccb_xa.flags = ATA_F_POLL;
+       ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE;
        ccb->ccb_xa.complete = sili_pm_dummy_done;
        ccb->ccb_xa.at = at;
 
@@ -392,7 +392,7 @@ sili_pm_set_feature(struct sili_port *ap, int feature, int enable)
 
        xa->complete = sili_pm_dummy_done;
        xa->datalen = 0;
-       xa->flags = ATA_F_READ | ATA_F_POLL;
+       xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE;
        xa->timeout = 1000;
 
        if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
@@ -486,7 +486,7 @@ sili_pm_read(struct sili_port *ap, int target, int which, u_int32_t *datap)
 
        xa->complete = sili_pm_dummy_done;
        xa->datalen = 0;
-       xa->flags = ATA_F_READ | ATA_F_POLL;
+       xa->flags = ATA_F_POLL | ATA_F_AUTOSENSE;
        xa->timeout = 1000;
 
        if (sili_ata_cmd(xa) == ATA_S_COMPLETE) {
@@ -527,7 +527,7 @@ sili_pm_write(struct sili_port *ap, int target, int which, u_int32_t data)
 
        xa->complete = sili_pm_dummy_done;
        xa->datalen = 0;
-       xa->flags = ATA_F_READ | ATA_F_POLL;
+       xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE;
        xa->timeout = 1000;
 
        if (sili_ata_cmd(xa) == ATA_S_COMPLETE)