diff options
author | Remy Bohmer <linux@bohmer.net> | 2008-06-03 15:26:25 +0200 |
---|---|---|
committer | Ben Warren <biggerbadderben@gmail.com> | 2008-06-04 23:47:31 -0700 |
commit | 850ba7555dbd4ca8d14fc475b864d534797adab3 (patch) | |
tree | 497c9dc4c0e24539b998c86f5234c6de602eef89 /drivers | |
parent | fbcb7ece0ea1e364180f1cf963e0fa0ce7f6560d (diff) |
DM9000: Make driver work properly for DM9000A
The DM9000A network controller does not work with the U-boot DM9000x driver.
Analysis showed that many incoming packets are lost.
The DM9000A Application Notes V1.20 (section 5.6.1) recommend that the poll to
check for a valid rx packet be done on the interrupt status register, not
directly by performing the dummy read and the rx status check as is currently
the case in the u-boot driver.
When the recommended poll is done as suggested the driver starts working
correctly on 10Mbit/HD, but on 100MBit/FD packets come in faster so that there
can be more than 1 package in the fifo at the same time.
The driver must perform the rx-status check in a loop and read and handle all
packages until there is no more left _after_ the interrupt RX flag is set.
This change has been tested with DM9000A, DM9000E, DM9000EP.
Signed-off-by: Remy Bohmer <linux@bohmer.net>
Signed-off-by: Ben Warren <biggerbadderben@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/dm9000x.c | 93 |
1 files changed, 54 insertions, 39 deletions
diff --git a/drivers/net/dm9000x.c b/drivers/net/dm9000x.c index 5b00e9588fe..08248f44072 100644 --- a/drivers/net/dm9000x.c +++ b/drivers/net/dm9000x.c @@ -40,6 +40,10 @@ v1.2 03/18/2003 Weilun Huang <weilun_huang@davicom.com.tw>: Sascha Hauer <saschahauer@web.de> 06/03/2008 Remy Bohmer <linux@bohmer.net> + - Fixed the driver to work with DM9000A. + (check on ISR receive status bit before reading the + FIFO as described in DM9000 programming guide and + application notes) - Added autodetect of databus width. - Made debug code compile again. - Adapt eth_send such that it matches the DM9000* @@ -598,53 +602,64 @@ eth_rx(void) u16 RxStatus, RxLen = 0; struct board_info *db = &dm9000_info; - /* Check packet ready or not */ - DM9000_ior(DM9000_MRCMDX); /* Dummy read */ - rxbyte = DM9000_inb(DM9000_DATA); /* Got most updated data */ - if (rxbyte == 0) + /* Check packet ready or not, we must check + the ISR status first for DM9000A */ + if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */ return 0; - /* Status check: this byte must be 0 or 1 */ - if (rxbyte > 1) { - DM9000_iow(DM9000_RCR, 0x00); /* Stop Device */ - DM9000_iow(DM9000_ISR, 0x80); /* Stop INT request */ - DM9000_DBG("rx status check: %d\n", rxbyte); - } - DM9000_DBG("receiving packet\n"); + DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */ - /* A packet ready now & Get status/length */ - DM9000_outb(DM9000_MRCMD, DM9000_IO); + /* There is _at least_ 1 package in the fifo, read them all */ + for (;;) { + DM9000_ior(DM9000_MRCMDX); /* Dummy read */ - (db->rx_status)(&RxStatus, &RxLen); + /* Get most updated data */ + rxbyte = DM9000_inb(DM9000_DATA); - DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen); + /* Status check: this byte must be 0 or 1 */ + if (rxbyte > DM9000_PKT_RDY) { + DM9000_iow(DM9000_RCR, 0x00); /* Stop Device */ + DM9000_iow(DM9000_ISR, 0x80); /* Stop INT request */ + printf("DM9000 error: status check fail: 0x%x\n", + rxbyte); + return 0; + } - /* Move data from DM9000 */ - /* Read received packet from RX SRAM */ - (db->inblk)(rdptr, RxLen); + if (rxbyte != DM9000_PKT_RDY) + return 0; /* No packet received, ignore */ + + DM9000_DBG("receiving packet\n"); + + /* A packet ready now & Get status/length */ + (db->rx_status)(&RxStatus, &RxLen); + + DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen); + + /* Move data from DM9000 */ + /* Read received packet from RX SRAM */ + (db->inblk)(rdptr, RxLen); + + if ((RxStatus & 0xbf00) || (RxLen < 0x40) + || (RxLen > DM9000_PKT_MAX)) { + if (RxStatus & 0x100) { + printf("rx fifo error\n"); + } + if (RxStatus & 0x200) { + printf("rx crc error\n"); + } + if (RxStatus & 0x8000) { + printf("rx length error\n"); + } + if (RxLen > DM9000_PKT_MAX) { + printf("rx length too big\n"); + dm9000_reset(); + } + } else { + DM9000_DMP_PACKET("eth_rx", rdptr, RxLen); - if ((RxStatus & 0xbf00) || (RxLen < 0x40) - || (RxLen > DM9000_PKT_MAX)) { - if (RxStatus & 0x100) { - printf("rx fifo error\n"); + DM9000_DBG("passing packet to upper layer\n"); + NetReceive(NetRxPackets[0], RxLen); } - if (RxStatus & 0x200) { - printf("rx crc error\n"); - } - if (RxStatus & 0x8000) { - printf("rx length error\n"); - } - if (RxLen > DM9000_PKT_MAX) { - printf("rx length too big\n"); - dm9000_reset(); - } - } else { - DM9000_DMP_PACKET("eth_rx", rdptr, RxLen); - - /* Pass to upper layer */ - DM9000_DBG("passing packet to upper layer\n"); - NetReceive(NetRxPackets[0], RxLen); - return RxLen; } return 0; } |