diff --git a/sys/dev/dc/if_dc.c b/sys/dev/dc/if_dc.c index 38f4ca58c6df..184fa8ffa096 100644 --- a/sys/dev/dc/if_dc.c +++ b/sys/dev/dc/if_dc.c @@ -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_t *); 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, int reg) 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, int reg, int data) 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: @@ -1284,6 +1316,97 @@ dc_setfilt_asix(struct dc_softc *sc) CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[1]); } +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) { @@ -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 bits) 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); diff --git a/sys/dev/dc/if_dcreg.h b/sys/dev/dc/if_dcreg.h index cc7df2934822..acd80c026d8f 100644 --- a/sys/dev/dc/if_dcreg.h +++ b/sys/dev/dc/if_dcreg.h @@ -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)) /*