e1000phy: Add support for more Marvell PHYs
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 5 Aug 2011 02:04:58 +0000 (10:04 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 5 Aug 2011 02:07:46 +0000 (10:07 +0800)
Obtained-from: FreeBSD @211046 +223688

sys/dev/netif/mii_layer/e1000phy.c
sys/dev/netif/mii_layer/e1000phyreg.h

index 7b5b676..6b14045 100644 (file)
@@ -76,22 +76,26 @@ static device_method_t e1000phy_methods[] = {
 };
 
 static const struct mii_phydesc e1000phys[] = {
-       MII_PHYDESC(xxMARVELL,  E1000_2),
+       MII_PHYDESC(xxMARVELL,  E1011),
        MII_PHYDESC(xxMARVELL,  E1000_3),
        MII_PHYDESC(xxMARVELL,  E1000_5),
        MII_PHYDESC(xxMARVELL,  E1111),
        MII_PHYDESC(MARVELL,    E1000),
        MII_PHYDESC(MARVELL,    E1011),
        MII_PHYDESC(MARVELL,    E1000_3),
-       MII_PHYDESC(MARVELL,    E1000_4),
+       MII_PHYDESC(MARVELL,    E1000S),
        MII_PHYDESC(MARVELL,    E1000_5),
-       MII_PHYDESC(MARVELL,    E1000_6),
+       MII_PHYDESC(MARVELL,    E1101),
        MII_PHYDESC(MARVELL,    E3082),
        MII_PHYDESC(MARVELL,    E1112),
        MII_PHYDESC(MARVELL,    E1149),
        MII_PHYDESC(MARVELL,    E1111),
        MII_PHYDESC(MARVELL,    E1116),
+       MII_PHYDESC(MARVELL,    E1116R),
        MII_PHYDESC(MARVELL,    E1118),
+       MII_PHYDESC(MARVELL,    E3016),
+       MII_PHYDESC(MARVELL,    PHYG65G),
+       MII_PHYDESC(MARVELL,    E1149R),
        MII_PHYDESC_NULL
 };
 
@@ -124,8 +128,6 @@ e1000phy_attach(device_t dev)
        struct mii_softc *sc;
        struct mii_attach_args *ma;
        struct mii_data *mii;
-       const char *sep = "";
-       int fast_ether = 0;
 
        sc = device_get_softc(dev);
        ma = device_get_ivars(dev);
@@ -148,10 +150,20 @@ e1000phy_attach(device_t dev)
                if (PHY_READ(sc, E1000_ESSR) & E1000_ESSR_FIBER_LINK)
                        sc->mii_flags |= MIIF_HAVEFIBER;
                break;
-       case MII_MODEL_MARVELL_E3082:
-               /* 88E3082 10/100 Fast Ethernet PHY. */
-               sc->mii_anegticks = MII_ANEGTICKS;
-               fast_ether = 1;
+       case MII_MODEL_MARVELL_E1149:
+       case MII_MODEL_MARVELL_E1149R:
+               /*
+                * Some 88E1149 PHY's page select is initialized to
+                * point to other bank instead of copper/fiber bank
+                * which in turn resulted in wrong registers were
+                * accessed during PHY operation. It is believed that
+                * page 0 should be used for copper PHY so reinitialize
+                * E1000_EADR to select default copper PHY. If parent
+                * device know the type of PHY(either copper or fiber),
+                * that information should be used to select default
+                * type of PHY.
+                */
+               PHY_WRITE(sc, E1000_EADR, 0);
                break;
        }
 
@@ -159,59 +171,29 @@ e1000phy_attach(device_t dev)
 
        e1000phy_reset(sc);
 
-#define        ADD(m, c)       ifmedia_add(&mii->mii_media, (m), (c), NULL)
-#define PRINT(s)       kprintf("%s%s", sep, s); sep = ", "
-
-       device_printf(dev, "%s", "");
-
-       ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
-           E1000_CR_ISOLATE);
-       if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
-               if (!fast_ether) {
-                       /*
-                        * 1000T-simplex not supported; driver must ignore
-                        * this entry, but it must be present in order to
-                        * manually set full-duplex.
-                        */
-                       ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0,
-                           sc->mii_inst), E1000_CR_SPEED_1000);
-                       ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX,
-                           sc->mii_inst),
-                           E1000_CR_SPEED_1000 | E1000_CR_FULL_DUPLEX);
-                       PRINT("1000baseT-FDX");
-               }
-               ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
-                   E1000_CR_SPEED_100 | E1000_CR_FULL_DUPLEX);
-               PRINT("100baseTX-FDX");
-               ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
-                   E1000_CR_SPEED_100);
-               PRINT("100baseTX");
-               ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
-                   E1000_CR_SPEED_10 | E1000_CR_FULL_DUPLEX);
-               PRINT("10baseTX-FDX");
-               ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
-                   E1000_CR_SPEED_10);
-               PRINT("10baseTX");
-       } else {
-               ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst),
-                   E1000_CR_SPEED_1000 | E1000_CR_FULL_DUPLEX);
-               PRINT("1000baseSX-FDX");
-       }
+       sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
+       if (sc->mii_capabilities & BMSR_EXTSTAT)
+               sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
 
-       ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
-       PRINT("auto\n");
+       if ((sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX)) == 0)
+               sc->mii_anegticks = MII_ANEGTICKS;
 
-#undef ADD
-#undef PRINT
+       device_printf(dev, " ");
+       if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
+           (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
+               kprintf("no media present");
+       else
+               mii_phy_add_media(sc);
+       kprintf("\n");
 
        MIIBUS_MEDIAINIT(sc->mii_dev);
-       return(0);
+       return 0;
 }
 
 static void
 e1000phy_reset(struct mii_softc *sc)
 {
-       uint16_t reg;
+       uint16_t reg, page;
 
        reg = PHY_READ(sc, E1000_SCR);
        if ((sc->mii_flags & MIIF_HAVEFIBER) != 0) {
@@ -219,12 +201,14 @@ e1000phy_reset(struct mii_softc *sc)
                PHY_WRITE(sc, E1000_SCR, reg);
                if (sc->mii_model == MII_MODEL_MARVELL_E1112) {
                        /* Select 1000BASE-X only mode. */
+                       page = PHY_READ(sc, E1000_EADR);
                        PHY_WRITE(sc, E1000_EADR, 2);
                        reg = PHY_READ(sc, E1000_SCR);
                        reg &= ~E1000_SCR_MODE_MASK;
                        reg |= E1000_SCR_MODE_1000BX;
                        PHY_WRITE(sc, E1000_SCR, reg);
-                       PHY_WRITE(sc, E1000_EADR, 1);
+                       /* TODO */
+                       PHY_WRITE(sc, E1000_EADR, page);
                }
        } else {
                switch (sc->mii_model) {
@@ -233,31 +217,75 @@ e1000phy_reset(struct mii_softc *sc)
                case MII_MODEL_MARVELL_E1116:
                case MII_MODEL_MARVELL_E1118:
                case MII_MODEL_MARVELL_E1149:
+               case MII_MODEL_MARVELL_E1149R:
+               case MII_MODEL_MARVELL_PHYG65G:
                        /* Disable energy detect mode. */
                        reg &= ~E1000_SCR_EN_DETECT_MASK;
                        reg |= E1000_SCR_AUTO_X_MODE;
+                       if (sc->mii_model == MII_MODEL_MARVELL_E1116)
+                               reg &= ~E1000_SCR_POWER_DOWN;
+                       reg |= E1000_SCR_ASSERT_CRS_ON_TX;
                        break;
                case MII_MODEL_MARVELL_E3082:
                        reg |= (E1000_SCR_AUTO_X_MODE >> 1);
+                       reg |= E1000_SCR_ASSERT_CRS_ON_TX;
+                       break;
+               case MII_MODEL_MARVELL_E3016:
+                       reg |= E1000_SCR_AUTO_MDIX;
+                       reg &= ~(E1000_SCR_EN_DETECT |
+                           E1000_SCR_SCRAMBLER_DISABLE);
+                       reg |= E1000_SCR_LPNP;
+                       /* XXX Enable class A driver for Yukon FE+ A0. */
+                       PHY_WRITE(sc, 0x1C, PHY_READ(sc, 0x1C) | 0x0001);
                        break;
                default:
                        reg &= ~E1000_SCR_AUTO_X_MODE;
+                       reg |= E1000_SCR_ASSERT_CRS_ON_TX;
                        break;
                }
-               /* Enable CRS on TX. */
-               reg |= E1000_SCR_ASSERT_CRS_ON_TX;
-               /* Auto correction for reversed cable polarity. */
-               reg &= ~E1000_SCR_POLARITY_REVERSAL;
+               if (sc->mii_model != MII_MODEL_MARVELL_E3016) {
+                       /* Auto correction for reversed cable polarity. */
+                       reg &= ~E1000_SCR_POLARITY_REVERSAL;
+               }
                PHY_WRITE(sc, E1000_SCR, reg);
+
+               if (sc->mii_model == MII_MODEL_MARVELL_E1116 ||
+                   sc->mii_model == MII_MODEL_MARVELL_E1149 ||
+                   sc->mii_model == MII_MODEL_MARVELL_E1149R) {
+                       PHY_WRITE(sc, E1000_EADR, 2);
+                       reg = PHY_READ(sc, E1000_SCR);
+                       reg |= E1000_SCR_RGMII_POWER_UP;
+                       PHY_WRITE(sc, E1000_SCR, reg);
+                       PHY_WRITE(sc, E1000_EADR, 0);
+               }
        }
 
        switch (MII_MODEL(sc->mii_model)) {
        case MII_MODEL_MARVELL_E3082:
        case MII_MODEL_MARVELL_E1112:
-       case MII_MODEL_MARVELL_E1116:
        case MII_MODEL_MARVELL_E1118:
-       case MII_MODEL_MARVELL_E1149:
                break;
+       case MII_MODEL_MARVELL_E1116:
+               page = PHY_READ(sc, E1000_EADR);
+               /* Select page 3, LED control register. */
+               PHY_WRITE(sc, E1000_EADR, 3);
+               PHY_WRITE(sc, E1000_SCR,
+                   E1000_SCR_LED_LOS(1) |      /* Link/Act */
+                   E1000_SCR_LED_INIT(8) |     /* 10Mbps */
+                   E1000_SCR_LED_STAT1(7) |    /* 100Mbps */
+                   E1000_SCR_LED_STAT0(7));    /* 1000Mbps */
+               /* Set blink rate. */
+               PHY_WRITE(sc, E1000_IER, E1000_PULSE_DUR(E1000_PULSE_170MS) |
+                   E1000_BLINK_RATE(E1000_BLINK_84MS));
+               PHY_WRITE(sc, E1000_EADR, page);
+               break;
+       case MII_MODEL_MARVELL_E3016:
+               /* LED2 -> ACT, LED1 -> LINK, LED0 -> SPEED. */
+               PHY_WRITE(sc, 0x16, 0x0B << 8 | 0x05 << 4 | 0x04);
+               /* Integrated register calibration workaround. */
+               PHY_WRITE(sc, 0x1D, 17);
+               PHY_WRITE(sc, 0x1E, 0x3F60);
+               break;
        default:
                /* Force TX_CLK to 25MHz clock. */
                reg = PHY_READ(sc, E1000_ESCR);
@@ -313,13 +341,15 @@ e1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
                speed = 0;
                switch (IFM_SUBTYPE(ife->ifm_media)) {
                case IFM_1000_T:
-                       if (sc->mii_model == MII_MODEL_MARVELL_E3082)
-                               return (EINVAL);
+                       if ((sc->mii_extcapabilities &
+                           (EXTSR_1000TFDX | EXTSR_1000THDX)) == 0)
+                               return EINVAL;
                        speed = E1000_CR_SPEED_1000;
                        break;
                case IFM_1000_SX:
-                       if (sc->mii_model == MII_MODEL_MARVELL_E3082)
-                               return (EINVAL);
+                       if ((sc->mii_extcapabilities &
+                           (EXTSR_1000XFDX | EXTSR_1000XHDX)) == 0)
+                               return EINVAL;
                        speed = E1000_CR_SPEED_1000;
                        break;
                case IFM_100_TX:
@@ -366,7 +396,8 @@ e1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
                                    E1000_1GCR_MS_ENABLE);
                        }
                } else {
-                       if (sc->mii_model != MII_MODEL_MARVELL_E3082)
+                       if ((sc->mii_extcapabilities &
+                           (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)
                                PHY_WRITE(sc, E1000_1GCR, 0);
                }
                PHY_WRITE(sc, E1000_AR, E1000_AR_SELECTOR_FIELD);
@@ -428,18 +459,14 @@ static void
 e1000phy_status(struct mii_softc *sc)
 {
        struct mii_data *mii = sc->mii_pdata;
-       int bmsr, bmcr, esr, gsr, ssr, isr, ar, lpar;
+       int bmsr, bmcr, gsr, ssr, ar, lpar;
 
        mii->mii_media_status = IFM_AVALID;
        mii->mii_media_active = IFM_ETHER;
 
        bmsr = PHY_READ(sc, E1000_SR) | PHY_READ(sc, E1000_SR);
-       esr = PHY_READ(sc, E1000_ESR);
        bmcr = PHY_READ(sc, E1000_CR);
        ssr = PHY_READ(sc, E1000_SSR);
-       isr = PHY_READ(sc, E1000_ISR);
-       ar = PHY_READ(sc, E1000_AR);
-       lpar = PHY_READ(sc, E1000_LPAR);
 
        if (bmsr & E1000_SR_LINK_STATUS)
                mii->mii_media_status |= IFM_ACTIVE;
@@ -447,9 +474,7 @@ e1000phy_status(struct mii_softc *sc)
        if (bmcr & E1000_CR_LOOPBACK)
                mii->mii_media_active |= IFM_LOOP;
 
-       if (((bmcr & E1000_CR_AUTO_NEG_ENABLE) != 0 &&
-            (bmsr & E1000_SR_AUTO_NEG_COMPLETE) == 0) ||
-           (ssr & E1000_SSR_LINK) == 0 ||
+       if ((bmcr & E1000_CR_AUTO_NEG_ENABLE) != 0 &&
            (ssr & E1000_SSR_SPD_DPLX_RESOLVED) == 0) {
                /* Erg, still trying, I guess... */
                mii->mii_media_active |= IFM_NONE;
@@ -457,15 +482,26 @@ e1000phy_status(struct mii_softc *sc)
        }
 
        if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
-               if (ssr & E1000_SSR_1000MBS)
+               switch (ssr & E1000_SSR_SPEED) {
+               case E1000_SSR_1000MBS:
                        mii->mii_media_active |= IFM_1000_T;
-               else if (ssr & E1000_SSR_100MBS)
+                       break;
+               case E1000_SSR_100MBS:
                        mii->mii_media_active |= IFM_100_TX;
-               else
+                       break;
+               case E1000_SSR_10MBS:
                        mii->mii_media_active |= IFM_10_T;
+                       break;
+               default:
+                       mii->mii_media_active |= IFM_NONE;
+                       return;
+               }
        } else {
-               if (ssr & E1000_SSR_1000MBS)
-                       mii->mii_media_active |= IFM_1000_SX;
+               /*
+                * Some fiber PHY(88E1112) does not seem to set resolved
+                * speed so always assume we've got IFM_1000_SX.
+                */
+               mii->mii_media_active |= IFM_1000_SX;
        }
 
        if (ssr & E1000_SSR_DUPLEX)
@@ -474,6 +510,8 @@ e1000phy_status(struct mii_softc *sc)
                mii->mii_media_active |= IFM_HDX;
 
        if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
+               ar = PHY_READ(sc, E1000_AR);
+               lpar = PHY_READ(sc, E1000_LPAR);
                /* FLAG0==rx-flow-control FLAG1==tx-flow-control */
                if ((ar & E1000_AR_PAUSE) && (lpar & E1000_LPAR_PAUSE)) {
                        mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1;
@@ -500,14 +538,19 @@ static void
 e1000phy_mii_phy_auto(struct mii_softc *sc)
 {
        if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) {
-               PHY_WRITE(sc, E1000_AR, E1000_AR_10T | E1000_AR_10T_FD |
-                   E1000_AR_100TX | E1000_AR_100TX_FD |
-                   E1000_AR_PAUSE | E1000_AR_ASM_DIR);
+               uint16_t reg;
+
+               reg = PHY_READ(sc, E1000_AR);
+               reg |= E1000_AR_10T | E1000_AR_10T_FD |
+                   E1000_AR_100TX | E1000_AR_100TX_FD |
+                   E1000_AR_PAUSE | E1000_AR_ASM_DIR;
+               PHY_WRITE(sc, E1000_AR, reg | E1000_AR_SELECTOR_FIELD);
        } else {
                PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD | E1000_FA_1000X |
                    E1000_FA_SYM_PAUSE | E1000_FA_ASYM_PAUSE);
        }
-       if (sc->mii_model != MII_MODEL_MARVELL_E3082) {
+       if ((sc->mii_extcapabilities &
+           (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0) {
                PHY_WRITE(sc, E1000_1GCR,
                    E1000_1GCR_1000T_FD | E1000_1GCR_1000T);
        }
index ca66af4..a7b525c 100644 (file)
@@ -1,5 +1,3 @@
-/* $FreeBSD: src/sys/dev/mii/e1000phyreg.h,v 1.4 2006/12/11 10:43:32 yongari Exp $ */
-/* $DragonFly: src/sys/dev/netif/mii_layer/e1000phyreg.h,v 1.4 2007/08/07 11:44:41 sephe Exp $ */
 /*-
  * Principal Author: Parag Patel
  * Copyright (c) 2001
 #define E1000_SCR_TX_FIFO_DEPTH_10     0x8000
 #define E1000_SCR_TX_FIFO_DEPTH_12     0xC000
 
+/* 88E3016 only */
+#define        E1000_SCR_AUTO_MDIX             0x0030
+#define        E1000_SCR_SIGDET_POLARITY       0x0040
+#define        E1000_SCR_EXT_DISTANCE          0x0080
+#define        E1000_SCR_FEFI_DISABLE          0x0100
+#define        E1000_SCR_NLP_GEN_DISABLE       0x0800
+#define        E1000_SCR_LPNP                  0x1000
+#define        E1000_SCR_NLP_CHK_DISABLE       0x2000
+#define        E1000_SCR_EN_DETECT             0x4000
+
 #define E1000_SCR_EN_DETECT_MASK       0x0300
 
+/* 88E1112 page 1 fiber specific control */
+#define E1000_SCR_FIB_TX_DIS           0x0008
+#define E1000_SCR_FIB_SIGDET_POLARITY  0x0200
+#define E1000_SCR_FIB_FORCE_LINK       0x0400
+
 /* 88E1112 page 2 */
 #define E1000_SCR_MODE_MASK            0x0380
 #define E1000_SCR_MODE_AUTO            0x0180
 #define E1000_SCR_MODE_COPPER          0x0280
 #define E1000_SCR_MODE_1000BX          0x0380
 
+/* 88E1116 page 0 */
+#define        E1000_SCR_POWER_DOWN            0x0004
+/* 88E1116, 88E1149 page 2 */
+#define        E1000_SCR_RGMII_POWER_UP        0x0008
+
+/* 88E1116, 88E1149 page 3 */
+#define E1000_SCR_LED_STAT0_MASK       0x000F
+#define E1000_SCR_LED_STAT1_MASK       0x00F0
+#define E1000_SCR_LED_INIT_MASK                0x0F00
+#define E1000_SCR_LED_LOS_MASK         0xF000
+#define E1000_SCR_LED_STAT0(x)         ((x) & E1000_SCR_LED_STAT0_MASK)
+#define E1000_SCR_LED_STAT1(x)         ((x) & E1000_SCR_LED_STAT1_MASK)
+#define E1000_SCR_LED_INIT(x)          ((x) & E1000_SCR_LED_INIT_MASK)
+#define E1000_SCR_LED_LOS(x)           ((x) & E1000_SCR_LED_LOS_MASK)
+
 #define E1000_SSR                      0x11    /* special status register */
 #define E1000_SSR_JABBER               0x0001
 #define E1000_SSR_REV_POLARITY         0x0002
 #define E1000_IER_SPEED_CHANGED                0x4000
 #define E1000_IER_AUTO_NEG_ERR         0x8000
 
+/* 88E1116, 88E1149 page 3, LED timer control. */
+#define        E1000_PULSE_MASK        0x7000
+#define        E1000_PULSE_NO_STR      0       /* no pulse stretching */
+#define        E1000_PULSE_21MS        1       /* 21 ms to 42 ms */
+#define        E1000_PULSE_42MS        2       /* 42 ms to 84 ms */
+#define        E1000_PULSE_84MS        3       /* 84 ms to 170 ms */
+#define        E1000_PULSE_170MS       4       /* 170 ms to 340 ms */
+#define        E1000_PULSE_340MS       5       /* 340 ms to 670 ms */
+#define        E1000_PULSE_670MS       6       /* 670 ms to 1300 ms */
+#define        E1000_PULSE_1300MS      7       /* 1300 ms to 2700 ms */
+#define        E1000_PULSE_DUR(x)      ((x) &  E1000_PULSE_MASK) 
+
+#define        E1000_BLINK_MASK        0x0700
+#define        E1000_BLINK_42MS        0       /* 42 ms */
+#define        E1000_BLINK_84MS        1       /* 84 ms */
+#define        E1000_BLINK_170MS       2       /* 170 ms */
+#define        E1000_BLINK_340MS       3       /* 340 ms */
+#define        E1000_BLINK_670MS       4       /* 670 ms */
+#define        E1000_BLINK_RATE(x)     ((x) &  E1000_BLINK_MASK) 
+
 #define E1000_ISR                      0x13    /* interrupt status reg */
 #define E1000_ISR_JABBER               0x0001
 #define E1000_ISR_POLARITY_CHANGE      0x0002