PR 429/bsps
	PR 432/bsps
	* network/elnk.c: Due to a bug in the driver, if multiple packets
	are queued onto an elnk board, the ISR will deadlock the device with
	a tx complete interrupt.  Lighter tx loads are not affected as they
	end up submitting single packets to the device.
This commit is contained in:
Joel Sherrill
2003-07-18 15:52:07 +00:00
parent 734d1c5da8
commit 552af999ba
2 changed files with 164 additions and 75 deletions

View File

@@ -1,3 +1,12 @@
2003-07-18 Greg Menke <gregory.menke@gsfc.nasa.gov>
PR 429/bsps
PR 432/bsps
* network/elnk.c: Due to a bug in the driver, if multiple packets
are queued onto an elnk board, the ISR will deadlock the device with
a tx complete interrupt. Lighter tx loads are not affected as they
end up submitting single packets to the device.
2003-06-30 Greg Menke <gregory.menke@gsfc.nasa.gov>
* network/dec21140.c, network/elnk.c: Update to compile on i386.

View File

@@ -70,11 +70,9 @@
* from being compiled on systems which can't support this driver.
*/
/*
#if defined(__i386__)
#define ELNK_SUPPORTED
#endif
*/
#if defined(__PPC__) && (defined(mpc604) || defined(mpc750) || defined(mpc603e))
#define ELNK_SUPPORTED
@@ -116,8 +114,12 @@
#if defined(__i386__)
#include <irq.h>
#define IO_MASK 0x3
#define MEM_MASK 0xF
#endif
#if defined(__PPC)
#if defined(__PPC__)
#include <bsp/irq.h>
#endif
@@ -191,7 +193,7 @@ static rtems_event_set unit_signals[NUM_UNITS]= { RTEMS_EVENT_1,
#if defined(__PPC)
#if defined(__PPC__)
#define phys_to_bus(address) ((unsigned int)((address)) + PCI_DRAM_OFFSET)
#define bus_to_phys(address) ((unsigned int)((address)) - PCI_DRAM_OFFSET)
#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PPC_CACHE_ALIGNMENT
@@ -780,6 +782,8 @@ struct xl_stats
u_int8_t txstatus;
u_int16_t mediastatus;
u_int32_t txcomplete_ints;
u_int16_t miianr, miipar, miistatus, miicmd;
u_int32_t device_interrupts;
@@ -864,20 +868,6 @@ static struct xl_type xl_devs[] = {
};
#define CSR_WRITE_4(sc, reg, val) outl( val, sc->ioaddr + reg)
#define CSR_WRITE_2(sc, reg, val) outw( val, sc->ioaddr + reg)
#define CSR_WRITE_1(sc, reg, val) outb( val, sc->ioaddr + reg)
#define CSR_READ_4(sc, reg) inl(sc->ioaddr + reg)
#define CSR_READ_2(sc, reg) inw(sc->ioaddr + reg)
#define CSR_READ_1(sc, reg) inb(sc->ioaddr + reg)
#define XL_SEL_WIN(x) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_WINSEL | x)
#define XL_TIMEOUT 1000
@@ -902,7 +892,7 @@ struct RXMD
#define NUM_FRAGS 8
#define NUM_FRAGS 6
/*
* tx message descriptor entry, ensure the struct is aligned to 8 bytes
@@ -984,6 +974,56 @@ static rtems_id chainRecoveryQueue;
#if defined(__i386__)
#define CSR_WRITE_4(sc, reg, val) i386_outport_long( sc->ioaddr + reg, val )
#define CSR_WRITE_2(sc, reg, val) i386_outport_word( sc->ioaddr + reg, val )
#define CSR_WRITE_1(sc, reg, val) i386_outport_byte( sc->ioaddr + reg, val )
inline unsigned int CSR_READ_4( struct elnk_softc *sc, int reg)
{
unsigned int myval;
i386_inport_long( sc->ioaddr + reg, myval );
return myval;
}
inline unsigned short CSR_READ_2( struct elnk_softc *sc, int reg)
{
unsigned short myval;
i386_inport_word( sc->ioaddr + reg, myval );
return myval;
}
inline unsigned char CSR_READ_1( struct elnk_softc *sc, int reg)
{
unsigned char myval;
i386_inport_byte( sc->ioaddr + reg, myval );
return myval;
}
#endif
#if defined(__PPC__)
#define CSR_WRITE_4(sc, reg, val) outl( val, sc->ioaddr + reg)
#define CSR_WRITE_2(sc, reg, val) outw( val, sc->ioaddr + reg)
#define CSR_WRITE_1(sc, reg, val) outb( val, sc->ioaddr + reg)
#define CSR_READ_4(sc, reg) inl(sc->ioaddr + reg)
#define CSR_READ_2(sc, reg) inw(sc->ioaddr + reg)
#define CSR_READ_1(sc, reg) inb(sc->ioaddr + reg)
#endif
#define XL_SEL_WIN(x) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_WINSEL | x)
@@ -1904,15 +1944,42 @@ elnk_interrupt_handler ( struct elnk_softc *sc )
rtems_event_send(rxDaemonTid, sc->ioevent);
}
if (status & XL_STAT_DOWN_COMPLETE)
if( (status & XL_STAT_DOWN_COMPLETE) || (status & XL_STAT_TX_COMPLETE) )
{
/* all packets uploaded to the device */
struct TXMD *chaintailmd = NULL;
if( status & XL_STAT_TX_COMPLETE )
{
/* if we got a tx complete error, count it, then reset the
transmitter. Consider the entire chain lost.. */
ifp->if_oerrors++;
sc->xl_stats.txcomplete_ints++;
printk("etherlink : unit elnk%d transmit error\n", sc->xl_unit );
/* reset, re-enable fifo */
xl_wait(sc);
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
xl_wait(sc);
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET | 1 );
xl_wait(sc);
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
xl_wait(sc);
}
/* send the chain head to the tx task which will recover the
whole chain */
rtems_message_queue_send( chainRecoveryQueue, &sc->last_txchain_head, sizeof(struct TXMD *));
/* set up the next chain */
if( sc->last_txchain_head->chainptr )
{
@@ -1937,11 +2004,11 @@ elnk_interrupt_handler ( struct elnk_softc *sc )
/*
** otherwise, this is a pointer to the last packet in the
** chain of 2 or more packets. If the chain's last
** packet's mbuf is != 0, then another chain is ready to
** send.
** packet's chainptr is != 0, then another chain is ready
** to send.
*/
chaintailmd = sc->last_txchain_head->chainptr;
if( !chaintailmd->mbuf ) chaintailmd = NULL;
if( !chaintailmd->chainptr ) chaintailmd = NULL;
}
}
@@ -1965,25 +2032,6 @@ elnk_interrupt_handler ( struct elnk_softc *sc )
}
if (status & XL_STAT_TX_COMPLETE)
{
ifp->if_oerrors++;
{
unsigned32 txstat;
XL_SEL_WIN(1);
txstat = CSR_READ_1(sc, XL_W1_TX_STATUS );
XL_SEL_WIN(7);
printk("etherlink : unit elnk%d transmit error, txstat = %02x \n", sc->xl_unit, txstat );
xl_wait(sc);
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
xl_wait(sc);
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
xl_wait(sc);
}
}
if (status & XL_STAT_ADFAIL)
{
printk("etherlink : unit elnk%d Catastrophic bus failure\n", sc->xl_unit );
@@ -2316,10 +2364,6 @@ elnk_rxDaemon (void *arg)
/*
* Driver transmit daemon
*/
@@ -2354,6 +2398,7 @@ elnk_txDaemon (void *arg)
/*
* Send packets till queue is empty or tx ring is full
*/
chainCount = 0;
firstmd = NULL;
@@ -2361,12 +2406,12 @@ elnk_txDaemon (void *arg)
for(;;)
{
/*
** Check the chain recovery queue whenever the tx
** daemon wakes up. Note this routine does not assume
** the context of one of the lanboard units because
** used tx mbufs no longer associated with any unit.
** daemon services the stack. Note this routine does
** not assume the context of one of the lanboard units
** because used tx mbufs are no longer associated with
** any unit.
*/
{
struct TXMD *chainhead, *chaintail;
@@ -2385,6 +2430,7 @@ elnk_txDaemon (void *arg)
for(;;)
{
m_freem( chainhead->mbuf );
st_le32( &chainhead->status, XL_TXSTAT_DL_COMPLETE );
chainhead->mbuf = NULL;
if( chainhead == chaintail ) break;
@@ -2395,21 +2441,27 @@ elnk_txDaemon (void *arg)
{
/* a single packet chain */
m_freem( chainhead->mbuf );
st_le32( &chainhead->status, XL_TXSTAT_DL_COMPLETE );
chainhead->mbuf = NULL;
}
}
}
nextmd = lastmd->next_md;
/* stop when ring is full */
if( nextmd->mbuf || ! (ld_le32(&nextmd->status) & XL_TXSTAT_DL_COMPLETE) )
if( ! (ld_le32(&nextmd->status) & XL_TXSTAT_DL_COMPLETE) )
{
printk("etherlink : unit elnk%d tx ring full!\n", sc->xl_unit);
break;
}
/* sanity check the next packet descriptor */
if( nextmd->mbuf )
{
printk("etherlink : unit elnk%d tx ring corrupt!\n", sc->xl_unit);
break;
}
IF_DEQUEUE(&ifp->if_snd, m);
@@ -2437,7 +2489,7 @@ elnk_txDaemon (void *arg)
#if 0
{
char *pkt = bus_to_phys( ld_le32( &nextmd->txfrags[i].addr ), *delim;
char *pkt = bus_to_phys( ld_le32( &nextmd->txfrags[i].addr )), *delim;
int i;
printk("unit %d queued pkt (%08x) ", sc->xl_unit, (unsigned32)pkt );
for(delim="", i=0; i < sizeof(struct ether_header); i++, delim=":")
@@ -2479,9 +2531,11 @@ elnk_txDaemon (void *arg)
if( firstmd )
{
/* only entered if we've queued one or more packets */
/* only enter if we've queued one or more packets */
/* save the last descriptor we set up in the chain */
sc->last_tx_md = lastmd;
@@ -2494,11 +2548,11 @@ elnk_txDaemon (void *arg)
st_le32( &lastmd->status, XL_TXSTAT_DL_INTR );
/*
* point the chain head's mbuf to the tail so we can
* locate the next chain to send inside the isr. If
* we're only sending one packet, then don't bother
* with the link, as the chainptr value will either be 0
* if theres no next chain or -1 if there is.
* point the chain head's chainptr to the tail so we
* can jump to the next chain to send inside the isr.
* If we're only sending one packet, then don't bother
* with the link, as the chainptr value will either be
* 0 if theres no next chain or -1 if there is.
*/
if( chainCount > 1 )
{
@@ -2510,9 +2564,9 @@ elnk_txDaemon (void *arg)
/*
** clear the last packet's chainptr flag. If another
** chain is added later, before this chain is finished
** being sent, the flag on this packet will be re-set
** to -1
** chain is added later but before this chain is
** finished being sent, this flag on this packet will
** be re-set to -1
*/
lastmd->chainptr = NULL;
@@ -2543,6 +2597,7 @@ elnk_txDaemon (void *arg)
}
}
ifp->if_flags &= ~IFF_OACTIVE;
}
}
@@ -2939,8 +2994,9 @@ elnk_stats (struct elnk_softc *sc)
}
}
printf(" interrupts:%-9d avg_chain_len:%-5d\n",
printf(" interrupts:%-9d txcmp_ints:%-5d avg_chain_len:%-4d\n",
sc->xl_stats.device_interrupts,
sc->xl_stats.txcomplete_ints,
(totalLengths / numLengths) );
}
@@ -3078,10 +3134,17 @@ rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
int unitNumber;
int mtu, i;
unsigned char cvalue;
int pbus, pdev, pfun;
unsigned int lvalue;
struct el_boards sysboards[NUM_UNITS];
int numFound = 0;
int pbus, pdev, pfun;
#if defined(__i386__)
int signature;
int value;
char interrupt;
#endif
#if defined(__PPC__)
unsigned int lvalue;
#endif
/*
@@ -3116,12 +3179,10 @@ rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
*/
for( i=0; !done && xl_devs[i].xl_vid; i++)
{
for(unum= 1;
!done && BSP_pciFindDevice( xl_devs[i].xl_vid, xl_devs[i].xl_did, unum-1,
for(unum= 1; !done && BSP_pciFindDevice( xl_devs[i].xl_vid, xl_devs[i].xl_did, unum-1,
&sysboards[numFound].pbus,
&sysboards[numFound].pdev,
&sysboards[numFound].pfun)==0;
unum++)
&sysboards[numFound].pfun)==0; unum++)
{
if( numFound == NUM_UNITS )
{
@@ -3186,6 +3247,9 @@ rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
pbus = sysboards[unitNumber-1].pbus;
pdev = sysboards[unitNumber-1].pdev;
pfun = sysboards[unitNumber-1].pfun;
#if defined(__i386__)
signature = PCIB_DEVSIG_MAKE(pbus,pdev,pfun);
#endif
}
sc = &elnk_softc[unitNumber - 1];
@@ -3252,6 +3316,14 @@ rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
sc->ioevent = unit_signals[unitNumber-1];
#if defined(__i386__)
pcib_conf_read32(signature, 16, &value);
sc->ioaddr = value & ~IO_MASK;
pcib_conf_read8(signature, 60, &interrupt);
cvalue = interrupt;
#endif
#if defined(__PPC__)
/*
** Prep the board
*/
@@ -3261,17 +3333,14 @@ rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
PCI_COMMAND_MASTER |
PCI_COMMAND_INVALIDATE |
PCI_COMMAND_WAIT ) );
/*
* Get the devices base address
* Get the device's base address
*/
pci_read_config_dword(pbus, pdev, pfun,
PCI_BASE_ADDRESS_0,
&lvalue);
sc->ioaddr = (unsigned32)lvalue & PCI_BASE_ADDRESS_IO_MASK;
/*
** Store the interrupt name, we'll use it later when we initialize
** the board.
@@ -3279,6 +3348,7 @@ rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
pci_read_config_byte(pbus, pdev, pfun,
PCI_INTERRUPT_LINE,
&cvalue);
#endif
memset(&sc->irqInfo,0,sizeof(rtems_irq_connect_data));
sc->irqInfo.name = cvalue;
@@ -3297,11 +3367,21 @@ rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
must be set to the maximum value to avoid data corruption that occurs
when the timer expires during a transfer. This bug exists the Vortex
chip only. */
#if defined(__i386__)
pcib_conf_read8(signature, 0x0d, &pci_latency);
#endif
#if defined(__PPC__)
pci_read_config_byte(pbus,pdev,pfun, PCI_LATENCY_TIMER, &pci_latency);
#endif
if (pci_latency < new_latency)
{
printk("etherlink : unit elnk%d Overriding PCI latency, timer (CFLT) setting of %d, new value is %d.\n", sc->xl_unit, pci_latency, new_latency );
#if defined(__i386__)
pcib_conf_write8(signature, 0x0d, new_latency);
#endif
#if defined(__PPC__)
pci_write_config_byte(pbus,pdev,pfun, PCI_LATENCY_TIMER, new_latency);
#endif
}
}