bge: Rework MII register accessing and auto-polling support
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 2 Jul 2012 05:30:04 +0000 (13:30 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 2 Jul 2012 08:46:01 +0000 (16:46 +0800)
Obtained-from: FreeBSD 213485 213710 213808 213812

sys/dev/netif/bge/if_bge.c
sys/dev/netif/bge/if_bgereg.h

index 980eaac..137a097 100644 (file)
@@ -352,6 +352,7 @@ static void bge_miibus_statchg(device_t);
 static void    bge_bcm5700_link_upd(struct bge_softc *, uint32_t);
 static void    bge_tbi_link_upd(struct bge_softc *, uint32_t);
 static void    bge_copper_link_upd(struct bge_softc *, uint32_t);
+static void    bge_autopoll_link_upd(struct bge_softc *, uint32_t);
 
 static void    bge_reset(struct bge_softc *);
 
@@ -628,57 +629,54 @@ static int
 bge_miibus_readreg(device_t dev, int phy, int reg)
 {
        struct bge_softc *sc = device_get_softc(dev);
-       struct ifnet *ifp = &sc->arpcom.ac_if;
-       uint32_t val, autopoll;
+       uint32_t val;
        int i;
 
        KASSERT(phy == sc->bge_phyno,
            ("invalid phyno %d, should be %d", phy, sc->bge_phyno));
 
-       /* Reading with autopolling on may trigger PCI errors */
-       autopoll = CSR_READ_4(sc, BGE_MI_MODE);
-       if (autopoll & BGE_MIMODE_AUTOPOLL) {
-               BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
-               DELAY(40);
+       /* Clear the autopoll bit if set, otherwise may trigger PCI errors. */
+       if (sc->bge_mi_mode & BGE_MIMODE_AUTOPOLL) {
+               CSR_WRITE_4(sc, BGE_MI_MODE,
+                   sc->bge_mi_mode & ~BGE_MIMODE_AUTOPOLL);
+               DELAY(80);
        }
 
-       CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_READ|BGE_MICOMM_BUSY|
-           BGE_MIPHY(phy)|BGE_MIREG(reg));
+       CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_READ | BGE_MICOMM_BUSY |
+           BGE_MIPHY(phy) | BGE_MIREG(reg));
 
+       /* Poll for the PHY register access to complete. */
        for (i = 0; i < BGE_TIMEOUT; i++) {
                DELAY(10);
                val = CSR_READ_4(sc, BGE_MI_COMM);
-               if (!(val & BGE_MICOMM_BUSY))
+               if ((val & BGE_MICOMM_BUSY) == 0) {
+                       DELAY(5);
+                       val = CSR_READ_4(sc, BGE_MI_COMM);
                        break;
+               }
        }
-
        if (i == BGE_TIMEOUT) {
-               if_printf(ifp, "PHY read timed out "
-                         "(phy %d, reg %d, val 0x%08x)\n", phy, reg, val);
+               if_printf(&sc->arpcom.ac_if, "PHY read timed out "
+                   "(phy %d, reg %d, val 0x%08x)\n", phy, reg, val);
                val = 0;
-               goto done;
        }
 
-       DELAY(5);
-       val = CSR_READ_4(sc, BGE_MI_COMM);
-
-done:
-       if (autopoll & BGE_MIMODE_AUTOPOLL) {
-               BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
-               DELAY(40);
+       /* Restore the autopoll bit if necessary. */
+       if (sc->bge_mi_mode & BGE_MIMODE_AUTOPOLL) {
+               CSR_WRITE_4(sc, BGE_MI_MODE, sc->bge_mi_mode);
+               DELAY(80);
        }
 
        if (val & BGE_MICOMM_READFAIL)
-               return(0);
+               return 0;
 
-       return(val & 0xFFFF);
+       return (val & 0xFFFF);
 }
 
 static int
 bge_miibus_writereg(device_t dev, int phy, int reg, int val)
 {
        struct bge_softc *sc = device_get_softc(dev);
-       uint32_t autopoll;
        int i;
 
        KASSERT(phy == sc->bge_phyno,
@@ -686,17 +684,17 @@ bge_miibus_writereg(device_t dev, int phy, int reg, int val)
 
        if (sc->bge_asicrev == BGE_ASICREV_BCM5906 &&
            (reg == BRGPHY_MII_1000CTL || reg == BRGPHY_MII_AUXCTL))
-              return(0);
+              return 0;
 
-       /* Reading with autopolling on may trigger PCI errors */
-       autopoll = CSR_READ_4(sc, BGE_MI_MODE);
-       if (autopoll & BGE_MIMODE_AUTOPOLL) {
-               BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
-               DELAY(40);
+       /* Clear the autopoll bit if set, otherwise may trigger PCI errors. */
+       if (sc->bge_mi_mode & BGE_MIMODE_AUTOPOLL) {
+               CSR_WRITE_4(sc, BGE_MI_MODE,
+                   sc->bge_mi_mode & ~BGE_MIMODE_AUTOPOLL);
+               DELAY(80);
        }
 
-       CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_WRITE|BGE_MICOMM_BUSY|
-           BGE_MIPHY(phy)|BGE_MIREG(reg)|val);
+       CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_WRITE | BGE_MICOMM_BUSY |
+           BGE_MIPHY(phy) | BGE_MIREG(reg) | val);
 
        for (i = 0; i < BGE_TIMEOUT; i++) {
                DELAY(10);
@@ -706,19 +704,18 @@ bge_miibus_writereg(device_t dev, int phy, int reg, int val)
                        break;
                }
        }
-
-       if (autopoll & BGE_MIMODE_AUTOPOLL) {
-               BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
-               DELAY(40);
-       }
-
        if (i == BGE_TIMEOUT) {
                if_printf(&sc->arpcom.ac_if, "PHY write timed out "
-                         "(phy %d, reg %d, val %d)\n", phy, reg, val);
-               return(0);
+                   "(phy %d, reg %d, val %d)\n", phy, reg, val);
        }
 
-       return(0);
+       /* Restore the autopoll bit if necessary. */
+       if (sc->bge_mi_mode & BGE_MIMODE_AUTOPOLL) {
+               CSR_WRITE_4(sc, BGE_MI_MODE, sc->bge_mi_mode);
+               DELAY(80);
+       }
+
+       return 0;
 }
 
 static void
@@ -730,6 +727,31 @@ bge_miibus_statchg(device_t dev)
        sc = device_get_softc(dev);
        mii = device_get_softc(sc->bge_miibus);
 
+       if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+           (IFM_ACTIVE | IFM_AVALID)) {
+               switch (IFM_SUBTYPE(mii->mii_media_active)) {
+               case IFM_10_T:
+               case IFM_100_TX:
+                       sc->bge_link = 1;
+                       break;
+               case IFM_1000_T:
+               case IFM_1000_SX:
+               case IFM_2500_SX:
+                       if (sc->bge_asicrev != BGE_ASICREV_BCM5906)
+                               sc->bge_link = 1;
+                       else
+                               sc->bge_link = 0;
+                       break;
+               default:
+                       sc->bge_link = 0;
+                       break;
+               }
+       } else {
+               sc->bge_link = 0;
+       }
+       if (sc->bge_link == 0)
+               return;
+
        BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_PORTMODE);
        if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T ||
            IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) {
@@ -1764,11 +1786,17 @@ bge_blockinit(struct bge_softc *sc)
            BGE_MACSTAT_LINK_CHANGED);
        CSR_WRITE_4(sc, BGE_MI_STS, 0);
 
-       /* Enable PHY auto polling (for MII/GMII only) */
+       /*
+        * Enable attention when the link has changed state for
+        * devices that use auto polling.
+        */
        if (sc->bge_flags & BGE_FLAG_TBI) {
                CSR_WRITE_4(sc, BGE_MI_STS, BGE_MISTS_LINK);
        } else {
-               BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL|10<<16);
+               if (sc->bge_mi_mode & BGE_MIMODE_AUTOPOLL) {
+                       CSR_WRITE_4(sc, BGE_MI_MODE, sc->bge_mi_mode);
+                       DELAY(80);
+               }
                if (sc->bge_asicrev == BGE_ASICREV_BCM5700 &&
                    sc->bge_chipid != BGE_CHIPID_BCM5700_B2) {
                        CSR_WRITE_4(sc, BGE_MAC_EVT_ENB,
@@ -1990,6 +2018,21 @@ bge_attach(device_t dev)
                }
        }
 
+       /* Identify the chips that use an CPMU. */
+       if (sc->bge_asicrev == BGE_ASICREV_BCM5784 ||
+           sc->bge_asicrev == BGE_ASICREV_BCM5761 ||
+           sc->bge_asicrev == BGE_ASICREV_BCM5785 ||
+           sc->bge_asicrev == BGE_ASICREV_BCM57780)
+               sc->bge_flags |= BGE_FLAG_CPMU;
+       if (sc->bge_flags & BGE_FLAG_CPMU)
+               sc->bge_mi_mode = BGE_MIMODE_500KHZ_CONST;
+       else
+               sc->bge_mi_mode = BGE_MIMODE_BASE;
+
+       /* Enable auto polling for BCM570[0-5]. */
+       if (BGE_IS_5700_FAMILY(sc) || sc->bge_asicrev == BGE_ASICREV_BCM5705)
+               sc->bge_mi_mode |= BGE_MIMODE_AUTOPOLL;
+
        /* Allocate interrupt */
        rid = 0;
 
@@ -2183,6 +2226,9 @@ bge_attach(device_t dev)
        } else if (sc->bge_flags & BGE_FLAG_TBI) {
                sc->bge_link_upd = bge_tbi_link_upd;
                sc->bge_link_chg = BGE_MACSTAT_LINK_CHANGED;
+       } else if (sc->bge_mi_mode & BGE_MIMODE_AUTOPOLL) {
+               sc->bge_link_upd = bge_autopoll_link_upd;
+               sc->bge_link_chg = BGE_MACSTAT_LINK_CHANGED;
        } else {
                sc->bge_link_upd = bge_copper_link_upd;
                sc->bge_link_chg = BGE_MACSTAT_LINK_CHANGED;
@@ -3913,32 +3959,45 @@ bge_tbi_link_upd(struct bge_softc *sc, uint32_t status)
 static void
 bge_copper_link_upd(struct bge_softc *sc, uint32_t status __unused)
 {
-       /*
-        * Check that the AUTOPOLL bit is set before
-        * processing the event as a real link change.
-        * Turning AUTOPOLL on and off in the MII read/write
-        * functions will often trigger a link status
-        * interrupt for no reason.
-        */
-       if (CSR_READ_4(sc, BGE_MI_MODE) & BGE_MIMODE_AUTOPOLL) {
-               struct ifnet *ifp = &sc->arpcom.ac_if;
-               struct mii_data *mii = device_get_softc(sc->bge_miibus);
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct mii_data *mii = device_get_softc(sc->bge_miibus);
 
-               mii_pollstat(mii);
+       mii_pollstat(mii);
+       bge_miibus_statchg(sc->bge_dev);
 
-               if (!sc->bge_link &&
-                   (mii->mii_media_status & IFM_ACTIVE) &&
-                   IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
-                       sc->bge_link++;
-                       if (bootverbose)
-                               if_printf(ifp, "link UP\n");
-               } else if (sc->bge_link &&
-                   (!(mii->mii_media_status & IFM_ACTIVE) ||
-                   IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE)) {
-                       sc->bge_link = 0;
-                       if (bootverbose)
-                               if_printf(ifp, "link DOWN\n");
-               }
+       if (bootverbose) {
+               if (sc->bge_link)
+                       if_printf(ifp, "link UP\n");
+               else
+                       if_printf(ifp, "link DOWN\n");
+       }
+
+       /* Clear the attention. */
+       CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED |
+           BGE_MACSTAT_CFG_CHANGED | BGE_MACSTAT_MI_COMPLETE |
+           BGE_MACSTAT_LINK_CHANGED);
+}
+
+static void
+bge_autopoll_link_upd(struct bge_softc *sc, uint32_t status __unused)
+{
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct mii_data *mii = device_get_softc(sc->bge_miibus);
+
+       mii_pollstat(mii);
+
+       if (!sc->bge_link &&
+           (mii->mii_media_status & IFM_ACTIVE) &&
+           IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+               sc->bge_link++;
+               if (bootverbose)
+                       if_printf(ifp, "link UP\n");
+       } else if (sc->bge_link &&
+           (!(mii->mii_media_status & IFM_ACTIVE) ||
+           IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE)) {
+               sc->bge_link = 0;
+               if (bootverbose)
+                       if_printf(ifp, "link DOWN\n");
        }
 
        /* Clear the attention. */
index aa96244..6a0ca8e 100644 (file)
 #define BGE_MISTS_LINK                 0x00000001
 #define BGE_MISTS_10MBPS               0x00000002
 
+#define BGE_MIMODE_CLK_10MHZ           0x00000001
 #define BGE_MIMODE_SHORTPREAMBLE       0x00000002
 #define BGE_MIMODE_AUTOPOLL            0x00000010
 #define BGE_MIMODE_CLKCNT              0x001F0000
+#define BGE_MIMODE_500KHZ_CONST                0x00008000
+#define BGE_MIMODE_BASE                        0x000C0000
 
 
 /*
 /* Receive List Selector Status register */
 #define BGE_RXLSSTAT_ERROR             0x00000004
 
+#define        BGE_CPMU_CTRL                   0x3600
+#define        BGE_CPMU_LSPD_10MB_CLK          0x3604
+#define        BGE_CPMU_LSPD_1000MB_CLK        0x360C
+#define        BGE_CPMU_LNK_AWARE_PWRMD        0x3610
+#define        BGE_CPMU_HST_ACC                0x361C
+#define        BGE_CPMU_CLCK_STAT              0x3630
+#define        BGE_CPMU_MUTEX_REQ              0x365C
+#define        BGE_CPMU_MUTEX_GNT              0x3660
+#define        BGE_CPMU_PHY_STRAP              0x3664
+
+/* Central Power Management Unit (CPMU) register */
+#define        BGE_CPMU_CTRL_LINK_IDLE_MODE    0x00000200
+#define        BGE_CPMU_CTRL_LINK_AWARE_MODE   0x00000400
+#define        BGE_CPMU_CTRL_LINK_SPEED_MODE   0x00004000
+#define        BGE_CPMU_CTRL_GPHY_10MB_RXONLY  0x00010000
+
+/* Link Speed 10MB/No Link Power Mode Clock Policy register */
+#define        BGE_CPMU_LSPD_10MB_MACCLK_MASK  0x001F0000
+#define        BGE_CPMU_LSPD_10MB_MACCLK_6_25  0x00130000
+
+/* Link Speed 1000MB Power Mode Clock Policy register */
+#define        BGE_CPMU_LSPD_1000MB_MACCLK_62_5        0x00000000
+#define        BGE_CPMU_LSPD_1000MB_MACCLK_12_5        0x00110000
+#define        BGE_CPMU_LSPD_1000MB_MACCLK_MASK        0x001F0000
+
+/* Link Aware Power Mode Clock Policy register */
+#define        BGE_CPMU_LNK_AWARE_MACCLK_MASK  0x001F0000
+#define        BGE_CPMU_LNK_AWARE_MACCLK_6_25  0x00130000
+
+#define        BGE_CPMU_HST_ACC_MACCLK_MASK    0x001F0000
+#define        BGE_CPMU_HST_ACC_MACCLK_6_25    0x00130000
+
+/* CPMU Clock Status register */
+#define        BGE_CPMU_CLCK_STAT_MAC_CLCK_MASK        0x001F0000
+#define        BGE_CPMU_CLCK_STAT_MAC_CLCK_62_5        0x00000000
+#define        BGE_CPMU_CLCK_STAT_MAC_CLCK_12_5        0x00110000
+#define        BGE_CPMU_CLCK_STAT_MAC_CLCK_6_25        0x00130000
+
+/* CPMU Mutex Request register */
+#define        BGE_CPMU_MUTEX_REQ_DRIVER       0x00001000
+#define        BGE_CPMU_MUTEX_GNT_DRIVER       0x00001000
+
+/* CPMU GPHY Strap register */
+#define        BGE_CPMU_PHY_STRAP_IS_SERDES    0x00000020
+
 /*
  * Mbuf Cluster Free registers (has nothing to do with BSD mbufs)
  */
@@ -2466,6 +2514,7 @@ struct bge_softc {
 #define BGE_FLAG_TBI           0x00000001
 #define BGE_FLAG_JUMBO         0x00000002
 #define BGE_FLAG_MII_SERDES    0x00000010
+#define        BGE_FLAG_CPMU           0x00000020
 #define BGE_FLAG_PCIX          0x00000200
 #define BGE_FLAG_PCIE          0x00000400
 #define BGE_FLAG_5700_FAMILY   0x00001000
@@ -2497,6 +2546,7 @@ struct bge_softc {
        uint32_t                bge_rx_max_coal_bds;
        uint32_t                bge_tx_max_coal_bds;
        uint32_t                bge_tx_buf_ratio;
+       uint32_t                bge_mi_mode;
        int                     bge_force_defrag;
        int                     bge_if_flags;
        int                     bge_txcnt;