Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci catch up with FreeBSD driver, adds support for A...



details:   https://anonhg.NetBSD.org/src/rev/507b09221e0f
branches:  trunk
changeset: 762555:507b09221e0f
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Wed Feb 23 02:25:04 2011 +0000

description:
catch up with FreeBSD driver, adds support for AR815x chips

diffstat:

 sys/dev/pci/if_alc.c    |  421 ++++++++++++++++++++++++++++++++++++++---------
 sys/dev/pci/if_alcreg.h |   62 +++++-
 2 files changed, 389 insertions(+), 94 deletions(-)

diffs (truncated from 833 to 300 lines):

diff -r a8b597c07411 -r 507b09221e0f sys/dev/pci/if_alc.c
--- a/sys/dev/pci/if_alc.c      Wed Feb 23 01:23:03 2011 +0000
+++ b/sys/dev/pci/if_alc.c      Wed Feb 23 02:25:04 2011 +0000
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
 
-/* Driver for Atheros AR8131/AR8132 PCIe Ethernet. */
+/* Driver for Atheros AR813x/AR815x PCIe Ethernet. */
 
 #ifdef _KERNEL_OPT
 #include "vlan.h"
@@ -79,6 +79,25 @@
 
 #include <dev/pci/if_alcreg.h>
 
+/*
+ * Devices supported by this driver.
+ */
+static struct alc_ident alc_ident_table[] = {
+       { PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8131, 9 * 1024,
+               "Atheros AR8131 PCIe Gigabit Ethernet" },
+       { PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8132, 9 * 1024,
+               "Atheros AR8132 PCIe Fast Ethernet" },
+       { PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8151, 6 * 1024,
+               "Atheros AR8151 v1.0 PCIe Gigabit Ethernet" },
+       { PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8151_V2, 6 * 1024,
+               "Atheros AR8151 v2.0 PCIe Gigabit Ethernet" },
+       { PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8152_B, 6 * 1024,
+               "Atheros AR8152 v1.1 PCIe Fast Ethernet" },
+       { PCI_VENDOR_ATTANSIC, PCI_PRODUCT_ATTANSIC_AR8152_B2, 6 * 1024,
+               "Atheros AR8152 v2.0 PCIe Fast Ethernet" },
+       { 0, 0, 0, NULL },
+};
+
 static int     alc_match(device_t, cfdata_t, void *);
 static void    alc_attach(device_t, device_t, void *);
 static int     alc_detach(device_t, int);
@@ -90,11 +109,13 @@
 static int     alc_mediachange(struct ifnet *);
 static void    alc_mediastatus(struct ifnet *, struct ifmediareq *);
 
-static void    alc_aspm(struct alc_softc *);
+static void    alc_aspm(struct alc_softc *, int);
 static void    alc_disable_l0s_l1(struct alc_softc *);
 static int     alc_dma_alloc(struct alc_softc *);
 static void    alc_dma_free(struct alc_softc *);
 static int     alc_encap(struct alc_softc *, struct mbuf **);
+static struct alc_ident *
+               alc_find_ident(struct pci_attach_args *);
 static void    alc_get_macaddr(struct alc_softc *);
 static void    alc_init_cmb(struct alc_softc *);
 static void    alc_init_rr_ring(struct alc_softc *);
@@ -228,8 +249,8 @@
                reg = CSR_READ_4(sc, ALC_MAC_CFG);
                reg |= MAC_CFG_TX_ENB | MAC_CFG_RX_ENB;
                CSR_WRITE_4(sc, ALC_MAC_CFG, reg);
+               alc_aspm(sc, IFM_SUBTYPE(mii->mii_media_active));
        }
-       alc_aspm(sc);
 }
 
 static void
@@ -261,44 +282,81 @@
        return (error);
 }
 
+static struct alc_ident *
+alc_find_ident(struct pci_attach_args *pa)
+{
+       struct alc_ident *ident;
+       uint16_t vendor, devid;
+
+       vendor = PCI_VENDOR(pa->pa_id);
+       devid = PCI_PRODUCT(pa->pa_id);
+       for (ident = alc_ident_table; ident->name != NULL; ident++) {
+               if (vendor == ident->vendorid && devid == ident->deviceid)
+                       return (ident);
+       }
+
+       return (NULL);
+}
+
 static int
 alc_match(device_t dev, cfdata_t match, void *aux)
 {
        struct pci_attach_args *pa = aux;
 
-       if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_ATTANSIC)
-               return 0;
-
-       switch (PCI_PRODUCT(pa->pa_id)) {
-       case PCI_PRODUCT_ATTANSIC_AR8131:
-       case PCI_PRODUCT_ATTANSIC_AR8132:
-       case PCI_PRODUCT_ATTANSIC_AR8152_B2:
-               break;
-       default:
-               return 0;
-       }
-
-       return 1;
+       return alc_find_ident(pa) != NULL;
 }
 
 static void
 alc_get_macaddr(struct alc_softc *sc)
 {
        uint32_t ea[2], opt;
-       int i;
+       uint16_t val;
+       int eeprom, i;
 
+       eeprom = 0;
        opt = CSR_READ_4(sc, ALC_OPT_CFG);
-       if ((CSR_READ_4(sc, ALC_TWSI_DEBUG) & TWSI_DEBUG_DEV_EXIST) != 0) {
+       if ((CSR_READ_4(sc, ALC_MASTER_CFG) & MASTER_OTP_SEL) != 0 &&
+           (CSR_READ_4(sc, ALC_TWSI_DEBUG) & TWSI_DEBUG_DEV_EXIST) != 0) {
                /*
                 * EEPROM found, let TWSI reload EEPROM configuration.
                 * This will set ethernet address of controller.
                 */
-               if ((opt & OPT_CFG_CLK_ENB) == 0) {
-                       opt |= OPT_CFG_CLK_ENB;
-                       CSR_WRITE_4(sc, ALC_OPT_CFG, opt);
-                       CSR_READ_4(sc, ALC_OPT_CFG);
-                       DELAY(1000);
+               eeprom++;
+               switch (sc->alc_ident->deviceid) {
+               case PCI_PRODUCT_ATTANSIC_AR8131:
+               case PCI_PRODUCT_ATTANSIC_AR8132:
+                       if ((opt & OPT_CFG_CLK_ENB) == 0) {
+                               opt |= OPT_CFG_CLK_ENB;
+                               CSR_WRITE_4(sc, ALC_OPT_CFG, opt);
+                               CSR_READ_4(sc, ALC_OPT_CFG);
+                               DELAY(1000);
+                       }
+                       break;
+               case PCI_PRODUCT_ATTANSIC_AR8151:
+               case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+               case PCI_PRODUCT_ATTANSIC_AR8152_B:
+               case PCI_PRODUCT_ATTANSIC_AR8152_B2:
+                       alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_ADDR, 0x00);
+                       val = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_DATA);
+                       alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_DATA, val & 0xFF7F);
+                       alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_ADDR, 0x3B);
+                       val = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_DATA);
+                       alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_DATA, val | 0x0008);
+                       DELAY(20);
+                       break;
                }
+
+               CSR_WRITE_4(sc, ALC_LTSSM_ID_CFG,
+                   CSR_READ_4(sc, ALC_LTSSM_ID_CFG) & ~LTSSM_ID_WRO_ENB);
+               CSR_WRITE_4(sc, ALC_WOL_CFG, 0);
+               CSR_READ_4(sc, ALC_WOL_CFG);
+
                CSR_WRITE_4(sc, ALC_TWSI_CFG, CSR_READ_4(sc, ALC_TWSI_CFG) |
                    TWSI_CFG_SW_LD_START);
                for (i = 100; i > 0; i--) {
@@ -314,11 +372,36 @@
                if (alcdebug)
                        printf("%s: EEPROM not found!\n", device_xname(sc->sc_dev));
        }
-       if ((opt & OPT_CFG_CLK_ENB) != 0) {
-               opt &= ~OPT_CFG_CLK_ENB;
-               CSR_WRITE_4(sc, ALC_OPT_CFG, opt);
-               CSR_READ_4(sc, ALC_OPT_CFG);
-               DELAY(1000);
+       if (eeprom != 0) {
+               switch (sc->alc_ident->deviceid) {
+               case PCI_PRODUCT_ATTANSIC_AR8131:
+               case PCI_PRODUCT_ATTANSIC_AR8132:
+                       if ((opt & OPT_CFG_CLK_ENB) != 0) {
+                               opt &= ~OPT_CFG_CLK_ENB;
+                               CSR_WRITE_4(sc, ALC_OPT_CFG, opt);
+                               CSR_READ_4(sc, ALC_OPT_CFG);
+                               DELAY(1000);
+                       }
+                       break;
+               case PCI_PRODUCT_ATTANSIC_AR8151:
+               case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+               case PCI_PRODUCT_ATTANSIC_AR8152_B:
+               case PCI_PRODUCT_ATTANSIC_AR8152_B2:
+                       alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_ADDR, 0x00);
+                       val = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_DATA);
+                       alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_DATA, val | 0x0080);
+                       alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_ADDR, 0x3B);
+                       val = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_DATA);
+                       alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                           ALC_MII_DBG_DATA, val & 0xFFF7);
+                       DELAY(20);
+                       break;
+               }
        }
 
        ea[0] = CSR_READ_4(sc, ALC_PAR0);
@@ -363,6 +446,43 @@
        CSR_READ_2(sc, ALC_GPHY_CFG);
        DELAY(10 * 1000);
 
+       /* DSP fixup, Vendor magic. */
+       if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B) {
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_ADDR, 0x000A);
+               data = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_DATA);
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_DATA, data & 0xDFFF);
+       }
+       if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151 ||
+           sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2 ||
+           sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B ||
+           sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B2) {
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_ADDR, 0x003B);
+               data = alc_miibus_readreg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_DATA);
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_DATA, data & 0xFFF7);
+               DELAY(20 * 1000);
+       }
+       if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151) {
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_ADDR, 0x0029);
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_DATA, 0x929D);
+       }
+       if (sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8131 ||
+           sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8132 ||
+           sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8151_V2 ||
+           sc->alc_ident->deviceid == PCI_PRODUCT_ATTANSIC_AR8152_B2) {
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_ADDR, 0x0029);
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   ALC_MII_DBG_DATA, 0xB6DD);
+       }
+
        /* Load DSP codes, vendor magic. */
        data = ANA_LOOP_SEL_10BT | ANA_EN_MASK_TB | ANA_EN_10BT_IDLE |
            ((1 << ANA_INTERVAL_SEL_TIMER_SHIFT) & ANA_INTERVAL_SEL_TIMER_MASK);
@@ -411,35 +531,115 @@
 static void
 alc_phy_down(struct alc_softc *sc)
 {
-
-       /* Force PHY down. */
-       CSR_WRITE_2(sc, ALC_GPHY_CFG,
-           GPHY_CFG_EXT_RESET | GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE |
-           GPHY_CFG_SEL_ANA_RESET | GPHY_CFG_PHY_IDDQ | GPHY_CFG_PWDOWN_HW);
-       DELAY(1000);
+       switch (sc->alc_ident->deviceid) {
+       case PCI_PRODUCT_ATTANSIC_AR8151:
+       case PCI_PRODUCT_ATTANSIC_AR8151_V2:
+               /*
+                * GPHY power down caused more problems on AR8151 v2.0.
+                * When driver is reloaded after GPHY power down,
+                * accesses to PHY/MAC registers hung the system. Only
+                * cold boot recovered from it.  I'm not sure whether
+                * AR8151 v1.0 also requires this one though.  I don't
+                * have AR8151 v1.0 controller in hand.
+                * The only option left is to isolate the PHY and
+                * initiates power down the PHY which in turn saves
+                * more power when driver is unloaded.
+                */
+               alc_miibus_writereg(sc->sc_dev, sc->alc_phyaddr,
+                   MII_BMCR, BMCR_ISO | BMCR_PDOWN);
+               break;
+       default:
+               /* Force PHY down. */
+               CSR_WRITE_2(sc, ALC_GPHY_CFG,
+                   GPHY_CFG_EXT_RESET | GPHY_CFG_HIB_EN | GPHY_CFG_HIB_PULSE |
+                   GPHY_CFG_SEL_ANA_RESET | GPHY_CFG_PHY_IDDQ |
+                   GPHY_CFG_PWDOWN_HW);
+               DELAY(1000);
+               break;
+       }
 }
 
 static void
-alc_aspm(struct alc_softc *sc)
+alc_aspm(struct alc_softc *sc, int media)
 {
        uint32_t pmcfg;
-
+       uint16_t linkcfg;
+ 
        pmcfg = CSR_READ_4(sc, ALC_PM_CFG);
+       if ((sc->alc_flags & (ALC_FLAG_APS | ALC_FLAG_PCIE)) ==
+           (ALC_FLAG_APS | ALC_FLAG_PCIE))



Home | Main Index | Thread Index | Old Index