Author: mav
Date: Fri Apr 15 16:40:31 2011
New Revision: 220657
URL: http://svn.freebsd.org/changeset/base/220657

Log:
  Some changes around hot-plug and interface power-management:
   - use ATA_SE_EXCHANGED (SError.DIAG.X) bit to detect hot-plug events when
  power-management enabled and ATA_SE_PHY_CHANGED (SError.DIAG.N) can't be
  trusted;
   - on controllers supporting staggered spin-up (SS) put unused channels
  into Listen state instead of Off. It should still save some power, but
  allow plug-in events to be detected;
   - on controllers supporting cold presence detection (CPD), when power
  management enabled, use CPD events to detect hot-plug in addition to PHY
  events.

Modified:
  head/sys/dev/ahci/ahci.c
  head/sys/dev/ahci/ahci.h

Modified: head/sys/dev/ahci/ahci.c
==============================================================================
--- head/sys/dev/ahci/ahci.c    Fri Apr 15 15:33:24 2011        (r220656)
+++ head/sys/dev/ahci/ahci.c    Fri Apr 15 16:40:31 2011        (r220657)
@@ -1272,34 +1272,66 @@ ahci_slotsfree(device_t dev)
        }
 }
 
-static void
+static int
 ahci_phy_check_events(device_t dev, u_int32_t serr)
 {
        struct ahci_channel *ch = device_get_softc(dev);
 
-       if ((serr & ATA_SE_PHY_CHANGED) && (ch->pm_level == 0)) {
+       if (((ch->pm_level == 0) && (serr & ATA_SE_PHY_CHANGED)) ||
+           ((ch->pm_level != 0 || ch->listening) && (serr & 
ATA_SE_EXCHANGED))) {
                u_int32_t status = ATA_INL(ch->r_mem, AHCI_P_SSTS);
                union ccb *ccb;
 
                if (bootverbose) {
-                       if (((status & ATA_SS_DET_MASK) == 
ATA_SS_DET_PHY_ONLINE) &&
-                           ((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) 
&&
-                           ((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) {
+                       if ((status & ATA_SS_DET_MASK) != ATA_SS_DET_NO_DEVICE)
                                device_printf(dev, "CONNECT requested\n");
-                       } else
+                       else
                                device_printf(dev, "DISCONNECT requested\n");
                }
                ahci_reset(dev);
                if ((ccb = xpt_alloc_ccb_nowait()) == NULL)
-                       return;
+                       return (0);
                if (xpt_create_path(&ccb->ccb_h.path, NULL,
                    cam_sim_path(ch->sim),
                    CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
                        xpt_free_ccb(ccb);
-                       return;
+                       return (0);
                }
                xpt_rescan(ccb);
+               return (1);
        }
+       return (0);
+}
+
+static void
+ahci_cpd_check_events(device_t dev)
+{
+       struct ahci_channel *ch = device_get_softc(dev);
+       u_int32_t status;
+       union ccb *ccb;
+
+       if (ch->pm_level == 0)
+               return;
+
+       status = ATA_INL(ch->r_mem, AHCI_P_CMD);
+       if ((status & AHCI_P_CMD_CPD) == 0)
+               return;
+
+       if (bootverbose) {
+               if (status & AHCI_P_CMD_CPS) {
+                       device_printf(dev, "COLD CONNECT requested\n");
+               } else
+                       device_printf(dev, "COLD DISCONNECT requested\n");
+       }
+       ahci_reset(dev);
+       if ((ccb = xpt_alloc_ccb_nowait()) == NULL)
+               return;
+       if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim),
+           CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+               xpt_free_ccb(ccb);
+               return;
+       }
+       xpt_rescan(ccb);
 }
 
 static void
@@ -1359,7 +1391,7 @@ ahci_ch_intr(void *data)
        struct ahci_channel *ch = device_get_softc(dev);
        uint32_t istatus, sstatus, cstatus, serr = 0, sntf = 0, ok, err;
        enum ahci_err_type et;
-       int i, ccs, port;
+       int i, ccs, port, reset = 0;
 
        /* Read and clear interrupt statuses. */
        istatus = ATA_INL(ch->r_mem, AHCI_P_IS);
@@ -1395,9 +1427,12 @@ ahci_ch_intr(void *data)
                serr = ATA_INL(ch->r_mem, AHCI_P_SERR);
                if (serr) {
                        ATA_OUTL(ch->r_mem, AHCI_P_SERR, serr);
-                       ahci_phy_check_events(dev, serr);
+                       reset = ahci_phy_check_events(dev, serr);
                }
        }
+       /* Process cold presence detection events */
+       if ((istatus & AHCI_P_IX_CPD) && !reset)
+               ahci_cpd_check_events(dev);
        /* Process command errors */
        if (istatus & (AHCI_P_IX_OF | AHCI_P_IX_IF |
            AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) {
@@ -2446,7 +2481,8 @@ ahci_reset(device_t dev)
                ch->devices = 0;
                /* Enable wanted port interrupts */
                ATA_OUTL(ch->r_mem, AHCI_P_IE,
-                   (AHCI_P_IX_CPD | AHCI_P_IX_PRC | AHCI_P_IX_PC));
+                   (((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) |
+                    AHCI_P_IX_PRC | AHCI_P_IX_PC));
                xpt_release_simq(ch->sim, TRUE);
                return;
        }
@@ -2463,9 +2499,10 @@ ahci_reset(device_t dev)
        ch->devices = 1;
        /* Enable wanted port interrupts */
        ATA_OUTL(ch->r_mem, AHCI_P_IE,
-            (AHCI_P_IX_CPD | AHCI_P_IX_TFE | AHCI_P_IX_HBF |
+            (((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) |
+             AHCI_P_IX_TFE | AHCI_P_IX_HBF |
              AHCI_P_IX_HBD | AHCI_P_IX_IF | AHCI_P_IX_OF |
-             ((ch->pm_level == 0) ? AHCI_P_IX_PRC | AHCI_P_IX_PC : 0) |
+             ((ch->pm_level == 0) ? AHCI_P_IX_PRC : 0) | AHCI_P_IX_PC |
              AHCI_P_IX_DP | AHCI_P_IX_UF | (ctlr->ccc ? 0 : AHCI_P_IX_SDB) |
              AHCI_P_IX_DS | AHCI_P_IX_PS | (ctlr->ccc ? 0 : AHCI_P_IX_DHR)));
        if (ch->resetting)
@@ -2570,6 +2607,12 @@ ahci_sata_phy_reset(device_t dev)
        int sata_rev;
        uint32_t val;
 
+       if (ch->listening) {
+               val = ATA_INL(ch->r_mem, AHCI_P_CMD);
+               val |= AHCI_P_CMD_SUD;
+               ATA_OUTL(ch->r_mem, AHCI_P_CMD, val);
+               ch->listening = 0;
+       }
        sata_rev = ch->user[ch->pm_present ? 15 : 0].revision;
        if (sata_rev == 1)
                val = ATA_SC_SPD_SPEED_GEN1;
@@ -2588,7 +2631,12 @@ ahci_sata_phy_reset(device_t dev)
            (ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER)));
        DELAY(5000);
        if (!ahci_sata_connect(ch)) {
-               if (ch->pm_level > 0)
+               if (ch->caps & AHCI_CAP_SSS) {
+                       val = ATA_INL(ch->r_mem, AHCI_P_CMD);
+                       val &= ~AHCI_P_CMD_SUD;
+                       ATA_OUTL(ch->r_mem, AHCI_P_CMD, val);
+                       ch->listening = 1;
+               } else if (ch->pm_level > 0)
                        ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE);
                return (0);
        }

Modified: head/sys/dev/ahci/ahci.h
==============================================================================
--- head/sys/dev/ahci/ahci.h    Fri Apr 15 15:33:24 2011        (r220656)
+++ head/sys/dev/ahci/ahci.h    Fri Apr 15 16:40:31 2011        (r220657)
@@ -118,6 +118,7 @@
 #define         ATA_SE_LINKSEQ_ERR      0x00800000
 #define         ATA_SE_TRANSPORT_ERR    0x01000000
 #define         ATA_SE_UNKNOWN_FIS      0x02000000
+#define         ATA_SE_EXCHANGED        0x04000000
 
 #define ATA_SCONTROL                    15
 #define         ATA_SC_DET_MASK         0x0000000f
@@ -221,7 +222,7 @@
 #define         AHCI_P_IX_UF        0x00000010
 #define         AHCI_P_IX_DP        0x00000020
 #define         AHCI_P_IX_PC        0x00000040
-#define         AHCI_P_IX_DI        0x00000080
+#define         AHCI_P_IX_MP        0x00000080
 
 #define         AHCI_P_IX_PRC       0x00400000
 #define         AHCI_P_IX_IPM       0x00800000
@@ -413,6 +414,7 @@ struct ahci_channel {
        int                     lastslot;       /* Last used slot */
        int                     taggedtarget;   /* Last tagged target */
        int                     resetting;      /* Hard-reset in progress. */
+       int                     listening;      /* SUD bit is cleared. */
        union ccb               *frozen;        /* Frozen command */
        struct callout          pm_timer;       /* Power management events */
        struct callout          reset_timer;    /* Hard-reset timeout */
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to