Author: yongari
Date: Mon Oct 24 20:48:02 2011
New Revision: 226701
URL: http://svn.freebsd.org/changeset/base/226701

Log:
  Add support for ALi/ULi, now NVIDIA, M5261/M5263 PCI FastEthernet
  controller which is found on ULi M1563 South Bridge & M1689 Bridge.
  These controllers look like a tulip clone.
  M5263 controller does not support MII bitbang so use DC_ROM
  register to access MII registers.  Like other tulip variants, ULi
  controller uses a setup frame to configure RX filter and uses new
  setup frame format.  It's not clear to me whether the controller
  supports a hash based multicast filtering so this patch uses 14
  perfect multicast filter to filter multicast frames.  If number of
  multicast addresses is greater than 14, controller is put into a
  mode that receives all multicast frames.
  Due to lack of access to M5261, this change was not tested with
  M5261 but it probably works.  Many thanks to Marco who provided
  remote access to M5263.
  
  Tested by:    Marco Steinbach <coco <> executive-computing dot de>,
                Martin MATO <martin.mato <> orange dot fr>

Modified:
  head/sys/dev/dc/if_dc.c
  head/sys/dev/dc/if_dcreg.h

Modified: head/sys/dev/dc/if_dc.c
==============================================================================
--- head/sys/dev/dc/if_dc.c     Mon Oct 24 20:41:31 2011        (r226700)
+++ head/sys/dev/dc/if_dc.c     Mon Oct 24 20:48:02 2011        (r226701)
@@ -224,6 +224,10 @@ static const struct dc_type dc_devs[] = 
                "Linksys PCMPC200 CardBus 10/100" },
        { DC_DEVID(DC_VENDORID_LINKSYS, DC_DEVICEID_PCMPC200_AB09), 0,
                "Linksys PCMPC200 CardBus 10/100" },
+       { DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5261), 0,
+               "ULi M5261 FastEthernet" },
+       { DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5263), 0,
+               "ULi M5263 FastEthernet" },
        { 0, 0, NULL }
 };
 
@@ -286,6 +290,7 @@ static uint32_t dc_mchash_be(const uint8
 static void dc_setfilt_21143(struct dc_softc *);
 static void dc_setfilt_asix(struct dc_softc *);
 static void dc_setfilt_admtek(struct dc_softc *);
+static void dc_setfilt_uli(struct dc_softc *);
 static void dc_setfilt_xircom(struct dc_softc *);
 
 static void dc_setfilt(struct dc_softc *);
@@ -828,6 +833,23 @@ dc_miibus_readreg(device_t dev, int phy,
                return (0);
        }
 
+       if (sc->dc_type == DC_TYPE_ULI_M5263) {
+               CSR_WRITE_4(sc, DC_ROM,
+                   ((phy << DC_ULI_PHY_ADDR_SHIFT) & DC_ULI_PHY_ADDR_MASK) |
+                   ((reg << DC_ULI_PHY_REG_SHIFT) & DC_ULI_PHY_REG_MASK) |
+                   DC_ULI_PHY_OP_READ);
+               for (i = 0; i < DC_TIMEOUT; i++) {
+                       DELAY(1);
+                       rval = CSR_READ_4(sc, DC_ROM);
+                       if ((rval & DC_ULI_PHY_OP_DONE) != 0) {
+                               return (rval & DC_ULI_PHY_DATA_MASK);
+                       }
+               }
+               if (i == DC_TIMEOUT)
+                       device_printf(dev, "phy read timed out\n");
+               return (0);
+       }
+
        if (DC_IS_COMET(sc)) {
                switch (reg) {
                case MII_BMCR:
@@ -898,6 +920,16 @@ dc_miibus_writereg(device_t dev, int phy
                return (0);
        }
 
+       if (sc->dc_type == DC_TYPE_ULI_M5263) {
+               CSR_WRITE_4(sc, DC_ROM,
+                   ((phy << DC_ULI_PHY_ADDR_SHIFT) & DC_ULI_PHY_ADDR_MASK) |
+                   ((reg << DC_ULI_PHY_REG_SHIFT) & DC_ULI_PHY_REG_MASK) |
+                   ((data << DC_ULI_PHY_DATA_SHIFT) & DC_ULI_PHY_DATA_MASK) |
+                   DC_ULI_PHY_OP_WRITE);
+               DELAY(1);
+               return (0);
+       }
+
        if (DC_IS_COMET(sc)) {
                switch (reg) {
                case MII_BMCR:
@@ -1285,6 +1317,97 @@ dc_setfilt_asix(struct dc_softc *sc)
 }
 
 static void
+dc_setfilt_uli(struct dc_softc *sc)
+{
+       uint8_t eaddr[ETHER_ADDR_LEN];
+       struct ifnet *ifp;
+       struct ifmultiaddr *ifma;
+       struct dc_desc *sframe;
+       uint32_t filter, *sp;
+       uint8_t *ma;
+       int i, mcnt;
+
+       ifp = sc->dc_ifp;
+
+       i = sc->dc_cdata.dc_tx_prod;
+       DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT);
+       sc->dc_cdata.dc_tx_cnt++;
+       sframe = &sc->dc_ldata.dc_tx_list[i];
+       sp = sc->dc_cdata.dc_sbuf;
+       bzero(sp, DC_SFRAME_LEN);
+
+       sframe->dc_data = htole32(DC_ADDR_LO(sc->dc_saddr));
+       sframe->dc_ctl = htole32(DC_SFRAME_LEN | DC_TXCTL_SETUP |
+           DC_TXCTL_TLINK | DC_FILTER_PERFECT | DC_TXCTL_FINT);
+
+       sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)sc->dc_cdata.dc_sbuf;
+
+       /* Set station address. */
+       bcopy(IF_LLADDR(sc->dc_ifp), eaddr, ETHER_ADDR_LEN);
+       *sp++ = DC_SP_MAC(eaddr[1] << 8 | eaddr[0]);
+       *sp++ = DC_SP_MAC(eaddr[3] << 8 | eaddr[2]);
+       *sp++ = DC_SP_MAC(eaddr[5] << 8 | eaddr[4]);
+
+       /* Set broadcast address. */
+       *sp++ = DC_SP_MAC(0xFFFF);
+       *sp++ = DC_SP_MAC(0xFFFF);
+       *sp++ = DC_SP_MAC(0xFFFF);
+
+       /* Extract current filter configuration. */
+       filter = CSR_READ_4(sc, DC_NETCFG);
+       filter &= ~(DC_NETCFG_RX_PROMISC | DC_NETCFG_RX_ALLMULTI);
+
+       /* Now build perfect filters. */
+       mcnt = 0;
+       if_maddr_rlock(ifp);
+       TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+               if (ifma->ifma_addr->sa_family != AF_LINK)
+                       continue;
+               if (mcnt >= DC_ULI_FILTER_NPERF) {
+                       filter |= DC_NETCFG_RX_ALLMULTI;
+                       break;
+               }
+               ma = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
+               *sp++ = DC_SP_MAC(ma[1] << 8 | ma[0]);
+               *sp++ = DC_SP_MAC(ma[3] << 8 | ma[2]);
+               *sp++ = DC_SP_MAC(ma[5] << 8 | ma[4]);
+               mcnt++;
+       }
+       if_maddr_runlock(ifp);
+
+       for (; mcnt < DC_ULI_FILTER_NPERF; mcnt++) {
+               *sp++ = DC_SP_MAC(0xFFFF);
+               *sp++ = DC_SP_MAC(0xFFFF);
+               *sp++ = DC_SP_MAC(0xFFFF);
+       }
+
+       if (filter & (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON))
+               CSR_WRITE_4(sc, DC_NETCFG,
+                   filter & ~(DC_NETCFG_TX_ON | DC_NETCFG_RX_ON));
+       if (ifp->if_flags & IFF_PROMISC)
+               filter |= DC_NETCFG_RX_PROMISC | DC_NETCFG_RX_ALLMULTI;
+       if (ifp->if_flags & IFF_ALLMULTI)
+               filter |= DC_NETCFG_RX_ALLMULTI;
+       CSR_WRITE_4(sc, DC_NETCFG,
+           filter & ~(DC_NETCFG_TX_ON | DC_NETCFG_RX_ON));
+       if (filter & (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON))
+               CSR_WRITE_4(sc, DC_NETCFG, filter);
+
+       sframe->dc_status = htole32(DC_TXSTAT_OWN);
+       bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap, BUS_DMASYNC_PREREAD |
+           BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(sc->dc_stag, sc->dc_smap, BUS_DMASYNC_PREWRITE);
+       CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+       /*
+        * Wait some time...
+        */
+       DELAY(1000);
+
+       sc->dc_wdog_timer = 5;
+}
+
+static void
 dc_setfilt_xircom(struct dc_softc *sc)
 {
        uint16_t eaddr[(ETHER_ADDR_LEN+1)/2];
@@ -1372,6 +1495,9 @@ dc_setfilt(struct dc_softc *sc)
        if (DC_IS_ADMTEK(sc))
                dc_setfilt_admtek(sc);
 
+       if (DC_IS_ULI(sc))
+               dc_setfilt_uli(sc);
+
        if (DC_IS_XIRCOM(sc))
                dc_setfilt_xircom(sc);
 }
@@ -1540,7 +1666,7 @@ dc_reset(struct dc_softc *sc)
        }
 
        if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc) || DC_IS_CONEXANT(sc) ||
-           DC_IS_XIRCOM(sc) || DC_IS_INTEL(sc)) {
+           DC_IS_XIRCOM(sc) || DC_IS_INTEL(sc) || DC_IS_ULI(sc)) {
                DELAY(10000);
                DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET);
                i = 0;
@@ -1752,7 +1878,7 @@ dc_read_srom(struct dc_softc *sc, int bi
        int size;
 
        size = DC_ROM_SIZE(bits);
-       sc->dc_srom = malloc(size, M_DEVBUF, M_NOWAIT);
+       sc->dc_srom = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
        if (sc->dc_srom == NULL) {
                device_printf(sc->dc_dev, "Could not allocate SROM buffer\n");
                return (ENOMEM);
@@ -2046,7 +2172,8 @@ dc_attach(device_t dev)
        struct ifnet *ifp;
        struct dc_mediainfo *m;
        uint32_t reg, revision;
-       int error, mac_offset, phy, rid, tmp;
+       uint16_t *srom;
+       int error, mac_offset, n, phy, rid, tmp;
        uint8_t *mac;
 
        sc = device_get_softc(dev);
@@ -2226,6 +2353,21 @@ dc_attach(device_t dev)
                if (error != 0)
                        goto fail;
                break;
+       case DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5261):
+       case DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5263):
+               if (sc->dc_info->dc_devid ==
+                   DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5261))
+                       sc->dc_type = DC_TYPE_ULI_M5261;
+               else
+                       sc->dc_type = DC_TYPE_ULI_M5263;
+               /* TX buffers should be aligned on 4 byte boundary. */
+               sc->dc_flags |= DC_TX_INTR_ALWAYS | DC_TX_COALESCE |
+                   DC_TX_ALIGN;
+               sc->dc_pmode = DC_PMODE_MII;
+               error = dc_read_srom(sc, sc->dc_romwidth);
+               if (error != 0)
+                       goto fail;
+               break;
        default:
                device_printf(dev, "unknown device: %x\n",
                    sc->dc_info->dc_devid);
@@ -2323,6 +2465,33 @@ dc_attach(device_t dev)
                }
                bcopy(mac, eaddr, ETHER_ADDR_LEN);
                break;
+       case DC_TYPE_ULI_M5261:
+       case DC_TYPE_ULI_M5263:
+               srom = (uint16_t *)sc->dc_srom;
+               if (srom == NULL || *srom == 0xFFFF || *srom == 0) {
+                       /*
+                        * No valid SROM present, read station address
+                        * from ID Table.
+                        */
+                       device_printf(dev,
+                           "Reading station address from ID Table.\n");
+                       CSR_WRITE_4(sc, DC_BUSCTL, 0x10000);
+                       CSR_WRITE_4(sc, DC_SIARESET, 0x01C0);
+                       CSR_WRITE_4(sc, DC_10BTCTRL, 0x0000);
+                       CSR_WRITE_4(sc, DC_10BTCTRL, 0x0010);
+                       CSR_WRITE_4(sc, DC_10BTCTRL, 0x0000);
+                       CSR_WRITE_4(sc, DC_SIARESET, 0x0000);
+                       CSR_WRITE_4(sc, DC_SIARESET, 0x01B0);
+                       mac = (uint8_t *)eaddr;
+                       for (n = 0; n < ETHER_ADDR_LEN; n++)
+                               mac[n] = (uint8_t)CSR_READ_4(sc, DC_10BTCTRL);
+                       CSR_WRITE_4(sc, DC_SIARESET, 0x0000);
+                       CSR_WRITE_4(sc, DC_BUSCTL, 0x0000);
+                       DELAY(10);
+               } else
+                       dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3,
+                           0);
+               break;
        default:
                dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0);
                break;
@@ -3599,7 +3768,7 @@ dc_init_locked(struct dc_softc *sc)
        /*
         * Set cache alignment and burst length.
         */
-       if (DC_IS_ASIX(sc) || DC_IS_DAVICOM(sc))
+       if (DC_IS_ASIX(sc) || DC_IS_DAVICOM(sc) || DC_IS_ULI(sc))
                CSR_WRITE_4(sc, DC_BUSCTL, 0);
        else
                CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME | DC_BUSCTL_MRLE);
@@ -3712,6 +3881,11 @@ dc_init_locked(struct dc_softc *sc)
        CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
        CSR_WRITE_4(sc, DC_ISR, 0xFFFFFFFF);
 
+       /* Initialize TX jabber and RX watchdog timer. */
+       if (DC_IS_ULI(sc))
+               CSR_WRITE_4(sc, DC_WATCHDOG, DC_WDOG_JABBERCLK |
+                   DC_WDOG_HOSTUNJAB);
+
        /* Enable transmitter. */
        DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
 

Modified: head/sys/dev/dc/if_dcreg.h
==============================================================================
--- head/sys/dev/dc/if_dcreg.h  Mon Oct 24 20:41:31 2011        (r226700)
+++ head/sys/dev/dc/if_dcreg.h  Mon Oct 24 20:48:02 2011        (r226701)
@@ -78,6 +78,8 @@
 #define        DC_TYPE_PNIC            0xA     /* 82c168/82c169 PNIC I */
 #define        DC_TYPE_XIRCOM          0xB     /* Xircom X3201 */
 #define        DC_TYPE_CONEXANT        0xC     /* Conexant LANfinity RS7112 */
+#define        DC_TYPE_ULI_M5261       0xD     /* ALi/ULi M5261 */
+#define        DC_TYPE_ULI_M5263       0xE     /* ALi/ULi M5263 */
 
 #define        DC_IS_MACRONIX(x)                       \
        (x->dc_type == DC_TYPE_98713 ||         \
@@ -88,6 +90,10 @@
        (x->dc_type == DC_TYPE_AL981 ||         \
         x->dc_type == DC_TYPE_AN983)
 
+#define        DC_IS_ULI(x)                            \
+       (x->dc_type == DC_TYPE_ULI_M5261 ||     \
+        x->dc_type == DC_TYPE_ULI_M5263)
+
 #define        DC_IS_INTEL(x)          (x->dc_type == DC_TYPE_21143)
 #define        DC_IS_ASIX(x)           (x->dc_type == DC_TYPE_ASIX)
 #define        DC_IS_COMET(x)          (x->dc_type == DC_TYPE_AL981)
@@ -725,6 +731,23 @@ struct dc_mii_frame {
 
 /* End of CONEXANT specific registers */
 
+/*
+ * ULi M5263 specific registers.
+ */
+#define        DC_ULI_FILTER_NPERF     14
+
+#define        DC_ULI_PHY_DATA_MASK    0x0000FFFF
+#define        DC_ULI_PHY_REG_MASK     0x001F0000
+#define        DC_ULI_PHY_ADDR_MASK    0x03E00000
+#define        DC_ULI_PHY_OP_WRITE     0x04000000
+#define        DC_ULI_PHY_OP_READ      0x08000000
+#define        DC_ULI_PHY_OP_DONE      0x10000000
+
+#define        DC_ULI_PHY_DATA_SHIFT   0
+#define        DC_ULI_PHY_REG_SHIFT    16
+#define        DC_ULI_PHY_ADDR_SHIFT   21
+
+/* End of ULi M5263 specific registers */
 
 struct dc_softc {
        struct ifnet            *dc_ifp;        /* interface info */
@@ -1032,6 +1055,17 @@ struct dc_softc {
 #define        DC_DEVICEID_PCMPC200_AB08       0xab08
 #define        DC_DEVICEID_PCMPC200_AB09       0xab09
 
+/*
+ * ULi vendor ID.
+ */
+#define        DC_VENDORID_ULI         0x10b9
+
+/*
+ * ULi device IDs.
+ */
+#define        DC_DEVICEID_M5261       0x5261
+#define        DC_DEVICEID_M5263       0x5263
+
 #define        DC_DEVID(vendor, device)        ((device) << 16 | (vendor))
 
 /*
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to