summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/pci/if_tl.c320
-rw-r--r--sys/pci/if_tlreg.h115
2 files changed, 274 insertions, 161 deletions
diff --git a/sys/pci/if_tl.c b/sys/pci/if_tl.c
index 1ae12ea1..78571c1 100644
--- a/sys/pci/if_tl.c
+++ b/sys/pci/if_tl.c
@@ -29,7 +29,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: if_tl.c,v 1.10 1998/06/21 14:53:34 bde Exp $
+ * $Id: if_tl.c,v 1.1 1998/07/11 00:34:45 wpaul Exp wpaul $
*/
/*
@@ -217,7 +217,7 @@
#ifndef lint
static char rcsid[] =
- "$Id: if_tl.c,v 1.10 1998/06/21 14:53:34 bde Exp $";
+ "$Id: if_tl.c,v 1.1 1998/07/11 00:34:45 wpaul Exp wpaul $";
#endif
/*
@@ -241,8 +241,18 @@ static struct tl_type tl_devs[] = {
"Compaq NetFlex-3/P" },
{ COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC,
"Compaq NetFlex 3/P w/ BNC" },
- { COMPAQ_VENDORID, COMPAQ_DEVICEID_DESKPRO_4000_5233MMX,
- "Compaq Deskpro 4000 5233MMX" },
+ { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED,
+ "Compaq Netelligent 10/100 TX Embedded UTP" },
+ { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX,
+ "Compaq Netelligent 10 T/2 PCI UTP/Coax" },
+ { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP,
+ "Compaq Netelligent 10/100 TX UTP" },
+ { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183,
+ "Olicom OC-2183/2185" },
+ { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325,
+ "Olicom OC-2325" },
+ { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326,
+ "Olicom OC-2326 10/100 TX UTP" },
{ 0, 0, NULL }
};
@@ -267,7 +277,7 @@ static unsigned long tl_count;
static char *tl_probe __P((pcici_t, pcidi_t));
static void tl_attach_ctlr __P((pcici_t, int));
-static int tl_attach_phy __P((struct tl_csr *, int, char *,
+static int tl_attach_phy __P((volatile struct tl_csr *, int, u_char *,
int, struct tl_iflist *));
static int tl_intvec_invalid __P((void *, u_int32_t));
static int tl_intvec_dummy __P((void *, u_int32_t));
@@ -279,7 +289,8 @@ static int tl_intvec_adchk __P((void *, u_int32_t));
static int tl_intvec_netsts __P((void *, u_int32_t));
static int tl_intvec_statoflow __P((void *, u_int32_t));
-static int tl_newbuf __P((struct tl_softc *, struct tl_chain *));
+static int tl_newbuf __P((struct tl_softc *,
+ struct tl_chain_onefrag *));
static void tl_stats_update __P((void *));
static int tl_encap __P((struct tl_softc *, struct tl_chain *,
struct mbuf *));
@@ -294,15 +305,20 @@ static void tl_shutdown __P((int, void *));
static int tl_ifmedia_upd __P((struct ifnet *));
static void tl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *));
-static u_int8_t tl_eeprom_putbyte __P((struct tl_csr *, u_int8_t));
-static u_int8_t tl_eeprom_getbyte __P((struct tl_csr *, u_int8_t ,
- u_int8_t * ));
-static int tl_read_eeprom __P((struct tl_csr *, caddr_t, int, int));
-
-static void tl_mii_sync __P((struct tl_csr *));
-static void tl_mii_send __P((struct tl_csr *, u_int32_t, int));
-static int tl_mii_readreg __P((struct tl_csr *, struct tl_mii_frame *));
-static int tl_mii_writereg __P((struct tl_csr *, struct tl_mii_frame *));
+static u_int8_t tl_eeprom_putbyte __P((volatile struct tl_csr *,
+ u_int8_t));
+static u_int8_t tl_eeprom_getbyte __P((volatile struct tl_csr *,
+ u_int8_t, u_int8_t * ));
+static int tl_read_eeprom __P((volatile struct tl_csr *,
+ caddr_t, int, int));
+
+static void tl_mii_sync __P((volatile struct tl_csr *));
+static void tl_mii_send __P((volatile struct tl_csr *,
+ u_int32_t, int));
+static int tl_mii_readreg __P((volatile struct tl_csr *,
+ struct tl_mii_frame *));
+static int tl_mii_writereg __P((volatile struct tl_csr *,
+ struct tl_mii_frame *));
static u_int16_t tl_phy_readreg __P((struct tl_softc *, int));
static void tl_phy_writereg __P((struct tl_softc *, u_int16_t, u_int16_t));
@@ -310,88 +326,15 @@ static void tl_autoneg __P((struct tl_softc *, int, int));
static void tl_setmode __P((struct tl_softc *, int));
static int tl_calchash __P((unsigned char *));
static void tl_setmulti __P((struct tl_softc *));
-static void tl_softreset __P((struct tl_csr *, int));
+static void tl_softreset __P((volatile struct tl_csr *, int));
static int tl_list_rx_init __P((struct tl_softc *));
static int tl_list_tx_init __P((struct tl_softc *));
/*
- * ThunderLAN adapters typically have a serial EEPROM containing
- * configuration information. The main reason we're interested in
- * it is because it also contains the adapters's station address.
- *
- * Access to the EEPROM is a bit goofy since it is a serial device:
- * you have to do reads and writes one bit at a time. The state of
- * the DATA bit can only change while the CLOCK line is held low.
- * Transactions work basically like this:
- *
- * 1) Send the EEPROM_START sequence to prepare the EEPROM for
- * accepting commands. This pulls the clock high, sets
- * the data bit to 0, enables transmission to the EEPROM,
- * pulls the data bit up to 1, then pulls the clock low.
- * The idea is to do a 0 to 1 transition of the data bit
- * while the clock pin is held high.
- *
- * 2) To write a bit to the EEPROM, set the TXENABLE bit, then
- * set the EDATA bit to send a 1 or clear it to send a 0.
- * Finally, set and then clear ECLOK. Strobing the clock
- * transmits the bit. After 8 bits have been written, the
- * EEPROM should respond with an ACK, which should be read.
- *
- * 3) To read a bit from the EEPROM, clear the TXENABLE bit,
- * then set ECLOK. The bit can then be read by reading EDATA.
- * ECLOCK should then be cleared again. This can be repeated
- * 8 times to read a whole byte, after which the
- *
- * 4) We need to send the address byte to the EEPROM. For this
- * we have to send the write control byte to the EEPROM to
- * tell it to accept data. The byte is 0xA0. The EEPROM should
- * ack this. The address byte can be send after that.
- *
- * 5) Now we have to tell the EEPROM to send us data. For that we
- * have to transmit the read control byte, which is 0xA1. This
- * byte should also be acked. We can then read the data bits
- * from the EEPROM.
- *
- * 6) When we're all finished, send the EEPROM_STOP sequence.
- *
- * Note that we use the ThunderLAN's NetSio register to access the
- * EEPROM, however there is an alternate method. There is a PCI NVRAM
- * register at PCI offset 0xB4 which can also be used with minor changes.
- * The difference is that access to PCI registers via pci_conf_read()
- * and pci_conf_write() is done using programmed I/O, which we want to
- * avoid.
- */
-
-/*
- * Note that EEPROM_START leaves transmission enabled.
- */
-#define EEPROM_START \
- DIO_SEL(TL_NETSIO); \
- DIO_BYTE1_SET(TL_SIO_ECLOK); /* Pull clock pin high */ \
- DIO_BYTE1_SET(TL_SIO_EDATA); /* Set DATA bit to 1 */ \
- DIO_BYTE1_SET(TL_SIO_ETXEN); /* Enable xmit to write bit */ \
- DIO_BYTE1_CLR(TL_SIO_EDATA); /* Pull DATA bit to 0 again */ \
- DIO_BYTE1_CLR(TL_SIO_ECLOK); /* Pull clock low again */
-
-/*
- * EEPROM_STOP ends access to the EEPROM and clears the ETXEN bit so
- * that no further data can be written to the EEPROM I/O pin.
- */
-#define EEPROM_STOP \
- DIO_SEL(TL_NETSIO); \
- DIO_BYTE1_CLR(TL_SIO_ETXEN); /* Disable xmit */ \
- DIO_BYTE1_CLR(TL_SIO_EDATA); /* Pull DATA to 0 */ \
- DIO_BYTE1_SET(TL_SIO_ECLOK); /* Pull clock high */ \
- DIO_BYTE1_SET(TL_SIO_ETXEN); /* Enable xmit */ \
- DIO_BYTE1_SET(TL_SIO_EDATA); /* Toggle DATA to 1 */ \
- DIO_BYTE1_CLR(TL_SIO_ETXEN); /* Disable xmit. */ \
- DIO_BYTE1_CLR(TL_SIO_ECLOK); /* Pull clock low again */
-
-/*
* Send an instruction or address to the EEPROM, check for ACK.
*/
static u_int8_t tl_eeprom_putbyte(csr, byte)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
u_int8_t byte;
{
register int i, ack = 0;
@@ -435,7 +378,7 @@ static u_int8_t tl_eeprom_putbyte(csr, byte)
* Read a byte of data stored in the EEPROM at address 'addr.'
*/
static u_int8_t tl_eeprom_getbyte(csr, addr, dest)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
u_int8_t addr;
u_int8_t *dest;
{
@@ -489,7 +432,7 @@ static u_int8_t tl_eeprom_getbyte(csr, addr, dest)
}
static void tl_mii_sync(csr)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
{
register int i;
@@ -505,7 +448,7 @@ static void tl_mii_sync(csr)
}
static void tl_mii_send(csr, bits, cnt)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
u_int32_t bits;
int cnt;
{
@@ -523,7 +466,7 @@ static void tl_mii_send(csr, bits, cnt)
}
static int tl_mii_readreg(csr, frame)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
struct tl_mii_frame *frame;
{
@@ -625,7 +568,7 @@ fail:
}
static int tl_mii_writereg(csr, frame)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
struct tl_mii_frame *frame;
{
@@ -692,7 +635,7 @@ static u_int16_t tl_phy_readreg(sc, reg)
int reg;
{
struct tl_mii_frame frame;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
bzero((char *)&frame, sizeof(frame));
@@ -715,7 +658,7 @@ static void tl_phy_writereg(sc, reg, data)
u_int16_t data;
{
struct tl_mii_frame frame;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
bzero((char *)&frame, sizeof(frame));
@@ -737,7 +680,7 @@ static void tl_phy_writereg(sc, reg, data)
* Read a sequence of bytes from the EEPROM.
*/
static int tl_read_eeprom(csr, dest, off, cnt)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
caddr_t dest;
int off;
int cnt;
@@ -794,7 +737,7 @@ static void tl_autoneg(sc, flag, verbose)
u_int16_t phy_sts = 0, media = 0;
struct ifnet *ifp;
struct ifmedia *ifm;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
ifm = &sc->ifmedia;
ifp = &sc->arpcom.ac_if;
@@ -826,11 +769,22 @@ static void tl_autoneg(sc, flag, verbose)
DELAY(3000000);
break;
case TL_FLAG_SCHEDDELAY:
+ /*
+ * Wait for the transmitter to go idle before starting
+ * an autoneg session, otherwise tl_start() may clobber
+ * our timeout, and we don't want to allow transmission
+ * during an autoneg session since that can screw it up.
+ */
+ if (!sc->tl_txeoc) {
+ sc->tl_want_auto = 1;
+ return;
+ }
phy_sts = tl_phy_readreg(sc, PHY_BMCR);
phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;
tl_phy_writereg(sc, PHY_BMCR, phy_sts);
ifp->if_timer = 3;
sc->tl_autoneg = 1;
+ sc->tl_want_auto = 0;
return;
case TL_FLAG_DELAYTIMEO:
ifp->if_timer = 0;
@@ -899,6 +853,12 @@ static void tl_autoneg(sc, flag, verbose)
printf("no carrier\n");
}
+ if (sc->tl_tx_pend) {
+ sc->tl_autoneg = 0;
+ sc->tl_tx_pend = 0;
+ tl_start(ifp);
+ }
+
return;
}
@@ -911,7 +871,7 @@ static void tl_setmode(sc, media)
int media;
{
u_int16_t bmcr, anar, ctl;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
csr = sc->csr;
bmcr = tl_phy_readreg(sc, PHY_BMCR);
@@ -988,9 +948,9 @@ static void tl_setmode(sc, media)
* the folded 24-bit value is split into 6-bit portions and XOR'd.
*/
static int tl_calchash(addr)
- unsigned char *addr;
+ unsigned char *addr;
{
- int t;
+ int t;
t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 |
(addr[2] ^ addr[5]);
@@ -1001,7 +961,7 @@ static void tl_setmulti(sc)
struct tl_softc *sc;
{
struct ifnet *ifp;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
u_int32_t hashes[2] = { 0, 0 };
int h;
struct ifmultiaddr *ifma;
@@ -1035,15 +995,15 @@ static void tl_setmulti(sc)
}
static void tl_softreset(csr, internal)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
int internal;
{
u_int32_t cmd, dummy;
/* Assert the adapter reset bit. */
- csr->tl_host_cmd |= TL_CMD_ADRST;
+ CMD_SET(csr, TL_CMD_ADRST);
/* Turn off interrupts */
- csr->tl_host_cmd |= TL_CMD_INTSOFF;
+ CMD_SET(csr, TL_CMD_INTSOFF);
/* First, clear the stats registers. */
DIO_SEL(TL_TXGOODFRAMES|TL_DIO_ADDR_INC);
@@ -1090,8 +1050,8 @@ static void tl_softreset(csr, internal)
cmd = csr->tl_host_cmd;
cmd |= TL_CMD_NES;
cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK);
- csr->tl_host_cmd = cmd | (TL_CMD_LDTHR | TX_THR);
- csr->tl_host_cmd = cmd | (TL_CMD_LDTMR | 0x00000003);
+ CMD_PUT(csr, cmd | (TL_CMD_LDTHR | TX_THR));
+ CMD_PUT(csr, cmd | (TL_CMD_LDTMR | 0x00000003));
/* Unreset the MII */
DIO_SEL(TL_NETSIO);
@@ -1148,6 +1108,10 @@ tl_probe(config_id, device_id)
new->tl_config_id = config_id;
new->tl_dinfo = t;
new->tl_next = tl_iflist;
+ if (t->tl_vid == COMPAQ_VENDORID)
+ new->tl_eeaddr = TL_EEPROM_EADDR;
+ if (t->tl_vid == OLICOM_VENDORID)
+ new->tl_eeaddr = TL_EEPROM_EADDR_OC;
tl_iflist = new;
return(t->tl_name);
}
@@ -1180,9 +1144,9 @@ tl_probe(config_id, device_id)
* ThunderLAN chip. Also also set up interrupt vectors.
*/
static int tl_attach_phy(csr, tl_unit, eaddr, tl_phy, ilist)
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
int tl_unit;
- char *eaddr;
+ u_char *eaddr;
int tl_phy;
struct tl_iflist *ilist;
{
@@ -1424,7 +1388,7 @@ tl_attach_ctlr(config_id, unit)
{
int s, i, phys = 0;
vm_offset_t pbase, vbase;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
u_char eaddr[ETHER_ADDR_LEN];
struct tl_mii_frame frame;
u_int32_t command;
@@ -1459,7 +1423,7 @@ tl_attach_ctlr(config_id, unit)
goto fail;
}
- csr = (struct tl_csr *)vbase;
+ csr = (volatile struct tl_csr *)vbase;
ilist->csr = csr;
ilist->tl_active_phy = TL_PHYS_IDLE;
@@ -1478,7 +1442,7 @@ tl_attach_ctlr(config_id, unit)
* Get station address from the EEPROM.
*/
if (tl_read_eeprom(csr, (caddr_t)&eaddr,
- TL_EEPROM_EADDR, ETHER_ADDR_LEN)) {
+ ilist->tl_eeaddr, ETHER_ADDR_LEN)) {
printf("tlc%d: failed to read station address\n", unit);
goto fail;
}
@@ -1575,7 +1539,7 @@ static int tl_list_rx_init(sc)
for (i = 0; i < TL_TX_LIST_CNT; i++) {
cd->tl_rx_chain[i].tl_ptr =
- (struct tl_list *)&ld->tl_rx_list[i];
+ (struct tl_list_onefrag *)&ld->tl_rx_list[i];
tl_newbuf(sc, &cd->tl_rx_chain[i]);
if (i == (TL_TX_LIST_CNT - 1)) {
cd->tl_rx_chain[i].tl_next = NULL;
@@ -1595,7 +1559,7 @@ static int tl_list_rx_init(sc)
static int tl_newbuf(sc, c)
struct tl_softc *sc;
- struct tl_chain *c;
+ struct tl_chain_onefrag *c;
{
struct mbuf *m_new = NULL;
@@ -1618,8 +1582,8 @@ static int tl_newbuf(sc, c)
c->tl_ptr->tlist_frsize = MCLBYTES;
c->tl_ptr->tlist_cstat = TL_CSTAT_READY;
c->tl_ptr->tlist_fptr = 0;
- c->tl_ptr->tl_frag[0].tlist_dadr = vtophys(mtod(m_new, caddr_t));
- c->tl_ptr->tl_frag[0].tlist_dcnt = MCLBYTES;
+ c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t));
+ c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES;
return(0);
}
@@ -1655,7 +1619,7 @@ static int tl_intvec_rxeof(xsc, type)
struct ether_header *eh;
struct mbuf *m;
struct ifnet *ifp;
- struct tl_chain *cur_rx;
+ struct tl_chain_onefrag *cur_rx;
sc = xsc;
ifp = &sc->arpcom.ac_if;
@@ -1677,6 +1641,19 @@ static int tl_intvec_rxeof(xsc, type)
eh = mtod(m, struct ether_header *);
m->m_pkthdr.rcvif = ifp;
+ /*
+ * Note: when the ThunderLAN chip is in 'capture all
+ * frames' mode, it will receive its own transmissions.
+ * We drop don't need to process our own transmissions,
+ * so we drop them here and continue.
+ */
+ if (ifp->if_flags & IFF_PROMISC &&
+ !bcmp(eh->ether_shost, sc->arpcom.ac_enaddr,
+ ETHER_ADDR_LEN)) {
+ m_freem(m);
+ continue;
+ }
+
#if NBPFILTER > 0
/*
* Handle BPF listeners. Let the BPF user see the packet, but
@@ -1726,7 +1703,7 @@ static int tl_intvec_rxeoc(xsc, type)
/* Flush out the receive queue and ack RXEOF interrupts. */
r = tl_intvec_rxeof(xsc, type);
- sc->csr->tl_host_cmd = TL_CMD_ACK | r | (type & ~(0x00100000));
+ CMD_PUT(sc->csr, TL_CMD_ACK | r | (type & ~(0x00100000)));
r = 1;
sc->csr->tl_ch_parm = vtophys(sc->tl_cdata.tl_rx_head->tl_ptr);
r |= (TL_CMD_GO|TL_CMD_RT);
@@ -1750,7 +1727,7 @@ static int tl_intvec_invalid(xsc, type)
printf("tl%d: got an invalid interrupt!\n", sc->tl_unit);
#endif
/* Re-enable interrupts but don't ack this one. */
- sc->csr->tl_host_cmd |= type;
+ CMD_PUT(sc->csr, type);
return(0);
}
@@ -1816,6 +1793,8 @@ static int tl_intvec_txeof(xsc, type)
cur_tx->tl_next = sc->tl_cdata.tl_tx_free;
sc->tl_cdata.tl_tx_free = cur_tx;
+ if (!cur_tx->tl_ptr->tlist_fptr)
+ break;
}
return(r);
@@ -1858,17 +1837,27 @@ static int tl_intvec_txeoc(xsc, type)
ifp->if_flags &= ~IFF_OACTIVE;
sc->tl_cdata.tl_tx_tail = NULL;
sc->tl_txeoc = 1;
+ /*
+ * If we just drained the TX queue and
+ * there's an autoneg request waiting, set
+ * it in motion. This will block the transmitter
+ * until the autoneg session completes which will
+ * no doubt piss off any processes waiting to
+ * transmit, but that's the way the ball bounces.
+ */
+ if (sc->tl_want_auto)
+ tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1);
} else {
sc->tl_txeoc = 0;
/* First we have to ack the EOC interrupt. */
- sc->csr->tl_host_cmd = TL_CMD_ACK | 0x00000001 | type;
+ CMD_PUT(sc->csr, TL_CMD_ACK | 0x00000001 | type);
/* Then load the address of the next TX list. */
sc->csr->tl_ch_parm = vtophys(sc->tl_cdata.tl_tx_head->tl_ptr);
/* Restart TX channel. */
cmd = sc->csr->tl_host_cmd;
cmd &= ~TL_CMD_RT;
cmd |= TL_CMD_GO|TL_CMD_INTSON;
- sc->csr->tl_host_cmd = cmd;
+ CMD_PUT(sc->csr, cmd);
return(0);
}
@@ -1880,14 +1869,35 @@ static int tl_intvec_adchk(xsc, type)
u_int32_t type;
{
struct tl_softc *sc;
+ u_int16_t bmcr, ctl;
+ volatile struct tl_csr *csr;
sc = xsc;
+ csr = sc->csr;
printf("tl%d: adapter check: %x\n", sc->tl_unit, sc->csr->tl_ch_parm);
+ /*
+ * Before resetting the adapter, try reading the PHY
+ * settings so we can put them back later. This is
+ * necessary to keep the chip operating at the same
+ * speed and duplex settings after the reset completes.
+ */
+ bmcr = tl_phy_readreg(sc, PHY_BMCR);
+ ctl = tl_phy_readreg(sc, TL_PHY_CTL);
tl_softreset(sc->csr, sc->tl_phy_addr == TL_PHYADDR_MAX ? 1 : 0);
+ tl_phy_writereg(sc, PHY_BMCR, bmcr);
+ tl_phy_writereg(sc, TL_PHY_CTL, ctl);
+ if (bmcr & PHY_BMCR_DUPLEX) {
+ DIO_SEL(TL_NETCMD);
+ DIO_BYTE0_SET(TL_CMD_DUPLEX);
+ } else {
+ DIO_SEL(TL_NETCMD);
+ DIO_BYTE0_CLR(TL_CMD_DUPLEX);
+ }
+ tl_stop(sc);
tl_init(sc);
- sc->csr->tl_host_cmd |= TL_CMD_INTSON;
+ CMD_SET(sc->csr, TL_CMD_INTSON);
return(0);
}
@@ -1898,7 +1908,7 @@ static int tl_intvec_netsts(xsc, type)
{
struct tl_softc *sc;
u_int16_t netsts;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
sc = xsc;
csr = sc->csr;
@@ -1917,7 +1927,7 @@ static void tl_intr(xilist)
{
struct tl_iflist *ilist;
struct tl_softc *sc;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
struct ifnet *ifp;
int r = 0;
u_int32_t type = 0;
@@ -1949,7 +1959,7 @@ static void tl_intr(xilist)
* for another PCI device that's sharing our IRQ.
*/
if (ints == TL_INTR_INVALID) {
- csr->tl_host_cmd |= type;
+ CMD_SET(csr, type);
return;
}
printf("tlc%d: interrupt type %x with all phys idle\n",
@@ -1995,8 +2005,12 @@ static void tl_intr(xilist)
}
/* Re-enable interrupts */
- if (r)
- csr->tl_host_cmd = TL_CMD_ACK | r | type;
+ if (r) {
+ CMD_PUT(csr, TL_CMD_ACK | r | type);
+ }
+
+ if (ifp->if_snd.ifq_head != NULL)
+ tl_start(ifp);
return;
}
@@ -2006,7 +2020,7 @@ static void tl_stats_update(xsc)
{
struct tl_softc *sc;
struct ifnet *ifp;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
struct tl_stats tl_stats;
u_int32_t *p;
@@ -2142,7 +2156,7 @@ static void tl_start(ifp)
struct ifnet *ifp;
{
struct tl_softc *sc;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
struct mbuf *m_head = NULL;
u_int32_t cmd;
struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx;
@@ -2150,6 +2164,11 @@ static void tl_start(ifp)
sc = ifp->if_softc;
csr = sc->csr;
+ if (sc->tl_autoneg) {
+ sc->tl_tx_pend = 1;
+ return;
+ }
+
/*
* Check for an available queue slot. If there are none,
* punt.
@@ -2208,13 +2227,10 @@ static void tl_start(ifp)
cmd = sc->csr->tl_host_cmd;
cmd &= ~TL_CMD_RT;
cmd |= TL_CMD_GO|TL_CMD_INTSON;
- sc->csr->tl_host_cmd = cmd;
+ CMD_PUT(sc->csr, cmd);
}
} else {
sc->tl_cdata.tl_tx_tail->tl_next = start_tx;
- sc->tl_cdata.tl_tx_tail->tl_ptr->tlist_fptr =
- vtophys(start_tx->tl_ptr);
- sc->tl_cdata.tl_tx_tail = start_tx;
}
/*
@@ -2230,7 +2246,7 @@ static void tl_init(xsc)
{
struct tl_softc *sc = xsc;
struct ifnet *ifp = &sc->arpcom.ac_if;
- struct tl_csr *csr = sc->csr;
+ volatile struct tl_csr *csr = sc->csr;
int s;
u_int16_t phy_sts;
@@ -2296,14 +2312,14 @@ static void tl_init(xsc)
DIO_BYTE1_SET(TL_SIO_MINTEN);
/* Enable PCI interrupts. */
- csr->tl_host_cmd |= TL_CMD_INTSON;
+ CMD_SET(sc->csr, TL_CMD_INTSON);
/* Load the address of the rx list */
- sc->csr->tl_host_cmd |= TL_CMD_RT;
+ CMD_SET(sc->csr, TL_CMD_RT);
sc->csr->tl_ch_parm = vtophys(&sc->tl_ldata->tl_rx_list[0]);
/* Send the RX go command */
- sc->csr->tl_host_cmd |= (TL_CMD_GO|TL_CMD_RT);
+ CMD_SET(sc->csr, (TL_CMD_GO|TL_CMD_RT));
sc->tl_iflist->tl_active_phy = sc->tl_phy_addr;
ifp->if_flags |= IFF_RUNNING;
@@ -2324,7 +2340,7 @@ static int tl_ifmedia_upd(ifp)
struct ifnet *ifp;
{
struct tl_softc *sc;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
struct ifmedia *ifm;
sc = ifp->if_softc;
@@ -2352,7 +2368,7 @@ static void tl_ifmedia_sts(ifp, ifmr)
u_int16_t phy_ctl;
u_int16_t phy_sts;
struct tl_softc *sc;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
sc = ifp->if_softc;
csr = sc->csr;
@@ -2482,7 +2498,7 @@ static void tl_stop(sc)
{
register int i;
struct ifnet *ifp;
- struct tl_csr *csr;
+ volatile struct tl_csr *csr;
struct tl_mii_frame frame;
csr = sc->csr;
@@ -2492,17 +2508,17 @@ static void tl_stop(sc)
untimeout(tl_stats_update, sc, sc->tl_stat_ch);
/* Stop the transmitter */
- sc->csr->tl_host_cmd &= TL_CMD_RT;
- sc->csr->tl_host_cmd |= TL_CMD_STOP;
+ CMD_CLR(sc->csr, TL_CMD_RT);
+ CMD_SET(sc->csr, TL_CMD_STOP);
/* Stop the receiver */
- sc->csr->tl_host_cmd |= TL_CMD_RT;
- sc->csr->tl_host_cmd |= TL_CMD_STOP;
+ CMD_SET(sc->csr, TL_CMD_RT);
+ CMD_SET(sc->csr, TL_CMD_STOP);
/*
* Disable host interrupts.
*/
- sc->csr->tl_host_cmd |= TL_CMD_INTSOFF;
+ CMD_SET(sc->csr, TL_CMD_INTSOFF);
/*
* Disable PHY interrupts.
@@ -2565,22 +2581,22 @@ static void tl_shutdown(howto, xilist)
void *xilist;
{
struct tl_iflist *ilist = (struct tl_iflist *)xilist;
- struct tl_csr *csr = ilist->csr;
+ volatile struct tl_csr *csr = ilist->csr;
struct tl_mii_frame frame;
int i;
/* Stop the transmitter */
- csr->tl_host_cmd &= TL_CMD_RT;
- csr->tl_host_cmd |= TL_CMD_STOP;
+ CMD_CLR(csr, TL_CMD_RT);
+ CMD_SET(csr, TL_CMD_STOP);
/* Stop the receiver */
- csr->tl_host_cmd |= TL_CMD_RT;
- csr->tl_host_cmd |= TL_CMD_STOP;
+ CMD_SET(csr, TL_CMD_RT);
+ CMD_SET(csr, TL_CMD_STOP);
/*
* Disable host interrupts.
*/
- csr->tl_host_cmd |= TL_CMD_INTSOFF;
+ CMD_SET(csr, TL_CMD_INTSOFF);
/*
* Disable PHY interrupts.
diff --git a/sys/pci/if_tlreg.h b/sys/pci/if_tlreg.h
index e774385..319498a 100644
--- a/sys/pci/if_tlreg.h
+++ b/sys/pci/if_tlreg.h
@@ -29,7 +29,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: if_tlreg.h,v 1.2 1998/05/21 16:24:05 jkh Exp $
+ * $Id: if_tlreg.h,v 1.20 1998/07/12 14:59:25 wpaul Exp wpaul $
*/
@@ -90,12 +90,18 @@ struct tl_chain {
struct tl_chain *tl_next;
};
+struct tl_chain_onefrag {
+ struct tl_list_onefrag *tl_ptr;
+ struct mbuf *tl_mbuf;
+ struct tl_chain_onefrag *tl_next;
+};
+
struct tl_chain_data {
- struct tl_chain tl_rx_chain[TL_RX_LIST_CNT];
+ struct tl_chain_onefrag tl_rx_chain[TL_RX_LIST_CNT];
struct tl_chain tl_tx_chain[TL_TX_LIST_CNT];
- struct tl_chain *tl_rx_head;
- struct tl_chain *tl_rx_tail;
+ struct tl_chain_onefrag *tl_rx_head;
+ struct tl_chain_onefrag *tl_rx_tail;
struct tl_chain *tl_tx_head;
struct tl_chain *tl_tx_tail;
@@ -107,12 +113,14 @@ struct tl_iflist;
struct tl_softc {
struct arpcom arpcom; /* interface info */
struct ifmedia ifmedia; /* media info */
- struct tl_csr *csr; /* pointer to register map */
+ volatile struct tl_csr *csr; /* pointer to register map */
struct tl_type *tl_dinfo; /* ThunderLAN adapter info */
struct tl_type *tl_pinfo; /* PHY info struct */
u_int8_t tl_ctlr; /* chip number */
u_int8_t tl_unit; /* interface number */
u_int8_t tl_phy_addr; /* PHY address */
+ u_int8_t tl_tx_pend; /* TX pending */
+ u_int8_t tl_want_auto; /* autoneg scheduled */
u_int8_t tl_autoneg; /* autoneg in progress */
u_int16_t tl_phy_sts; /* PHY status */
u_int16_t tl_phy_vid; /* PHY vendor ID */
@@ -125,7 +133,10 @@ struct tl_softc {
struct callout_handle tl_stat_ch;
};
-#define TX_THR 0x00000007
+/*
+ * Transmit interrupt threshold.
+ */
+#define TX_THR 0x00000001
#define TL_FLAG_FORCEDELAY 1
#define TL_FLAG_SCHEDDELAY 2
@@ -140,12 +151,13 @@ struct tl_softc {
#define PHY_UNKNOWN 6
struct tl_iflist {
- struct tl_csr *csr; /* Register map */
+ volatile struct tl_csr *csr; /* Register map */
struct tl_type *tl_dinfo;
int tl_active_phy; /* # of active PHY */
int tlc_unit; /* TLAN chip # */
struct tl_softc *tl_sc[TL_PHYADDR_MAX]; /* pointers to PHYs */
pcici_t tl_config_id;
+ u_int8_t tl_eeaddr;
struct tl_iflist *tl_next;
};
@@ -231,10 +243,17 @@ struct tl_iflist {
#define COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED 0xAE35
#define COMPAQ_DEVICEID_NETEL_10_100_DUAL 0xAE40
#define COMPAQ_DEVICEID_NETEL_10_100_PROLIANT 0xAE43
-#define COMPAQ_DEVICEID_DESKPRO_4000_5233MMX 0xB011
+#define COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED 0xB011
+#define COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX 0xB012
+#define COMPAQ_DEVICEID_NETEL_10_100_TX_UTP 0xB030
#define COMPAQ_DEVICEID_NETFLEX_3P 0xF130
#define COMPAQ_DEVICEID_NETFLEX_3P_BNC 0xF150
+#define OLICOM_VENDORID 0x108D
+#define OLICOM_DEVICEID_OC2183 0x0013
+#define OLICOM_DEVICEID_OC2325 0x0012
+#define OLICOM_DEVICEID_OC2326 0x0014
+
/*
* PCI low memory base and low I/O base
*/
@@ -380,6 +399,83 @@ struct tl_csr {
#define DIO_LONG_GET(x) x = csr->u.tl_dio_data
#define DIO_LONG_PUT(x) csr->u.tl_dio_data = (u_int32_t)x
+#define CMD_PUT(c, x) c->tl_host_cmd = (u_int32_t)x
+#define CMD_SET(c, x) c->tl_host_cmd |= (u_int32_t)x
+#define CMD_CLR(c, x) c->tl_host_cmd &= ~(u_int32_t)x
+
+/*
+ * ThunderLAN adapters typically have a serial EEPROM containing
+ * configuration information. The main reason we're interested in
+ * it is because it also contains the adapters's station address.
+ *
+ * Access to the EEPROM is a bit goofy since it is a serial device:
+ * you have to do reads and writes one bit at a time. The state of
+ * the DATA bit can only change while the CLOCK line is held low.
+ * Transactions work basically like this:
+ *
+ * 1) Send the EEPROM_START sequence to prepare the EEPROM for
+ * accepting commands. This pulls the clock high, sets
+ * the data bit to 0, enables transmission to the EEPROM,
+ * pulls the data bit up to 1, then pulls the clock low.
+ * The idea is to do a 0 to 1 transition of the data bit
+ * while the clock pin is held high.
+ *
+ * 2) To write a bit to the EEPROM, set the TXENABLE bit, then
+ * set the EDATA bit to send a 1 or clear it to send a 0.
+ * Finally, set and then clear ECLOK. Strobing the clock
+ * transmits the bit. After 8 bits have been written, the
+ * EEPROM should respond with an ACK, which should be read.
+ *
+ * 3) To read a bit from the EEPROM, clear the TXENABLE bit,
+ * then set ECLOK. The bit can then be read by reading EDATA.
+ * ECLOCK should then be cleared again. This can be repeated
+ * 8 times to read a whole byte, after which the
+ *
+ * 4) We need to send the address byte to the EEPROM. For this
+ * we have to send the write control byte to the EEPROM to
+ * tell it to accept data. The byte is 0xA0. The EEPROM should
+ * ack this. The address byte can be send after that.
+ *
+ * 5) Now we have to tell the EEPROM to send us data. For that we
+ * have to transmit the read control byte, which is 0xA1. This
+ * byte should also be acked. We can then read the data bits
+ * from the EEPROM.
+ *
+ * 6) When we're all finished, send the EEPROM_STOP sequence.
+ *
+ * Note that we use the ThunderLAN's NetSio register to access the
+ * EEPROM, however there is an alternate method. There is a PCI NVRAM
+ * register at PCI offset 0xB4 which can also be used with minor changes.
+ * The difference is that access to PCI registers via pci_conf_read()
+ * and pci_conf_write() is done using programmed I/O, which we want to
+ * avoid.
+ */
+
+/*
+ * Note that EEPROM_START leaves transmission enabled.
+ */
+#define EEPROM_START \
+ DIO_SEL(TL_NETSIO); \
+ DIO_BYTE1_SET(TL_SIO_ECLOK); /* Pull clock pin high */ \
+ DIO_BYTE1_SET(TL_SIO_EDATA); /* Set DATA bit to 1 */ \
+ DIO_BYTE1_SET(TL_SIO_ETXEN); /* Enable xmit to write bit */ \
+ DIO_BYTE1_CLR(TL_SIO_EDATA); /* Pull DATA bit to 0 again */ \
+ DIO_BYTE1_CLR(TL_SIO_ECLOK); /* Pull clock low again */
+
+/*
+ * EEPROM_STOP ends access to the EEPROM and clears the ETXEN bit so
+ * that no further data can be written to the EEPROM I/O pin.
+ */
+#define EEPROM_STOP \
+ DIO_SEL(TL_NETSIO); \
+ DIO_BYTE1_CLR(TL_SIO_ETXEN); /* Disable xmit */ \
+ DIO_BYTE1_CLR(TL_SIO_EDATA); /* Pull DATA to 0 */ \
+ DIO_BYTE1_SET(TL_SIO_ECLOK); /* Pull clock high */ \
+ DIO_BYTE1_SET(TL_SIO_ETXEN); /* Enable xmit */ \
+ DIO_BYTE1_SET(TL_SIO_EDATA); /* Toggle DATA to 1 */ \
+ DIO_BYTE1_CLR(TL_SIO_ETXEN); /* Disable xmit. */ \
+ DIO_BYTE1_CLR(TL_SIO_ECLOK); /* Pull clock low again */
+
#define TL_DIO_ADDR_INC 0x8000 /* Increment addr on each read */
#define TL_DIO_RAM_SEL 0x4000 /* RAM address select */
@@ -444,7 +540,8 @@ struct tl_csr {
#define TL_EEPROM_EADDR 0x83
#define TL_EEPROM_EADDR2 0x99
#define TL_EEPROM_EADDR3 0xAF
-
+#define TL_EEPROM_EADDR_OC 0xF8 /* Olicom cards use a different
+ address than Compaqs. */
/*
* ThunderLAN host command register offsets.
* (Can be accessed either by IO ports or memory map.)
OpenPOWER on IntegriCloud