diff options
author | Aaron Campbell <aaron@cvs.openbsd.org> | 2000-07-06 19:12:13 +0000 |
---|---|---|
committer | Aaron Campbell <aaron@cvs.openbsd.org> | 2000-07-06 19:12:13 +0000 |
commit | 10d0f7c3810416e288c3dfedfbb8bda2328a4bb7 (patch) | |
tree | b53514dcf45853ce3cf3fadda7cefda7e031f72e /sys/dev/pci | |
parent | 780162e866f56e9467e311c00e05ee6a48a3ed6c (diff) |
Add support for the National Semiconductor DP83815 Fast Ethernet chip.
This chip is currently being used on the NetGear FA312-TX adapter, which is
presumably a replacement for the PNIC-based FA310-TX. From FreeBSD.
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/if_sis.c | 240 | ||||
-rw-r--r-- | sys/dev/pci/if_sisreg.h | 26 |
2 files changed, 238 insertions, 28 deletions
diff --git a/sys/dev/pci/if_sis.c b/sys/dev/pci/if_sis.c index 78997e1e062..b5a9bfaa2de 100644 --- a/sys/dev/pci/if_sis.c +++ b/sys/dev/pci/if_sis.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_sis.c,v 1.3 2000/02/15 02:28:14 jason Exp $ */ +/* $OpenBSD: if_sis.c,v 1.4 2000/07/06 19:12:12 aaron Exp $ */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. @@ -30,7 +30,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/pci/if_sis.c,v 1.13 1999/09/25 17:29:01 wpaul Exp $ + * $FreeBSD: src/sys/pci/if_sis.c,v 1.17 2000/07/06 06:02:04 wpaul Exp $ */ /* @@ -120,6 +120,7 @@ void sis_watchdog __P((struct ifnet *)); int sis_ifmedia_upd __P((struct ifnet *)); void sis_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); +u_int16_t sis_reverse __P((u_int16_t)); void sis_delay __P((struct sis_softc *)); void sis_eeprom_idle __P((struct sis_softc *)); void sis_eeprom_putbyte __P((struct sis_softc *, int)); @@ -130,8 +131,9 @@ int sis_miibus_readreg __P((struct device *, int, int)); void sis_miibus_writereg __P((struct device *, int, int, int)); void sis_miibus_statchg __P((struct device *)); -void sis_setmulti __P((struct sis_softc *)); -u_int32_t sis_calchash __P((caddr_t)); +void sis_setmulti_sis __P((struct sis_softc *)); +void sis_setmulti_ns __P((struct sis_softc *)); +u_int32_t sis_crc __P((struct sis_softc *, caddr_t)); void sis_reset __P((struct sis_softc *)); int sis_list_rx_init __P((struct sis_softc *)); int sis_list_tx_init __P((struct sis_softc *)); @@ -150,6 +152,21 @@ int sis_list_tx_init __P((struct sis_softc *)); #define SIO_CLR(x) \ CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x) +/* + * Routine to reverse the bits in a word. Stolen almost + * verbatim from /usr/games/fortune. + */ +u_int16_t sis_reverse(n) + u_int16_t n; +{ + n = ((n >> 1) & 0x5555) | ((n << 1) & 0xaaaa); + n = ((n >> 2) & 0x3333) | ((n << 2) & 0xcccc); + n = ((n >> 4) & 0x0f0f) | ((n << 4) & 0xf0f0); + n = ((n >> 8) & 0x00ff) | ((n << 8) & 0xff00); + + return(n); +} + void sis_delay(sc) struct sis_softc *sc; { @@ -233,9 +250,9 @@ void sis_eeprom_getword(sc, addr, dest) /* Enter EEPROM access mode. */ sis_delay(sc); - SIO_SET(SIS_EECTL_CSEL); + SIO_CLR(SIS_EECTL_CLK); sis_delay(sc); - SIO_SET(SIS_EECTL_CLK); + SIO_SET(SIS_EECTL_CSEL); sis_delay(sc); /* @@ -294,7 +311,26 @@ int sis_miibus_readreg(self, phy, reg) int phy, reg; { struct sis_softc *sc = (struct sis_softc *)self; - int i, val; + int i, val = 0; + + if (sc->sis_type == SIS_TYPE_83815) { + if (phy != 0) + return(0); + /* + * The NatSemi chip can take a while after + * a reset to come ready, during which the BMSR + * returns a value of 0. This is *never* supposed + * to happen: some of the BMSR bits are meant to + * be hardwired in the on position, and this can + * confuse the miibus code a bit during the probe + * and attach phase. So we make an effort to check + * for this condition and wait for it to clear. + */ + if (!CSR_READ_4(sc, NS_BMSR)) + DELAY(1000); + val = CSR_READ_4(sc, NS_BMCR + (reg * 4)); + return(val); + } if (sc->sis_type == SIS_TYPE_900 && phy != 0) return(0); @@ -327,6 +363,13 @@ void sis_miibus_writereg(self, phy, reg, data) struct sis_softc *sc = (struct sis_softc *)self; int i; + if (sc->sis_type == SIS_TYPE_83815) { + if (phy != 0) + return; + CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data); + return; + } + if (sc->sis_type == SIS_TYPE_900 && phy != 0) return; @@ -366,7 +409,8 @@ void sis_miibus_statchg(self) return; } -u_int32_t sis_calchash(addr) +u_int32_t sis_crc(sc, addr) + struct sis_softc *sc; caddr_t addr; { u_int32_t crc, carry; @@ -387,11 +431,69 @@ u_int32_t sis_calchash(addr) } } - /* return the filter bit position */ + /* + * return the filter bit position + * + * The NatSemi chip has a 512-bit filter, which is + * different than the SiS, so we special-case it. + */ + if (sc->sis_type == SIS_TYPE_83815) + return((crc >> 23) & 0x1FF); + return((crc >> 25) & 0x0000007F); } -void sis_setmulti(sc) +void sis_setmulti_ns(sc) + struct sis_softc *sc; +{ + struct ifnet *ifp; + struct arpcom *ac = &sc->arpcom; + struct ether_multi *enm; + struct ether_multistep step; + u_int32_t h = 0, i, filtsave; + int bit, index; + + ifp = &sc->arpcom.ac_if; + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + SIS_CLRBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); + SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); + return; + } + + /* + * We have to explicitly enable the multicast hash table + * on the NatSemi chip if we want to use it, which we do. + */ + SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); + SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); + + filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); + + /* first, zot all the existing hash bits */ + for (i = 0; i < 32; i++) { + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + (i*2)); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); + } + + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + h = sis_crc(sc, enm->enm_addrlo); + index = h >> 3; + bit = h & 0x1F; + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + index); + if (bit > 0xF) + bit -= 0x10; + SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << bit)); + ETHER_NEXT_MULTI(step, enm); + } + + CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); + + return; +} + +void sis_setmulti_sis(sc) struct sis_softc *sc; { struct ifnet *ifp; @@ -420,7 +522,7 @@ void sis_setmulti(sc) /* now program new ones */ ETHER_FIRST_MULTI(step, ac, enm); while (enm != NULL) { - h = sis_calchash(enm->enm_addrlo); + h = sis_crc(sc, enm->enm_addrlo); CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + (h >> 4)) << 16); SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << (h & 0xF))); ETHER_NEXT_MULTI(step, enm); @@ -462,7 +564,8 @@ int sis_probe(parent, match, aux) { struct pci_attach_args *pa = (struct pci_attach_args *)aux; - if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_SIS) + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_SIS && + PCI_VENDOR(pa->pa_id) != PCI_VENDOR_NS) return(0); switch (PCI_PRODUCT(pa->pa_id)) { @@ -470,6 +573,8 @@ int sis_probe(parent, match, aux) return(1); case PCI_PRODUCT_SIS_7016: return(1); + case PCI_PRODUCT_NS_DP83815: + return(1); } return(0); @@ -497,7 +602,20 @@ void sis_attach(parent, self, aux) s = splimp(); sc->sis_unit = sc->sc_dev.dv_unit; - sc->sis_type = SIS_TYPE_900; + + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_SIS_900: + sc->sis_type = SIS_TYPE_900; + break; + case PCI_PRODUCT_SIS_7016: + sc->sis_type = SIS_TYPE_7016; + break; + case PCI_PRODUCT_NS_DP83815: + sc->sis_type = SIS_TYPE_83815; + break; + default: + break; + } /* * Handle power management nonsense. @@ -590,10 +708,51 @@ void sis_attach(parent, self, aux) /* * Get station address from the EEPROM. */ - sis_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, SIS_EE_NODEADDR,3,0); + switch (PCI_VENDOR(pa->pa_id)) { + case PCI_VENDOR_NS: + /* + * Reading the MAC address out of the EEPROM on + * the NatSemi chip takes a bit more work than + * you'd expect. The address spans 4 16-bit words, + * with the first word containing only a single bit. + * You have to shift everything over one bit to + * get it aligned properly. Also, the bits are + * stored backwards (the LSB is really the MSB, + * and so on) so you have to reverse them in order + * to get the MAC address into the form we want. + * Why? Who the hell knows. + */ + { + u_int16_t tmp[4]; + + sis_read_eeprom(sc, (caddr_t)&tmp, NS_EE_NODEADDR,4,0); + + /* Shift everything over one bit. */ + tmp[3] = tmp[3] >> 1; + tmp[3] |= tmp[2] >> 15; + tmp[2] = tmp[2] >> 1; + tmp[2] |= tmp[1] >> 15; + tmp[1] = tmp[1] >> 1; + tmp[1] |= tmp[0] >> 15; + + /* Now reverse all the bits. */ + tmp[3] = sis_reverse(tmp[3]); + tmp[2] = sis_reverse(tmp[2]); + tmp[1] = sis_reverse(tmp[1]); + + bcopy((char *)&tmp[1], sc->arpcom.ac_enaddr, + ETHER_ADDR_LEN); + } + break; + case PCI_VENDOR_SIS: + default: + sis_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, + SIS_EE_NODEADDR, 3, 0); + break; + } printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); - + sc->sis_ldata_ptr = malloc(sizeof(struct sis_list_data) + 8, M_DEVBUF, M_NOWAIT); if (sc->sis_ldata_ptr == NULL) { @@ -1096,15 +1255,27 @@ void sis_init(xsc) mii = &sc->sc_mii; /* Set MAC address */ - CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); - CSR_WRITE_4(sc, SIS_RXFILT_DATA, - ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); - CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); - CSR_WRITE_4(sc, SIS_RXFILT_DATA, - ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); - CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); - CSR_WRITE_4(sc, SIS_RXFILT_DATA, - ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); + if (sc->sis_type == SIS_TYPE_83815) { + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); + } else { + CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); + } /* Init circular RX list. */ if (sis_list_rx_init(sc) == ENOBUFS) { @@ -1120,6 +1291,17 @@ void sis_init(xsc) */ sis_list_tx_init(sc); + /* + * For the NatSemi chip, we have to explicitly enable the + * reception of ARP frames, as well as turn on the 'perfect + * match' filter where we store the station address, otherwise + * we won't receive unicasts meant for this host. + */ + if (sc->sis_type == SIS_TYPE_83815) { + SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP); + SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT); + } + /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); @@ -1139,7 +1321,10 @@ void sis_init(xsc) /* * Load the multicast filter. */ - sis_setmulti(sc); + if (sc->sis_type == SIS_TYPE_83815) + sis_setmulti_ns(sc); + else + sis_setmulti_sis(sc); /* Turn the receive filter on */ SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE); @@ -1266,7 +1451,10 @@ int sis_ioctl(ifp, command, data) * Multicast list has changed; set the hardware * filter accordingly. */ - sis_setmulti(sc); + if (sc->sis_type == SIS_TYPE_83815) + sis_setmulti_ns(sc); + else + sis_setmulti_sis(sc); error = 0; } break; diff --git a/sys/dev/pci/if_sisreg.h b/sys/dev/pci/if_sisreg.h index 910f42b5ff2..f27ad80c364 100644 --- a/sys/dev/pci/if_sisreg.h +++ b/sys/dev/pci/if_sisreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_sisreg.h,v 1.1 1999/12/04 20:27:37 aaron Exp $ */ +/* $OpenBSD: if_sisreg.h,v 1.2 2000/07/06 19:12:12 aaron Exp $ */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. @@ -30,7 +30,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/pci/if_sisreg.h,v 1.1 1999/09/05 21:00:59 wpaul Exp $ + * $FreeBSD: src/sys/pci/if_sisreg.h,v 1.2 2000/07/06 06:02:04 wpaul Exp $ */ /* @@ -75,6 +75,16 @@ #define SIS_TIMEUNIT 0xA4 #define SIS_GPIO 0xB8 +/* NS DP83815 registers */ +#define NS_BMCR 0x80 +#define NS_BMSR 0x84 +#define NS_PHYIDR1 0x88 +#define NS_PHYIDR2 0x8C +#define NS_ANAR 0x90 +#define NS_ANLPAR 0x94 +#define NS_ANER 0x98 +#define NS_ANNPTR 0x9C + #define SIS_CSR_TX_ENABLE 0x00000001 #define SIS_CSR_TX_DISABLE 0x00000002 #define SIS_CSR_RX_ENABLE 0x00000004 @@ -101,6 +111,7 @@ #define SIS_EECMD_ERASE 0x1c0 #define SIS_EE_NODEADDR 0x8 +#define NS_EE_NODEADDR 0x6 #define SIS_PCICTL_SRAMADDR 0x0000001F #define SIS_PCICTL_RAMTSTENB 0x00000020 @@ -221,6 +232,9 @@ (SIS_RXCFG_DRAIN(64)|SIS_RXDMA_256BYTES) #define SIS_RXFILTCTL_ADDR 0x000F0000 +#define NS_RXFILTCTL_MCHASH 0x00200000 +#define NS_RXFILTCTL_ARP 0x00400000 +#define NS_RXFILTCTL_PERFECT 0x08000000 #define SIS_RXFILTCTL_ALLPHYS 0x10000000 #define SIS_RXFILTCTL_ALLMULTI 0x20000000 #define SIS_RXFILTCTL_BROAD 0x40000000 @@ -238,6 +252,13 @@ #define SIS_FILTADDR_MAR6 0x000A0000 #define SIS_FILTADDR_MAR7 0x000B0000 +#define NS_FILTADDR_PAR0 0x00000000 +#define NS_FILTADDR_PAR1 0x00000002 +#define NS_FILTADDR_PAR2 0x00000004 + +#define NS_FILTADDR_FMEM_LO 0x00000200 +#define NS_FILTADDR_FMEM_HI 0x000003FE + /* * DMA descriptor structures. The first part of the descriptor * is the hardware descriptor format, which is just three longwords. @@ -331,6 +352,7 @@ struct sis_type { #define SIS_TYPE_900 1 #define SIS_TYPE_7016 2 +#define SIS_TYPE_83815 3 struct sis_softc { struct device sc_dev; /* generic device structure */ |