patch-2.2.0-pre8 linux/drivers/net/pcnet32.c
Next file: linux/drivers/net/ppp.c
Previous file: linux/drivers/net/ltpc.c
Back to the patch index
Back to the overall index
- Lines: 317
- Date:
Sun Jan 17 18:28:06 1999
- Orig file:
v2.2.0-pre7/linux/drivers/net/pcnet32.c
- Orig date:
Sat Sep 5 16:46:40 1998
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
@@ -1,6 +1,6 @@
/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */
/*
- * Copyright 1996,97,98 Thomas Bogendoerfer
+ * Copyright 1996-1999 Thomas Bogendoerfer
*
* Derived from the lance driver written 1993,1994,1995 by Donald Becker.
*
@@ -13,7 +13,7 @@
* This driver is for PCnet32 and PCnetPCI based ethercards
*/
-static const char *version = "pcnet32.c:v1.02 3.9.98 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.11 17.1.99 tsbogend@alpha.franken.de\n";
#include <linux/config.h>
#include <linux/module.h>
@@ -51,6 +51,17 @@
static const int max_interrupt_work = 20;
static const int rx_copybreak = 200;
+#define PORT_AUI 0x00
+#define PORT_10BT 0x01
+#define PORT_GPSI 0x02
+#define PORT_MII 0x03
+
+#define PORT_PORTSEL 0x03
+#define PORT_ASEL 0x04
+#define PORT_FD 0x80
+
+static int options = PORT_ASEL; /* port selection */
+
/*
* Theory of Operation
*
@@ -101,6 +112,10 @@
* v1.01: do ring dumps, only when debugging the driver
* increased the transmit timeout
* v1.02: fixed memory leak in pcnet32_init_ring()
+ * v1.10: workaround for stopped transmitter
+ * added port selection for modules
+ * detect special T1/E1 WAN card and setup port selection
+ * v1.11: fixed wrong checking of Tx errors
*/
@@ -176,8 +191,9 @@
int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
struct net_device_stats stats;
char tx_full;
- unsigned long lock;
- char shared_irq; /* shared irq possible */
+ int options;
+ int shared_irq:1, /* shared irq possible */
+ full_duplex:1; /* full duplex possible */
#ifdef MODULE
struct device *next;
#endif
@@ -250,11 +266,13 @@
unsigned int ioaddr = *port;
if ( check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) {
- if (pcnet32_probe1(dev, ioaddr, 0, 0) == 0)
+ /* check if there is really a pcnet chip on that ioaddr */
+ if ((inb(ioaddr + 14) == 0x57) &&
+ (inb(ioaddr + 15) == 0x57) &&
+ (pcnet32_probe1(dev, ioaddr, 0, 0) == 0))
return 0;
}
}
-
return ENODEV;
}
@@ -263,13 +281,9 @@
__initfunc(static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared))
{
struct pcnet32_private *lp;
- int i;
+ int i,full_duplex = 0;
char *chipname;
- /* check if there is really a pcnet chip on that ioaddr */
- if ((inb(ioaddr + 14) != 0x57) || (inb(ioaddr + 15) != 0x57))
- return ENODEV;
-
inw(ioaddr+PCNET32_RESET); /* Reset the PCNET32 */
outw(0x0000, ioaddr+PCNET32_ADDR); /* Switch to window 0 */
@@ -295,16 +309,22 @@
chipname = "PCnet/PCI 79C970";
break;
case 0x2430:
- chipname = "PCnet32";
+ if (shared)
+ chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */
+ else
+ chipname = "PCnet/32 79C965";
break;
case 0x2621:
chipname = "PCnet/PCI II 79C970A";
+ full_duplex = 1;
break;
case 0x2623:
chipname = "PCnet/FAST 79C971";
+ full_duplex = 1;
break;
case 0x2624:
chipname = "PCnet/FAST+ 79C972";
+ full_duplex = 1;
break;
default:
printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
@@ -332,8 +352,14 @@
dev->priv = lp;
lp->name = chipname;
lp->shared_irq = shared;
+ lp->full_duplex = full_duplex;
+ lp->options = options;
+
+ /* detect special T1/E1 WAN card by checking for MAC address */
+ if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75)
+ lp->options = PORT_FD | PORT_GPSI;
- lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */
+ lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */
lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
for (i = 0; i < 6; i++)
lp->init_block.phys_addr[i] = dev->dev_addr[i];
@@ -382,11 +408,6 @@
}
}
- outw(0x0002, ioaddr+PCNET32_ADDR);
- /* only touch autoselect bit */
- outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF);
-
-
if (pcnet32_debug > 0)
printk(version);
@@ -413,6 +434,7 @@
{
struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
unsigned int ioaddr = dev->base_addr;
+ unsigned short val;
int i;
if (dev->irq == 0 ||
@@ -428,19 +450,40 @@
outw(0x0014, ioaddr+PCNET32_ADDR);
outw(0x0002, ioaddr+PCNET32_BUS_IF);
- /* Turn on auto-select of media (AUI, BNC). */
- outw(0x0002, ioaddr+PCNET32_ADDR);
- /* only touch autoselect bit */
- outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF);
-
if (pcnet32_debug > 1)
printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",
dev->name, dev->irq,
(u32) virt_to_bus(lp->tx_ring),
(u32) virt_to_bus(lp->rx_ring),
(u32) virt_to_bus(&lp->init_block));
-
- lp->init_block.mode = 0x0000;
+
+ /* set/reset autoselect bit */
+ outw(0x0002, ioaddr+PCNET32_ADDR);
+ val = inw(ioaddr+PCNET32_BUS_IF) & ~2;
+ if (lp->options & PORT_ASEL)
+ val |= 2;
+ outw(val, ioaddr+PCNET32_BUS_IF);
+
+ /* handle full duplex setting */
+ if (lp->full_duplex) {
+ outw (0x0009, ioaddr+PCNET32_ADDR);
+ val = inw(ioaddr+PCNET32_BUS_IF) & ~3;
+ if (lp->options & PORT_FD) {
+ val |= 1;
+ if (lp->options == (PORT_FD | PORT_AUI))
+ val |= 2;
+ }
+ outw(val, ioaddr+PCNET32_BUS_IF);
+ }
+
+ /* set/reset GPSI bit in test register */
+ outw (0x007c, ioaddr+PCNET32_ADDR);
+ val = inw(ioaddr+PCNET32_DATA) & ~0x10;
+ if ((lp->options & PORT_PORTSEL) == PORT_GPSI)
+ val |= 0x10;
+ outw(val, ioaddr+PCNET32_DATA);
+
+ lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
lp->init_block.filter[0] = 0x00000000;
lp->init_block.filter[1] = 0x00000000;
if (pcnet32_init_ring(dev))
@@ -515,7 +558,7 @@
struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
int i;
- lp->lock = 0, lp->tx_full = 0;
+ lp->tx_full = 0;
lp->cur_rx = lp->cur_tx = 0;
lp->dirty_rx = lp->dirty_tx = 0;
@@ -613,7 +656,6 @@
outw(0x0000, ioaddr+PCNET32_ADDR);
printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name,
inw(ioaddr+PCNET32_DATA));
- outw(0x0000, ioaddr+PCNET32_DATA);
}
/* Block a timer-based transmit from overlapping. This could better be
@@ -623,12 +665,8 @@
return 1;
}
- if (test_and_set_bit(0, (void*)&lp->lock) != 0) {
- if (pcnet32_debug > 0)
- printk("%s: tx queue lock!.\n", dev->name);
- /* don't clear dev->tbusy flag. */
- return 1;
- }
+ save_flags (flags);
+ cli ();
/* Fill in a Tx ring entry */
@@ -655,15 +693,11 @@
dev->trans_start = jiffies;
- save_flags(flags);
- cli();
- lp->lock = 0;
if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
clear_bit (0, (void *)&dev->tbusy);
else
lp->tx_full = 1;
restore_flags(flags);
-
return 0;
}
@@ -675,6 +709,7 @@
struct pcnet32_private *lp;
unsigned int csr0, ioaddr;
int boguscnt = max_interrupt_work;
+ int must_restart;
if (dev == NULL) {
printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);
@@ -693,6 +728,8 @@
/* Acknowledge all of the current interrupt sources ASAP. */
outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA);
+ must_restart = 0;
+
if (pcnet32_debug > 5)
printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
dev->name, csr0, inw(dev->base_addr + PCNET32_DATA));
@@ -714,7 +751,7 @@
if (status & 0x4000) {
/* There was an major error, log it. */
- int err_status = le16_to_cpu(lp->tx_ring[entry].misc);
+ int err_status = le32_to_cpu(lp->tx_ring[entry].misc);
lp->stats.tx_errors++;
if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
@@ -725,10 +762,7 @@
/* Remove this verbosity later! */
printk("%s: Tx FIFO error! Status %4.4x.\n",
dev->name, csr0);
- /* stop the chip to clear the error condition, then restart */
- outw(0x0000, dev->base_addr + PCNET32_ADDR);
- outw(0x0004, dev->base_addr + PCNET32_DATA);
- pcnet32_restart(dev, 0x0002);
+ must_restart = 1;
}
} else {
if (status & 0x1800)
@@ -782,6 +816,13 @@
dev->name, csr0);
/* unlike for the lance, there is no restart needed */
}
+
+ if (must_restart) {
+ /* stop the chip to clear the error condition, then restart */
+ outw(0x0000, dev->base_addr + PCNET32_ADDR);
+ outw(0x0004, dev->base_addr + PCNET32_DATA);
+ pcnet32_restart(dev, 0x0002);
+ }
}
/* Clear any other interrupt, and set interrupt enable. */
@@ -1014,9 +1055,9 @@
if (dev->flags&IFF_PROMISC) {
/* Log any net taps. */
printk("%s: Promiscuous mode enabled.\n", dev->name);
- lp->init_block.mode = le16_to_cpu(0x8000);
+ lp->init_block.mode = le16_to_cpu(0x8000 | (lp->options & PORT_PORTSEL) << 7);
} else {
- lp->init_block.mode = 0x0000;
+ lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
pcnet32_load_multicast (dev);
}
@@ -1028,6 +1069,7 @@
#ifdef MODULE
MODULE_PARM(debug, "i");
+MODULE_PARM(options, "i");
MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM(rx_copybreak, "i");
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov