AHCI - Bug fixes - primary port probe and TFD flushing.
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 14 Jun 2009 06:07:00 +0000 (23:07 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 14 Jun 2009 06:07:00 +0000 (23:07 -0700)
* The primary port probe was skipping the software reset step.

* ahci_flush_tfd() was looping on the SERR.DIAG.X bit with a 10ms delay,
  but on some parts this bit gets re-set very quickly if the port is
  in a COMRESET state.  Remove the loop, just use an if() instead.

Reported-by: Sepherosa Ziehau
sys/dev/disk/ahci/ahci.c
sys/dev/disk/ahci/ahci_cam.c

index b565d4f..2e6a65a 100644 (file)
@@ -584,7 +584,7 @@ ahci_port_state_machine(struct ahci_port *ap, int initial)
         * during the initial parallel probe and the port's probe state
         * will not get past ATA_PROBE_NEED_IDENT.
         */
-       if (ap->ap_type == ATA_PORT_T_NONE) {
+       {
                if (initial == 0 && ap->ap_probe <= ATA_PROBE_NEED_HARD_RESET) {
                        kprintf("%s: Waiting 10 seconds on insertion\n",
                                PORTNAME(ap));
@@ -1314,6 +1314,8 @@ ahci_port_hardreset(struct ahci_port *ap, int hard)
                error = ENODEV;
        }
 
+       ahci_flush_tfd(ap);
+
        /*
         * Wait for the device to become ready.
         *
@@ -1713,11 +1715,9 @@ ahci_port_hardstop(struct ahci_port *ap)
 }
 
 /*
- * Multiple events may have built up in the TFD.  The spec is not very
- * clear on this but it does seem to serialize events so clearing DIAG_X
- * just once might not do the job during a reset sequence.
- *
- * XXX this probably isn't right.
+ * We can't loop on the X bit, a continuous COMINIT received will make
+ * it loop forever.  Just assume one event has built up and clear X
+ * so the task file descriptor can update.
  */
 void
 ahci_flush_tfd(struct ahci_port *ap)
@@ -1725,11 +1725,8 @@ ahci_flush_tfd(struct ahci_port *ap)
        u_int32_t r;
 
        r = ahci_pread(ap, AHCI_PREG_SERR);
-       while (r & AHCI_PREG_SERR_DIAG_X) {
+       if (r & AHCI_PREG_SERR_DIAG_X)
                ahci_pwrite(ap, AHCI_PREG_SERR, AHCI_PREG_SERR_DIAG_X);
-               ahci_os_sleep(1);
-               r = ahci_pread(ap, AHCI_PREG_SERR);
-       }
 }
 
 /*
index a61b57e..82301e4 100644 (file)
@@ -151,7 +151,10 @@ ahci_cam_attach(struct ahci_port *ap)
        }
        ap->ap_flags |= AP_F_BUS_REGISTERED;
 
-       error = ahci_cam_probe(ap, NULL);
+       if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
+               error = ahci_cam_probe(ap, NULL);
+       else
+               error = 0;
        if (error) {
                ahci_cam_detach(ap);
                return (EIO);