I'm going to post this patch to the etherboot mailing list but it is relevant here as well. This is our current patch against etherboot 5.0.10 to fix various problems: 1) RTL8139 interrupt handling was wrong 2) VIA-RHINE support needs workaround for the 3065 chip version. When resetting the chip, the act of shutting down the receive machinery can cause a problem, and when the chip is reset it never gets out of reset. So etherboot would hang when trying to send out a packet, such as a bootp request. This fix is from via's linuxfet driver. Google searches tracked down other people with the same problem and the eventual solution. 3) Some issue with initializing the timer, which without it was causing a divide by zero at some point.
The #2 was an especially bad problem and affected our epia-m solution. The boxes would stop sending out BOOTP requests after a while.
-Dave
diff -Nur etherboot-5.0.10/src/misc.c etherboot-5.0.10-new/src/misc.c --- etherboot-5.0.10/src/misc.c Sun Mar 9 03:17:15 2003 +++ etherboot-5.0.10-new/src/misc.c Wed Jul 16 09:45:26 2003 @@ -282,7 +282,7 @@ enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402, Query_A20_Support = 0x2403 } Int0x15Arg;
-#if defined(PCBIOS) && !defined(IBM_L40) +#if defined(TAGGED_IMAGE) || (defined(PCBIOS) && !defined(IBM_L40)) static void empty_8042(void) { unsigned long time; diff -Nur etherboot-5.0.10/src/rtl8139.c etherboot-5.0.10-new/src/rtl8139.c --- etherboot-5.0.10/src/rtl8139.c Tue Jul 23 17:50:53 2002 +++ etherboot-5.0.10-new/src/rtl8139.c Wed Jul 16 09:45:42 2003 @@ -183,7 +183,6 @@ static int rtl_poll(struct nic *nic); static void rtl_disable(struct nic*);
- struct nic *rtl8139_probe(struct nic *nic, unsigned short *probeaddrs, struct pci_device *pci) { @@ -252,6 +251,16 @@ #define EE_READ_CMD (6) #define EE_ERASE_CMD (7)
+static unsigned short cintr=0; +static unsigned short getstatus(void) +{ + return cintr|=inw(ioaddr + IntrStatus); +} +static void setstatus(unsigned short val) +{ + cintr&=~val; +} + static int read_eeprom(int location, int addr_len) { int i; @@ -340,7 +349,7 @@ static void rtl_transmit(struct nic *nic, const char *destaddr, unsigned int type, unsigned int len, const char *data) { - unsigned int status, to, nstype; + unsigned int status, to, nstype, st; unsigned long txstatus;
/* nstype assignment moved up here to avoid gcc 3.0.3 compiler bug */ @@ -365,14 +374,15 @@ outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | len, ioaddr + TxStatus0 + cur_tx*4);
+ st= currticks(); to = currticks() + RTL_TIMEOUT;
do { - status = inw(ioaddr + IntrStatus); + status = getstatus(); /* Only acknlowledge interrupt sources we can properly handle * here - the RxOverflow/RxFIFOOver MUST be handled in the * rtl_poll() function. */ - outw(status & (TxOK | TxErr | PCIErr), ioaddr + IntrStatus); + setstatus(status & (TxOK | TxErr | PCIErr)); if ((status & (TxOK | TxErr | PCIErr)) != 0) break; } while (currticks() < to);
@@ -382,12 +392,12 @@ cur_tx = (cur_tx + 1) % NUM_TX_DESC; #ifdef DEBUG_TX printf("tx done (%d ticks), status %hX txstatus %X\n", - to-currticks(), status, txstatus); + currticks()-st, status, txstatus); #endif } else { #ifdef DEBUG_TX printf("tx timeout/error (%d ticks), status %hX txstatus %X\n", - currticks()-to, status, txstatus); + currticks()-st, status, txstatus); #endif rtl_reset(nic); } @@ -403,9 +413,9 @@ return 0; }
- status = inw(ioaddr + IntrStatus); + status = getstatus(); /* See below for the rest of the interrupt acknowledges. */ - outw(status & ~(RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus); + setstatus(status & ~(RxFIFOOver | RxOverflow | RxOK));
#ifdef DEBUG_RX printf("rtl_poll: int %hX ", status); @@ -449,7 +459,7 @@ /* See RTL8139 Programming Guide V0.1 for the official handling of * Rx overflow situations. The document itself contains basically no * usable information, except for a few exception handling rules. */ - outw(status & (RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus); + setstatus(status & (RxFIFOOver | RxOverflow | RxOK)); return 1; }
diff -Nur etherboot-5.0.10/src/timer.c etherboot-5.0.10-new/src/timer.c --- etherboot-5.0.10/src/timer.c Sat Mar 22 11:52:25 2003 +++ etherboot-5.0.10-new/src/timer.c Wed Jul 16 09:45:46 2003 @@ -138,6 +138,8 @@ { unsigned long clocks_high, clocks_low; unsigned long currticks; + +setup_timers(); /* Read the Time Stamp Counter */ rdtsc(clocks_low, clocks_high);
--- etherboot-5.0.10/src/Makefile 2003-03-09 17:39:21.000000000 -0800 +++ etherboot-5.0.10/src/Makefile.new 2003-11-06 06:22:26.000000000 -0800 @@ -98,8 +98,8 @@
include Config
-GCC= gcc -CPP= gcc -E -Wp,-Wall +GCC= $(CC) +CPP= $(CC) -E -Wp,-Wall STRIP= strip OBJCOPY= objcopy # MAKEROM= bin/makerom diff -Nur etherboot-5.0.10/src/via-rhine.c etherboot-5.0.10-new/src/via-rhine.c --- etherboot-5.0.10/src/via-rhine.c 2003-02-07 15:07:56.000000000 -0800 +++ etherboot-5.0.10-new/src/via-rhine.c 2004-02-06 07:24:50.000000000 -0800 @@ -106,6 +106,10 @@ #define byCFGD ioaddr + 0x7b #define wTallyCntMPA ioaddr + 0x7c #define wTallyCntCRC ioaddr + 0x7d +#define bySTICKHW ioaddr + 0x83 +#define byWOLcrClr ioaddr + 0xA4 +#define byWOLcgClr ioaddr + 0xA7 +#define byPwrcsrClr ioaddr + 0xAC /*--------------------- Exioaddr Definitions -------------------------*/
/* @@ -873,6 +877,19 @@ return nic; }
+static void set_rx_mode(struct nic *nic) { + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + unsigned char rx_mode; + int ioaddr = tp->ioaddr; + + /* ! IFF_PROMISC */ + outl(0xffffffff, byMAR0); + outl(0xffffffff, byMAR4); + rx_mode = 0x0C; + + outb(0x60 /* thresh */ | rx_mode, byRCR ); +} + static struct nic * rhine_probe1 (struct nic *nic, int ioaddr, int chip_id, int options) { @@ -885,6 +902,29 @@
if (rhine_debug > 0 && did_version++ == 0) printf (version); + + /* D-Link provided reset code (with comment additions) */ + if((chip_id != 0x3043) && (chip_id != 0x6100)) { + unsigned char byOrgValue; + + if(rhine_debug > 0) + printf("Enabling Sticky Bit Workaround for Chip_id: 0x%hX\n" + , chip_id); + /* clear sticky bit before reset & read ethernet address */ + byOrgValue = inb(bySTICKHW); + byOrgValue = byOrgValue & 0xFC; + outb(byOrgValue, bySTICKHW); + + /* (bits written are cleared?) */ + /* disable force PME-enable */ + outb(0x80, byWOLcgClr); + /* disable power-event config bit */ + outb(0xFF, byWOLcrClr); + /* clear power status (undocumented in vt6102 docs?) */ + outb(0xFF, byPwrcsrClr); + + } + /* Perhaps this should be read from the EEPROM? */ for (i = 0; i < ETH_ALEN; i++) nic->node_addr[i] = inb (byPAR0 + i); @@ -980,6 +1020,9 @@ struct rhine_private *tp = (struct rhine_private *) nic->priv_data; int ioaddr = tp->ioaddr;
+ /* merge reset and disable */ + rhine_reset(nic); + printf ("rhine disable\n"); /* Switch to loopback mode to avoid hardware races. */ writeb(0x60 | 0x01, byTCR); @@ -1018,6 +1061,45 @@ /* Soft reset the chip. */ /*outb(CmdReset, ioaddr + ChipCmd); */
+ /* if the chip is 3065, we must patch shutdown bug*/ +// if ((np->revision >= 0x40) && (np->revision <= 0x80)) + { + int ww; + //Nic Loop Back On + + outb(inb(byTCR) | 0x01, byTCR); + + //Tx Off + outb(inb(byCR0) & 0xEF, byCR0); + +#define W_MAX_TIMEOUT 0x0FFF + // W_MAX_TIMEOUT is the timeout period + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + if((inb(byCR0) & 0x10) == 0) + break; + } + + //Rx Off + outb(inb(byCR0) & 0xF7, byCR0); + + // W_MAX_TIMEOUT is the timeout period + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + if((inb(byCR0) & 0x08) == 0) + break; + } + + if (ww == W_MAX_TIMEOUT) { + + // Turn on fifo test + outw(inw(dwNextTDSE1) | 0x0001, dwNextTDSE1); + // Turn on fifo reject + outw(inw(dwNextTDSE1) | 0x0800, dwNextTDSE1); + // Turn off fifo test + outw(inw(dwNextTDSE1) & 0xFFFE, dwNextTDSE1); + } + } + + tx_bufs_tmp = (int) buf1; tx_ring_tmp = (int) desc1; rx_bufs_tmp = (int) buf2; @@ -1069,6 +1151,9 @@ outl (virt_to_bus (tp->rx_ring), dwCurrentRxDescAddr); outl (virt_to_bus (tp->tx_ring), dwCurrentTxDescAddr);
+ /* Setup Multicast */ + set_rx_mode(nic); + /* close IMR */ outw (0x0000, byIMR0);