lance: perform full reinitialization for restarts
When performing a restart (CSR0 STOP, STRT), the behavior regarding the NIC's current RX/TX descriptor ring counters varies between cards: older LANCE cards do not reset the counters; newer PCnet cards do reset them; VirtualBox's emulation is once again broken in that it claims to emulate newer cards but implements the older behavior. Changing the card's receive mode requires such a restart, and now that the system can actually change receive modes dynamically as part of normal network operation, this results in the lance driver breaking all the time on at least VirtualBox. Instead of trying to figure out exactly what is going on with the counters during a restart, we now simply perform a full-blown reinitialization every time the NIC is restarted. That leaves no ambiguity regarding the counters, and appears to be what drivers on other OSes do as well. As a bonus, this approach actually saves code. Change-Id: I60fad2df6de4616d5de2cec39c09b60c15d854fb
This commit is contained in:
parent
f7df02e747
commit
47db417b1a
@ -27,7 +27,6 @@ static int do_init(unsigned int instance, netdriver_addr_t *addr,
|
|||||||
uint32_t *caps, unsigned int *ticks);
|
uint32_t *caps, unsigned int *ticks);
|
||||||
static void ec_confaddr(netdriver_addr_t *addr, unsigned int instance);
|
static void ec_confaddr(netdriver_addr_t *addr, unsigned int instance);
|
||||||
static void ec_reinit(ether_card_t *ec);
|
static void ec_reinit(ether_card_t *ec);
|
||||||
static void ec_reset(ether_card_t *ec);
|
|
||||||
static void do_intr(unsigned int mask);
|
static void do_intr(unsigned int mask);
|
||||||
static void do_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
|
static void do_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
|
||||||
unsigned int mcast_count);
|
unsigned int mcast_count);
|
||||||
@ -299,26 +298,24 @@ static int do_init(unsigned int instance, netdriver_addr_t *addr,
|
|||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
static void ec_reinit(ether_card_t *ec)
|
static void ec_reinit(ether_card_t *ec)
|
||||||
{
|
{
|
||||||
|
spin_t spin;
|
||||||
int i;
|
int i;
|
||||||
unsigned short ioaddr = ec->ec_port;
|
unsigned short ioaddr = ec->ec_port;
|
||||||
|
|
||||||
/* stop */
|
/* stop */
|
||||||
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
|
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
|
||||||
|
/* init */
|
||||||
/* purge Tx-ring */
|
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_INIT);
|
||||||
tx_slot_nr = cur_tx_slot_nr = 0;
|
/* poll for IDON */
|
||||||
for (i=0; i<TX_RING_SIZE; i++)
|
SPIN_FOR(&spin, 1000) {
|
||||||
{
|
if (read_csr(ioaddr, LANCE_CSR0) & LANCE_CSR0_IDON)
|
||||||
lp->tx_ring[i].u.base = 0;
|
break;
|
||||||
isstored[i]=0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* re-init Rx-ring */
|
/* Set 'Multicast Table' */
|
||||||
rx_slot_nr = 0;
|
for (i=0;i<4;++i)
|
||||||
for (i=0; i<RX_RING_SIZE; i++)
|
|
||||||
{
|
{
|
||||||
lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
|
write_csr(ioaddr, LANCE_CSR8 + i, 0xffff);
|
||||||
lp->rx_ring[i].u.addr[3] |= 0x80;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set 'Receive Mode' */
|
/* Set 'Receive Mode' */
|
||||||
@ -339,6 +336,23 @@ static void ec_reinit(ether_card_t *ec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* purge Tx-ring */
|
||||||
|
tx_slot_nr = cur_tx_slot_nr = 0;
|
||||||
|
for (i=0; i<TX_RING_SIZE; i++)
|
||||||
|
{
|
||||||
|
lp->tx_ring[i].u.base = 0;
|
||||||
|
isstored[i]=0;
|
||||||
|
}
|
||||||
|
cur_tx_slot_nr = tx_slot_nr;
|
||||||
|
|
||||||
|
/* re-init Rx-ring */
|
||||||
|
rx_slot_nr = 0;
|
||||||
|
for (i=0; i<RX_RING_SIZE; i++)
|
||||||
|
{
|
||||||
|
lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
|
||||||
|
lp->rx_ring[i].u.addr[3] |= 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
/* start && enable interrupt */
|
/* start && enable interrupt */
|
||||||
write_csr(ioaddr, LANCE_CSR0,
|
write_csr(ioaddr, LANCE_CSR0,
|
||||||
LANCE_CSR0_IDON|LANCE_CSR0_IENA|LANCE_CSR0_STRT);
|
LANCE_CSR0_IDON|LANCE_CSR0_IENA|LANCE_CSR0_STRT);
|
||||||
@ -482,7 +496,10 @@ static void do_intr(unsigned int __unused mask)
|
|||||||
printf("ETH: restarting...\n");
|
printf("ETH: restarting...\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ec_reset(ec);
|
ec_reinit(ec);
|
||||||
|
|
||||||
|
/* store a buffered message on the slot if it exists */
|
||||||
|
netdriver_send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,45 +508,6 @@ static void do_intr(unsigned int __unused mask)
|
|||||||
panic("couldn't enable interrupt: %d", r);
|
panic("couldn't enable interrupt: %d", r);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*===========================================================================*
|
|
||||||
* ec_reset *
|
|
||||||
*===========================================================================*/
|
|
||||||
static void ec_reset(ether_card_t *ec)
|
|
||||||
{
|
|
||||||
/* Stop/start the chip, and clear all RX,TX-slots. The spec says this type
|
|
||||||
* of reset should be done on MERR, UFLO, and TX BUFF errors. For now it is
|
|
||||||
* called only for UFLO and TX BUFF (which causes UFLO). MERR is not (yet?)
|
|
||||||
* handled. Also note that the PCnet spec says that the LANCE chip does not
|
|
||||||
* require resetting Rx/Tx, but PCnet does. We intend to support both here.
|
|
||||||
*/
|
|
||||||
unsigned short ioaddr = ec->ec_port;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* stop */
|
|
||||||
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
|
|
||||||
/* start */
|
|
||||||
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STRT);
|
|
||||||
|
|
||||||
/* purge Tx-ring */
|
|
||||||
tx_slot_nr = cur_tx_slot_nr = 0;
|
|
||||||
for (i=0; i<TX_RING_SIZE; i++)
|
|
||||||
{
|
|
||||||
lp->tx_ring[i].u.base = 0;
|
|
||||||
isstored[i]=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* re-init Rx-ring */
|
|
||||||
rx_slot_nr = 0;
|
|
||||||
for (i=0; i<RX_RING_SIZE; i++)
|
|
||||||
{
|
|
||||||
lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
|
|
||||||
lp->rx_ring[i].u.addr[3] |= 0x80;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store a buffered message on the slot if it exists */
|
|
||||||
netdriver_send();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
* do_recv *
|
* do_recv *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
@ -843,38 +821,7 @@ static void lance_init_hw(ether_card_t *ec, netdriver_addr_t *addr,
|
|||||||
|LANCE_CSR4_TXSTRTM|LANCE_CSR4_JABM);
|
|LANCE_CSR4_TXSTRTM|LANCE_CSR4_JABM);
|
||||||
|
|
||||||
/* ----- start when init done. ----- */
|
/* ----- start when init done. ----- */
|
||||||
/* stop */
|
ec_reinit(ec);
|
||||||
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
|
|
||||||
/* init */
|
|
||||||
write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_INIT);
|
|
||||||
/* poll for IDON */
|
|
||||||
for (i = 10000; i > 0; --i)
|
|
||||||
if (read_csr(ioaddr, LANCE_CSR0) & LANCE_CSR0_IDON)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Set 'Multicast Table' */
|
|
||||||
for (i=0;i<4;++i)
|
|
||||||
{
|
|
||||||
write_csr(ioaddr, LANCE_CSR8 + i, 0xffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set 'Receive Mode' */
|
|
||||||
if (ec->ec_mode & NDEV_MODE_PROMISC)
|
|
||||||
{
|
|
||||||
write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_PROM);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ec->ec_mode &
|
|
||||||
(NDEV_MODE_BCAST | NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
|
|
||||||
{
|
|
||||||
write_csr(ioaddr, LANCE_CSR15, 0x0000);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_DRCVBC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the interrupt handler */
|
/* Set the interrupt handler */
|
||||||
ec->ec_hook = ec->ec_irq;
|
ec->ec_hook = ec->ec_irq;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user