Declare pointers to CSR register space to be volatile. This seems to

cure the problems I was having with interrupts not being acknowledged
on time. This fixes a problem I observed where starting two ping -f
processes at 10Mbps would cause an adapter check due to TX GO commands
being issued before TXEOC interrupts were being acked.

Also fix a small problem with tl_start(): the mechanism I was using
to queue new packets onto the TX chain was bogus.

Change adapter check handler so that it resets card state after
tl_softreset() is stored.

Moved all EEPROM-related macro definitions into if_tlreg.h.

Don't allow an autoneg session to start until after the TX queue has
been drained, and don't transmit anything until after the autoneg
session is complete.

Also add support for two more Compaq ThunderLAN-based cards, and three
cards from Olicom which also use the ThunderLAN chip. The only thing
different about the Olicom cards is that they store the station address
at a different location within the EEPROM.
This commit is contained in:
Bill Paul 1998-07-13 18:15:48 +00:00
parent 5914e1ea80
commit fb1305c426
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=37626
2 changed files with 273 additions and 160 deletions

View File

@ -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 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((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 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 @@ static int tl_mii_readreg(csr, frame)
}
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.

View File

@ -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.)