aboutsummaryrefslogtreecommitdiff
path: root/sys/pci/if_sis.c
diff options
context:
space:
mode:
authorBill Paul <wpaul@FreeBSD.org>2000-07-06 06:02:04 +0000
committerBill Paul <wpaul@FreeBSD.org>2000-07-06 06:02:04 +0000
commit95674596b9c0f5f5054b9542c9fce1418ac754ae (patch)
treef97cfb8529ceb31c408c55e91d4fa6e4becac900 /sys/pci/if_sis.c
parent18c8a61a476743fd2e2b960d6854ce28f131f09c (diff)
downloadsrc-95674596b9c0f5f5054b9542c9fce1418ac754ae.tar.gz
src-95674596b9c0f5f5054b9542c9fce1418ac754ae.zip
Add support for the National Semiconductor DP83815 fast ethernet
controller chip. This chip is currently being used on the NetGear FA312-TX adapter, which I guess is a replacement for the FA310-TX (PNIC-based). I added support for this chip by modifying the sis driver since the SiS 900 and the NS DP83815 have almost the same programming interface (the RX filter programming and PHY access methods are different, but the general configuration, DMA scheme and register layout are identical). I would have had this done a lot sooner, but getting the damn MAC address out of the EEPROM proved to be more complicated than expected.
Notes
Notes: svn path=/head/; revision=62672
Diffstat (limited to 'sys/pci/if_sis.c')
-rw-r--r--sys/pci/if_sis.c218
1 files changed, 197 insertions, 21 deletions
diff --git a/sys/pci/if_sis.c b/sys/pci/if_sis.c
index b954433d4785..8410a7f70ec7 100644
--- a/sys/pci/if_sis.c
+++ b/sys/pci/if_sis.c
@@ -106,6 +106,7 @@ static const char rcsid[] =
static struct sis_type sis_devs[] = {
{ SIS_VENDORID, SIS_DEVICEID_900, "SiS 900 10/100BaseTX" },
{ SIS_VENDORID, SIS_DEVICEID_7016, "SiS 7016 10/100BaseTX" },
+ { NS_VENDORID, NS_DEVICEID_DP83815, "NatSemi DP83815 10/100BaseTX" },
{ 0, 0, NULL }
};
@@ -132,6 +133,7 @@ static void sis_shutdown __P((device_t));
static int sis_ifmedia_upd __P((struct ifnet *));
static void sis_ifmedia_sts __P((struct ifnet *, struct ifmediareq *));
+static u_int16_t sis_reverse __P((u_int16_t));
static void sis_delay __P((struct sis_softc *));
static void sis_eeprom_idle __P((struct sis_softc *));
static void sis_eeprom_putbyte __P((struct sis_softc *, int));
@@ -142,8 +144,9 @@ static int sis_miibus_readreg __P((device_t, int, int));
static int sis_miibus_writereg __P((device_t, int, int, int));
static void sis_miibus_statchg __P((device_t));
-static void sis_setmulti __P((struct sis_softc *));
-static u_int32_t sis_calchash __P((caddr_t));
+static void sis_setmulti_sis __P((struct sis_softc *));
+static void sis_setmulti_ns __P((struct sis_softc *));
+static u_int32_t sis_crc __P((struct sis_softc *, caddr_t));
static void sis_reset __P((struct sis_softc *));
static int sis_list_rx_init __P((struct sis_softc *));
static int sis_list_tx_init __P((struct sis_softc *));
@@ -200,6 +203,21 @@ DRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0);
#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.
+ */
+static 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);
+}
+
static void sis_delay(sc)
struct sis_softc *sc;
{
@@ -283,9 +301,9 @@ static 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);
/*
@@ -344,10 +362,29 @@ static int sis_miibus_readreg(dev, phy, reg)
int phy, reg;
{
struct sis_softc *sc;
- int i, val;
+ int i, val = 0;
sc = device_get_softc(dev);
+ 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);
@@ -381,6 +418,16 @@ static int sis_miibus_writereg(dev, phy, reg, data)
sc = device_get_softc(dev);
+ if (sc->sis_type == SIS_TYPE_83815) {
+ if (phy != 0)
+ return(0);
+ CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data);
+ return(0);
+ }
+
+ if (sc->sis_type == SIS_TYPE_900 && phy != 0)
+ return(0);
+
if (sc->sis_type == SIS_TYPE_900 && phy != 0)
return(0);
@@ -421,7 +468,8 @@ static void sis_miibus_statchg(dev)
return;
}
-static u_int32_t sis_calchash(addr)
+static u_int32_t sis_crc(sc, addr)
+ struct sis_softc *sc;
caddr_t addr;
{
u_int32_t crc, carry;
@@ -442,11 +490,68 @@ static 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);
}
-static void sis_setmulti(sc)
+static void sis_setmulti_ns(sc)
+ struct sis_softc *sc;
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ 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);
+ }
+
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ 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));
+ }
+
+ CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave);
+
+ return;
+}
+
+static void sis_setmulti_sis(sc)
struct sis_softc *sc;
{
struct ifnet *ifp;
@@ -475,7 +580,7 @@ static void sis_setmulti(sc)
ifma = ifma->ifma_link.le_next) {
if (ifma->ifma_addr->sa_family != AF_LINK)
continue;
- h = sis_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + (h >> 4)) << 16);
SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << (h & 0xF)));
}
@@ -552,6 +657,8 @@ static int sis_attach(dev)
sc->sis_type = SIS_TYPE_900;
if (pci_get_device(dev) == SIS_DEVICEID_7016)
sc->sis_type = SIS_TYPE_7016;
+ if (pci_get_vendor(dev) == NS_VENDORID)
+ sc->sis_type = SIS_TYPE_83815;
/*
* Handle power management nonsense.
@@ -645,7 +752,47 @@ static int sis_attach(dev)
/*
* Get station address from the EEPROM.
*/
- sis_read_eeprom(sc, (caddr_t)&eaddr, SIS_EE_NODEADDR, 3, 0);
+ switch (pci_get_vendor(dev)) {
+ case NS_VENDORID:
+ /*
+ * 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], eaddr, ETHER_ADDR_LEN);
+ }
+ break;
+ case SIS_VENDORID:
+ default:
+ sis_read_eeprom(sc, (caddr_t)&eaddr, SIS_EE_NODEADDR, 3, 0);
+ break;
+ }
/*
* A SiS chip was detected. Inform the world.
@@ -1183,15 +1330,27 @@ static void sis_init(xsc)
mii = device_get_softc(sc->sis_miibus);
/* 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) {
@@ -1207,6 +1366,17 @@ static 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);
@@ -1226,7 +1396,10 @@ static 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);
@@ -1331,7 +1504,10 @@ static int sis_ioctl(ifp, command, data)
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
- sis_setmulti(sc);
+ if (sc->sis_type == SIS_TYPE_83815)
+ sis_setmulti_ns(sc);
+ else
+ sis_setmulti_sis(sc);
error = 0;
break;
case SIOCGIFMEDIA: