summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2000-07-06 06:02:04 +0000
committerwpaul <wpaul@FreeBSD.org>2000-07-06 06:02:04 +0000
commit966a32cd5be7d251249d23eed9b6f23015e0907c (patch)
tree686dc936281e02259b748195c1a13cb004e0a59f /sys
parentba16109118a0985a50a5780377fab30a9fea33ad (diff)
downloadFreeBSD-src-966a32cd5be7d251249d23eed9b6f23015e0907c.zip
FreeBSD-src-966a32cd5be7d251249d23eed9b6f23015e0907c.tar.gz
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.
Diffstat (limited to 'sys')
-rw-r--r--sys/pci/if_sis.c218
-rw-r--r--sys/pci/if_sisreg.h32
2 files changed, 229 insertions, 21 deletions
diff --git a/sys/pci/if_sis.c b/sys/pci/if_sis.c
index b954433..8410a7f 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:
diff --git a/sys/pci/if_sisreg.h b/sys/pci/if_sisreg.h
index cef3a52..a74dc3b 100644
--- a/sys/pci/if_sisreg.h
+++ b/sys/pci/if_sisreg.h
@@ -74,6 +74,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
@@ -100,6 +110,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
@@ -220,6 +231,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
@@ -237,6 +251,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.
@@ -322,6 +343,16 @@ struct sis_ring_data {
#define SIS_DEVICEID_900 0x0900
#define SIS_DEVICEID_7016 0x7016
+/*
+ * NatSemi vendor ID
+ */
+#define NS_VENDORID 0x100B
+
+/*
+ * DP83815 device ID
+ */
+#define NS_DEVICEID_DP83815 0x0020
+
struct sis_type {
u_int16_t sis_vid;
u_int16_t sis_did;
@@ -330,6 +361,7 @@ struct sis_type {
#define SIS_TYPE_900 1
#define SIS_TYPE_7016 2
+#define SIS_TYPE_83815 3
struct sis_softc {
struct arpcom arpcom; /* interface info */
OpenPOWER on IntegriCloud