patch-2.1.121 linux/drivers/acorn/net/ether3.c
Next file: linux/drivers/acorn/net/ether3.h
Previous file: linux/drivers/acorn/net/Makefile
Back to the patch index
Back to the overall index
- Lines: 515
- Date:
Sun Sep 6 10:46:07 1998
- Orig file:
v2.1.120/linux/drivers/acorn/net/ether3.c
- Orig date:
Tue Jul 21 00:15:30 1998
diff -u --recursive --new-file v2.1.120/linux/drivers/acorn/net/ether3.c linux/drivers/acorn/net/ether3.c
@@ -35,7 +35,6 @@
* never updates the transmit status correctly.
* TODO:
* When we detect a fatal error on the interface, we should restart it.
- * Reap transmit packets after some time even if the buffer never filled.
*/
static char *version = "ether3 ethernet driver (c) 1995-1998 R.M.King v1.13\n";
@@ -98,15 +97,25 @@
buffer_read
} buffer_rw_t;
+/*
+ * ether3 read/write. Slow things down a bit...
+ */
+#define ether3_outb(v,r) { outb((v),(r)); udelay(1); }
+#define ether3_outw(v,r) { outw((v),(r)); udelay(1); }
+
+#define ether3_inb(r) ({ unsigned int __v = inb((r)); udelay(1); __v; })
+#define ether3_inw(r) ({ unsigned int __v = inw((r)); udelay(1); __v; })
+
static int
ether3_setbuffer(struct device *dev, buffer_rw_t read, int start)
{
struct dev_priv *priv = (struct dev_priv *)dev->priv;
int timeout = 1000;
- outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- while ((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) {
+ ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+ ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+
+ while ((ether3_inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) {
if (!timeout--) {
printk("%s: setbuffer broken\n", dev->name);
priv->broken = 1;
@@ -114,12 +123,13 @@
}
udelay(1);
}
+
if (read == buffer_read) {
- outw(start, REG_DMAADDR);
- outw(priv->regs.command | CMD_FIFOREAD, REG_COMMAND);
+ ether3_outw(start, REG_DMAADDR);
+ ether3_outw(priv->regs.command | CMD_FIFOREAD, REG_COMMAND);
} else {
- outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- outw(start, REG_DMAADDR);
+ ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+ ether3_outw(start, REG_DMAADDR);
}
return 0;
}
@@ -159,7 +169,7 @@
{
struct device *dev = (struct device *)data;
struct dev_priv *priv = (struct dev_priv *)dev->priv;
- outw(priv->regs.config2 |= CFG2_CTRLO, REG_CONFIG2);
+ ether3_outw(priv->regs.config2 |= CFG2_CTRLO, REG_CONFIG2);
}
/*
@@ -174,7 +184,7 @@
priv->timer.function = ether3_ledoff;
add_timer(&priv->timer);
if (priv->regs.config2 & CFG2_CTRLO)
- outw(priv->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2);
+ ether3_outw(priv->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2);
}
/*
@@ -240,7 +250,7 @@
} else {
if (bad != -1) {
if (bad != i - 1)
- printk(" - 0x%04X", i - 1);
+ printk(" - 0x%04X\n", i - 1);
printk("\n");
bad = -1;
}
@@ -268,9 +278,9 @@
/*
* Set up our hardware address
*/
- outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
+ ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], REG_BUFWIN);
+ ether3_outb(dev->dev_addr[i], REG_BUFWIN);
if (dev->flags & IFF_PROMISC)
priv->regs.config1 |= CFG1_RECVPROMISC;
@@ -284,14 +294,14 @@
* last two bytes. To get round this problem, we receive the CRC as
* well. That way, if we do loose the last two, then it doesn't matter.
*/
- outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- outw((TX_END>>8) - 1, REG_BUFWIN);
- outw(priv->rx_head, REG_RECVPTR);
- outw(0, REG_TRANSMITPTR);
- outw(priv->rx_head >> 8, REG_RECVEND);
- outw(priv->regs.config2, REG_CONFIG2);
- outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- outw(priv->regs.command, REG_COMMAND);
+ ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+ ether3_outw((TX_END>>8) - 1, REG_BUFWIN);
+ ether3_outw(priv->rx_head, REG_RECVPTR);
+ ether3_outw(0, REG_TRANSMITPTR);
+ ether3_outw(priv->rx_head >> 8, REG_RECVEND);
+ ether3_outw(priv->regs.config2, REG_CONFIG2);
+ ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+ ether3_outw(priv->regs.command, REG_COMMAND);
i = ether3_ramtest(dev, 0x5A);
if(i)
@@ -314,16 +324,16 @@
memset(&priv->stats, 0, sizeof(struct enet_statistics));
/* Reset the chip */
- outw(CFG2_RESET, REG_CONFIG2);
+ ether3_outw(CFG2_RESET, REG_CONFIG2);
udelay(4);
priv->regs.command = 0;
- outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
- while (inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
+ ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
+ while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
- outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
+ ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], REG_BUFWIN);
+ ether3_outb(dev->dev_addr[i], REG_BUFWIN);
priv->tx_used = 0;
priv->tx_head = 0;
@@ -331,36 +341,53 @@
priv->regs.config2 |= CFG2_CTRLO;
priv->rx_head = RX_START;
- outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- outw((TX_END>>8) - 1, REG_BUFWIN);
- outw(priv->rx_head, REG_RECVPTR);
- outw(priv->rx_head >> 8, REG_RECVEND);
- outw(0, REG_TRANSMITPTR);
- outw(priv->regs.config2, REG_CONFIG2);
- outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+ ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+ ether3_outw((TX_END>>8) - 1, REG_BUFWIN);
+ ether3_outw(priv->rx_head, REG_RECVPTR);
+ ether3_outw(priv->rx_head >> 8, REG_RECVEND);
+ ether3_outw(0, REG_TRANSMITPTR);
+ ether3_outw(priv->regs.config2, REG_CONFIG2);
+ ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
ether3_setbuffer(dev, buffer_write, 0);
ether3_writelong(dev, 0);
- priv->regs.command = CMD_ENINTRX;
- outw(priv->regs.command | CMD_RXON, REG_COMMAND);
+ priv->regs.command = CMD_ENINTRX | CMD_ENINTTX;
+ ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND);
}
static inline int
ether3_probe_bus_8(struct device *dev, int val)
{
- outb(val & 255, REG_RECVPTR);
- outb(val >> 8, REG_RECVPTR + 1);
+ int write_low, write_high, read_low, read_high;
- return inb(REG_RECVPTR) == (val & 255) && inb(REG_RECVPTR + 1) == (val >> 8);
+ write_low = val & 255;
+ write_high = val >> 8;
+
+ printk(KERN_DEBUG "ether3_probe: write8 [%02X:%02X]", write_high, write_low);
+
+ ether3_outb(write_low, REG_RECVPTR);
+ ether3_outb(write_high, REG_RECVPTR + 1);
+
+ read_low = ether3_inb(REG_RECVPTR);
+ read_high = ether3_inb(REG_RECVPTR + 1);
+
+ printk(", read8 [%02X:%02X]\n", read_high, read_low);
+
+ return read_low == write_low && read_high == write_high;
}
static inline int
ether3_probe_bus_16(struct device *dev, int val)
{
- outw(val, REG_RECVPTR);
+ int read_val;
+
+ ether3_outw(val, REG_RECVPTR);
+ read_val = ether3_inw(REG_RECVPTR);
- return inw(REG_RECVPTR) == val;
+ printk(KERN_DEBUG "ether3_probe: write16 [%04X], read16 [%04X]\n", val, read_val);
+
+ return read_val == val;
}
/*
@@ -373,13 +400,15 @@
struct dev_priv *priv;
unsigned int i, bus_type, error = ENODEV;
- if (net_debug && version_printed++ == 0)
+ if (net_debug && version_printed++ == 0)
printk(version);
if (!dev->priv) {
dev->priv = kmalloc(sizeof (struct dev_priv), GFP_KERNEL);
- if (!dev->priv)
+ if (!dev->priv) {
+ printk(KERN_ERR "ether3_probe1: no memory\n");
return -ENOMEM;
+ }
}
priv = (struct dev_priv *) dev->priv;
@@ -389,7 +418,7 @@
/* Reset card...
*/
- outb(0x80, REG_CONFIG2 + 1);
+ ether3_outb(0x80, REG_CONFIG2 + 1);
bus_type = BUS_UNKNOWN;
udelay(4);
@@ -506,11 +535,11 @@
disable_irq(dev->irq);
- outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
+ ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
priv->regs.command = 0;
- while (inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
- outb(0x80, REG_CONFIG2 + 1);
- outw(0, REG_COMMAND);
+ while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
+ ether3_outb(0x80, REG_CONFIG2 + 1);
+ ether3_outw(0, REG_COMMAND);
free_irq(dev->irq, dev);
@@ -548,7 +577,34 @@
} else
priv->regs.config1 |= CFG1_RECVSPECBROAD;
- outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+ ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+}
+
+/*
+ * Allocate memory in transmitter ring buffer.
+ */
+static int
+ether3_alloc_tx(struct dev_priv *priv, int length, int alloc)
+{
+ int start, head, tail;
+
+ tail = priv->tx_tail;
+ start = priv->tx_head;
+ head = start + length + 4;
+
+ if (head >= TX_END) {
+ if (tail > priv->tx_head)
+ return -1;
+ head -= TX_END - TX_START;
+ if (tail < head)
+ return -1;
+ } else if (start < tail && tail < head)
+ return -1;
+
+ if (alloc)
+ priv->tx_head = head;
+
+ return start;
}
/*
@@ -566,9 +622,9 @@
if (!test_and_set_bit(0, (void *)&dev->tbusy)) {
unsigned long flags;
unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- unsigned int ptr, nextptr;
+ int ptr;
- length = (length + 3) & ~3;
+ length = (length + 1) & ~1;
if (priv->broken) {
dev_kfree_skb(skb);
@@ -577,43 +633,32 @@
return 0;
}
- ptr = priv->tx_head;
- nextptr = ptr + 0x600;
- if (nextptr >= TX_END)
- nextptr = 0;
- if (nextptr == priv->tx_tail)
- return 1; /* unable to queue */
- priv->tx_head = nextptr;
-
save_flags_cli(flags);
+ ptr = ether3_alloc_tx(priv, length, 1);
+ if (ptr == -1)
+ return 1; /* unable to queue */
+
#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS)
- ether3_setbuffer(dev, buffer_write, nextptr);
+ ether3_setbuffer(dev, buffer_write, priv->tx_head);
ether3_writelong(dev, 0);
- ether3_setbuffer(dev, buffer_write, ptr + 4);
+ ether3_setbuffer(dev, buffer_write, ptr);
+ ether3_writelong(dev, 0);
ether3_writebuffer(dev, skb->data, length);
- ether3_writeword(dev, htons(nextptr));
- ether3_writeword(dev, (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE) >> 16);
ether3_setbuffer(dev, buffer_write, ptr);
- ether3_writeword(dev, htons(ptr + length + 4));
+ ether3_writeword(dev, htons(priv->tx_head));
ether3_writeword(dev, TXHDR_FLAGS >> 16);
ether3_ledon(dev, priv);
- priv->tx_used ++;
- if (priv->tx_used >= (MAX_TX_BUFFERED * 3 / 4)) {
- priv->regs.command |= CMD_ENINTTX;
- outw(priv->regs.command, REG_COMMAND);
- }
-
- if (!(inw(REG_STATUS) & STAT_TXON)) {
- outw(ptr, REG_TRANSMITPTR);
- outw(priv->regs.command | CMD_TXON, REG_COMMAND);
+ if (!(ether3_inw(REG_STATUS) & STAT_TXON)) {
+ ether3_outw(ptr, REG_TRANSMITPTR);
+ ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND);
}
- if (priv->tx_used < MAX_TX_BUFFERED)
+ if (ether3_alloc_tx(priv, 2044, 0) != -1)
dev->tbusy = 0;
dev->trans_start = jiffies;
@@ -641,9 +686,9 @@
save_flags_cli(flags);
printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name);
printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name,
- inw(REG_STATUS), inw(REG_CONFIG1), inw(REG_CONFIG2));
+ ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2));
printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name,
- inw(REG_RECVPTR), inw(REG_RECVEND), inw(REG_TRANSMITPTR));
+ ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR));
printk(KERN_ERR "%s: tx head=%04X tx tail=%04X\n", dev->name,
priv->tx_head, priv->tx_tail);
ether3_setbuffer(dev, buffer_read, priv->tx_tail);
@@ -653,7 +698,7 @@
dev->tbusy = 0;
priv->regs.config2 |= CFG2_CTRLO;
priv->stats.tx_errors += 1;
- outw(priv->regs.config2 , REG_CONFIG2);
+ ether3_outw(priv->regs.config2 , REG_CONFIG2);
dev->trans_start = jiffies;
goto retry;
}
@@ -674,31 +719,19 @@
priv = (struct dev_priv *)dev->priv;
dev->interrupt = 1;
- status = inw(REG_STATUS);
- /*
- * Dispite we disable the TX interrupt when the packet buffer is
- * mostly empty, if we happen to get a RX interrupt, we might as
- * well handle the TX packets as well.
- */
- if (status & STAT_INTTX) { /* Packets transmitted */
- outw(CMD_ACKINTTX | priv->regs.command, REG_COMMAND);
- ether3_tx(dev, priv);
+
+ status = ether3_inw(REG_STATUS);
+
+ if (status & STAT_INTRX) {
+ ether3_outw(CMD_ACKINTRX | priv->regs.command, REG_COMMAND);
+ ether3_rx(dev, priv, 12);
}
- status = inw(REG_STATUS);
- if (status & STAT_INTRX && ether3_rx(dev, priv, 12)) { /* Got packet(s). */
- /*
- * We only acknowledge the interrupt if we have received all packets
- * in the buffer or else we run out of memory. This is to allow the
- * bh routines to run.
- */
- outw(CMD_ACKINTRX | priv->regs.command, REG_COMMAND);
- /*
- * Receive again if some have become available - we may have cleared
- * a pending IRQ
- */
- ether3_rx(dev, priv, 4);
+ if (status & STAT_INTTX) {
+ ether3_outw(CMD_ACKINTTX | priv->regs.command, REG_COMMAND);
+ ether3_tx(dev, priv);
}
+
dev->interrupt = 0;
#if NET_DEBUG > 1
@@ -741,13 +774,23 @@
ether3_setbuffer(dev, buffer_read, this_ptr);
ether3_readbuffer(dev, addrs+2, 12);
+if (next_ptr < RX_START || next_ptr >= RX_END) {
+ int i;
+ printk("%s: bad next pointer @%04X: ", dev->name, priv->rx_head);
+ printk("%02X %02X %02X %02X ", next_ptr >> 8, next_ptr & 255, status & 255, status >> 8);
+ for (i = 2; i < 14; i++)
+ printk("%02X ", addrs[i]);
+ printk("\n");
+ next_ptr = priv->rx_head;
+ break;
+}
/*
* ignore our own packets...
*/
if (!(*(unsigned long *)&dev->dev_addr[0] ^ *(unsigned long *)&addrs[2+6]) &&
!(*(unsigned short *)&dev->dev_addr[4] ^ *(unsigned short *)&addrs[2+10])) {
maxcnt ++; /* compensate for loopedback packet */
- outw(next_ptr >> 8, REG_RECVEND);
+ ether3_outw(next_ptr >> 8, REG_RECVEND);
} else
if (!(status & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) {
unsigned int length = next_ptr - this_ptr;
@@ -764,7 +807,7 @@
skb_reserve(skb, 2);
buf = skb_put(skb, length);
ether3_readbuffer(dev, buf + 12, length - 12);
- outw(next_ptr >> 8, REG_RECVEND);
+ ether3_outw(next_ptr >> 8, REG_RECVEND);
*(unsigned short *)(buf + 0) = *(unsigned short *)(addrs + 2);
*(unsigned long *)(buf + 2) = *(unsigned long *)(addrs + 4);
*(unsigned long *)(buf + 6) = *(unsigned long *)(addrs + 8);
@@ -776,7 +819,7 @@
goto dropping;
} else {
struct enet_statistics *stats = &priv->stats;
- outw(next_ptr >> 8, REG_RECVEND);
+ ether3_outw(next_ptr >> 8, REG_RECVEND);
if (status & RXSTAT_OVERSIZE) stats->rx_over_errors ++;
if (status & RXSTAT_CRCERROR) stats->rx_crc_errors ++;
if (status & RXSTAT_DRIBBLEERROR) stats->rx_fifo_errors ++;
@@ -793,10 +836,10 @@
* If rx went off line, then that means that the buffer may be full. We
* have dropped at least one packet.
*/
- if (!(inw(REG_STATUS) & STAT_RXON)) {
+ if (!(ether3_inw(REG_STATUS) & STAT_RXON)) {
priv->stats.rx_dropped ++;
- outw(next_ptr, REG_RECVPTR);
- outw(priv->regs.command | CMD_RXON, REG_COMMAND);
+ ether3_outw(next_ptr, REG_RECVPTR);
+ ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND);
}
return maxcnt;
@@ -804,7 +847,7 @@
dropping:{
static unsigned long last_warned;
- outw(next_ptr >> 8, REG_RECVEND);
+ ether3_outw(next_ptr >> 8, REG_RECVEND);
/*
* Don't print this message too many times...
*/
@@ -852,15 +895,13 @@
if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++;
}
- /*
- * Get next packet address
- */
- tx_tail += 0x600;
- if (tx_tail >= TX_END)
- tx_tail = 0;
-
- if (priv->tx_used)
- priv->tx_used--;
+ tx_tail = htons(status & TX_NEXT);
+ if (tx_tail < TX_START || tx_tail >= TX_END) {
+ printk("%s: transmit error: next pointer = %04X\n", dev->name, tx_tail);
+ tx_tail = TX_START;
+ priv->tx_head = TX_START;
+ priv->tx_tail = TX_END;
+ }
} while (1);
if (priv->tx_tail != tx_tail) {
@@ -870,8 +911,6 @@
mark_bh(NET_BH); /* Inform upper layers. */
}
}
- priv->regs.command &= ~CMD_ENINTTX;
- outw(priv->regs.command, REG_COMMAND);
}
#ifdef MODULE
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov