summaryrefslogtreecommitdiffstats
path: root/drivers/net/8390.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/8390.c')
-rw-r--r--drivers/net/8390.c119
1 files changed, 74 insertions, 45 deletions
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index ebed123a7..161e20a20 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -15,11 +15,15 @@
This is the chip-specific code for many 8390-based ethernet adaptors.
This is not a complete driver, it must be combined with board-specific
code such as ne.c, wd.c, 3c503.c, etc.
+
+ 13/04/95 -- Don't blindly swallow ENISR_RDC interrupts for non-shared
+ memory cards. We need to follow these closely for neX000 cards.
+ Plus other minor cleanups. -- Paul Gortmaker
+
*/
static char *version =
"8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
/*
Braindamage remaining:
@@ -30,7 +34,11 @@ static char *version =
The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
*/
-#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
@@ -52,11 +60,6 @@ static char *version =
#include "8390.h"
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
/* These are the operational function interfaces to board-specific
routines.
void reset_8390(struct device *dev)
@@ -85,7 +88,7 @@ int ei_debug = 1;
#endif
/* Max number of packets received at one Intr.
- Current this may only be examined by a kernel debugger. */
+ Currently this may only be examined by a kernel debugger. */
static int high_water_mark = 0;
/* Index to functions. */
@@ -126,19 +129,25 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
int e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int length, send_length;
+ unsigned long flags;
- /* We normally shouldn't be called if dev->tbusy is set, but the
- existing code does anyway.
- If it has been too long (> 100 or 150ms.) since the last Tx we assume
- the board has died and kick it. */
-
+/*
+ * We normally shouldn't be called if dev->tbusy is set, but the
+ * existing code does anyway. If it has been too long since the
+ * last Tx, we assume the board has died and kick it.
+ */
+
if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
int txsr = inb(e8390_base+EN0_TSR), isr;
int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 10 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
+ if (tickssofar < TX_TIMEOUT || (tickssofar < (TX_TIMEOUT+5) && ! (txsr & ENTSR_PTX))) {
return 1;
}
isr = inb(e8390_base+EN0_ISR);
+ if (dev->start == 0) {
+ printk("%s: xmit on stopped card\n", dev->name);
+ return 1;
+ }
printk(KERN_DEBUG "%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
dev->name, txsr, isr);
/* Does the 8390 thinks it has posted an interrupt? */
@@ -168,15 +177,22 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
if (skb->len <= 0)
return 0;
- /* Block a timer-based transmit from overlapping. */
- if (set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
- return 1;
- }
+ save_flags(flags);
+ cli();
+
+ /* Block a timer-based transmit from overlapping. */
+ if ((set_bit(0, (void*)&dev->tbusy) != 0) || ei_local->irqlock) {
+ printk("%s: Tx access conflict. irq=%d lock=%d tx1=%d tx2=%d last=%d\n",
+ dev->name, dev->interrupt, ei_local->irqlock, ei_local->tx1,
+ ei_local->tx2, ei_local->lasttx);
+ restore_flags(flags);
+ return 1;
+ }
/* Mask interrupts from the ethercard. */
- outb(0x00, e8390_base + EN0_IMR);
+ outb(0x00, e8390_base + EN0_IMR);
ei_local->irqlock = 1;
+ restore_flags(flags);
send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
@@ -198,28 +214,30 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
ei_local->txing);
} else { /* We should never get here. */
if (ei_debug)
- printk("%s: No packet buffer space for ping-pong use.\n",
- dev->name);
+ printk("%s: No Tx buffers free. irq=%d tx1=%d tx2=%d last=%d\n",
+ dev->name, dev->interrupt, ei_local->tx1,
+ ei_local->tx2, ei_local->lasttx);
ei_local->irqlock = 0;
dev->tbusy = 1;
- outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
return 1;
}
ei_block_output(dev, length, skb->data, output_page);
if (! ei_local->txing) {
+ ei_local->txing = 1;
NS8390_trigger_send(dev, send_length, output_page);
dev->trans_start = jiffies;
if (output_page == ei_local->tx_start_page)
ei_local->tx1 = -1, ei_local->lasttx = -1;
else
ei_local->tx2 = -1, ei_local->lasttx = -2;
- ei_local->txing = 1;
} else
ei_local->txqueue++;
dev->tbusy = (ei_local->tx1 && ei_local->tx2);
} else { /* No pingpong, just a single Tx buffer. */
ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
+ ei_local->txing = 1;
NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
dev->trans_start = jiffies;
dev->tbusy = 1;
@@ -236,12 +254,11 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
/* The typical workload of the driver:
Handle the ether interface interrupts. */
-void ei_interrupt(int reg_ptr)
+void ei_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
int e8390_base;
- int interrupts, boguscount = 0;
+ int interrupts, nr_serviced = 0;
struct ei_device *ei_local;
if (dev == NULL) {
@@ -252,7 +269,6 @@ void ei_interrupt(int reg_ptr)
ei_local = (struct ei_device *) dev->priv;
if (dev->interrupt || ei_local->irqlock) {
/* The "irqlock" check is only for testing. */
- sti();
printk(ei_local->irqlock
? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
: "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
@@ -262,7 +278,6 @@ void ei_interrupt(int reg_ptr)
}
dev->interrupt = 1;
- sti(); /* Allow other interrupts. */
/* Change to page 0 and read the intr status reg. */
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
@@ -272,10 +287,11 @@ void ei_interrupt(int reg_ptr)
/* !!Assumption!! -- we stay in page 0. Don't break this. */
while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
- && ++boguscount < 9) {
- if (interrupts & ENISR_RDC) {
- /* Ack meaningless DMA complete. */
- outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+ && ++nr_serviced < MAX_SERVICE) {
+ if (dev->start == 0) {
+ printk("%s: interrupt from stopped card\n", dev->name);
+ interrupts = 0;
+ break;
}
if (interrupts & ENISR_OVER) {
ei_rx_overrun(dev);
@@ -297,17 +313,25 @@ void ei_interrupt(int reg_ptr)
if (interrupts & ENISR_TX_ERR) {
outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
}
+
+ if (interrupts & ENISR_RDC) {
+ if (dev->mem_start)
+ outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+ }
+
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
}
- if (interrupts && ei_debug) {
- if (boguscount == 9)
+ if ((interrupts & ~ENISR_RDC) && ei_debug) {
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ if (nr_serviced == MAX_SERVICE) {
printk("%s: Too much work at interrupt, status %#2.2x\n",
dev->name, interrupts);
- else
+ outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+ } else {
printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
- outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
}
dev->interrupt = 0;
return;
@@ -332,9 +356,9 @@ static void ei_tx_intr(struct device *dev)
ei_local->tx1 = 0;
dev->tbusy = 0;
if (ei_local->tx2 > 0) {
+ ei_local->txing = 1;
NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
dev->trans_start = jiffies;
- ei_local->txing = 1;
ei_local->tx2 = -1,
ei_local->lasttx = 2;
} else
@@ -346,9 +370,9 @@ static void ei_tx_intr(struct device *dev)
ei_local->tx2 = 0;
dev->tbusy = 0;
if (ei_local->tx1 > 0) {
+ ei_local->txing = 1;
NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
dev->trans_start = jiffies;
- ei_local->txing = 1;
ei_local->tx1 = -1;
ei_local->lasttx = 1;
} else
@@ -396,7 +420,7 @@ static void ei_receive(struct device *dev)
rxing_page = inb_p(e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
- /* Remove one frame from the ring. Boundary is alway a page behind. */
+ /* Remove one frame from the ring. Boundary is always a page behind. */
this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
if (this_frame >= ei_local->stop_page)
this_frame = ei_local->rx_start_page;
@@ -453,6 +477,7 @@ static void ei_receive(struct device *dev)
ei_block_input(dev, pkt_len, (char *) skb->data,
current_offset + sizeof(rx_frame));
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
ei_local->stat.rx_packets++;
}
@@ -469,12 +494,12 @@ static void ei_receive(struct device *dev)
/* This _should_ never happen: it's here for avoiding bad clones. */
if (next_frame >= ei_local->stop_page) {
- printk("%s: next frame inconsistency, %#2x..", dev->name,
+ printk("%s: next frame inconsistency, %#2x\n", dev->name,
next_frame);
next_frame = ei_local->rx_start_page;
}
ei_local->current_page = next_frame;
- outb(next_frame-1, e8390_base+EN0_BOUNDARY);
+ outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
}
/* If any worth-while packets have been received, dev_rint()
has done a mark_bh(NET_BH) for us and will work on them
@@ -533,6 +558,9 @@ static struct enet_statistics *get_stats(struct device *dev)
short ioaddr = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ /* If the card is stopped, just return the present stats. */
+ if (dev->start == 0) return &ei_local->stat;
+
/* Read the counter registers, assuming we are in page 0. */
ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
ei_local->stat.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1);
@@ -604,6 +632,7 @@ void NS8390_init(struct device *dev, int startp)
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int i;
int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+ unsigned long flags;
/* Follow National Semi's recommendations for initing the DP83902. */
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
@@ -627,6 +656,7 @@ void NS8390_init(struct device *dev, int startp)
/* Copy the station address into the DS8390 registers,
and set the multicast hash bitmap to receive all multicasts. */
+ save_flags(flags);
cli();
outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
for(i = 0; i < 6; i++) {
@@ -639,7 +669,7 @@ void NS8390_init(struct device *dev, int startp)
outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
- sti();
+ restore_flags(flags);
dev->tbusy = 0;
dev->interrupt = 0;
ei_local->tx1 = ei_local->tx2 = 0;
@@ -661,7 +691,6 @@ static void NS8390_trigger_send(struct device *dev, unsigned int length,
{
int e8390_base = dev->base_addr;
- ei_status.txing = 1;
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
if (inb_p(e8390_base) & E8390_TRANS) {