patch-2.1.4 linux/drivers/net/3c505.c
Next file: linux/drivers/net/3c505.h
Previous file: linux/drivers/isdn/isdn_tty.c
Back to the patch index
Back to the overall index
- Lines: 664
- Date:
Tue Oct 15 19:42:03 1996
- Orig file:
v2.1.3/linux/drivers/net/3c505.c
- Orig date:
Thu Aug 1 15:43:04 1996
diff -u --recursive --new-file v2.1.3/linux/drivers/net/3c505.c linux/drivers/net/3c505.c
@@ -30,11 +30,14 @@
* Terry Murphy, of 3Com Network Adapter Division
* Linux 1.3.0 changes by
* Alan Cox <Alan.Cox@linux.org>
- * More debugging and DMA version by Philip Blundell
+ * More debugging, DMA support, currently maintained by
+ * Philip Blundell <Philip.Blundell@pobox.com>
+ * Multicard/soft configurable dma channel/rev 2 hardware support
+ * by Christopher Collins <ccollins@pcug.org.au>
*/
/* Theory of operation:
-
+ *
* The 3c505 is quite an intelligent board. All communication with it is done
* by means of Primary Command Blocks (PCBs); these are transferred using PIO
* through the command register. The card has 256k of on-board RAM, which is
@@ -42,7 +45,9 @@
* are better, but in fact this isn't true. From my tests, it seems that
* more than about 10 buffers are unnecessary, and there is a noticeable
* performance hit in having more active on the card. So the majority of the
- * card's memory isn't, in fact, used.
+ * card's memory isn't, in fact, used. Sadly, the card only has one transmit
+ * buffer and, short of loading our own firmware into it (which is what some
+ * drivers resort to) there's nothing we can do about this.
*
* We keep up to 4 "receive packet" commands active on the board at a time.
* When a packet comes in, so long as there is a receive command active, the
@@ -71,11 +76,13 @@
* is blocked. In practice, this doesn't seem to happen very often.
*/
-/* This driver will not work with revision 2 hardware, because the host
- * control register is write-only. It should be fairly easy to arrange to
- * keep our own soft-copy of the intended contents of this register, if
- * somebody has the time. There may be firmware differences that cause
- * other problems, though, and I don't have an old card to test.
+/* This driver may now work with revision 2.x hardware, since all the read
+ * operations on the HCR have been removed (we now keep our own softcopy).
+ * But I don't have an old card to test it on.
+ *
+ * This has had the bad effect that the autoprobe routine is now a bit
+ * less friendly to other devices. However, it was never very good.
+ * before, so I doubt it will hurt anybody.
*/
/* The driver is a mess. I took Craig's and Juha's code, and hacked it firstly
@@ -104,9 +111,6 @@
#include "3c505.h"
-#define ELP_DMA 6 /* DMA channel to use */
-#define ELP_RX_PCBS 4
-
/*********************************************************
*
* define debug messages here as common strings to reduce space
@@ -174,7 +178,7 @@
* Last element MUST BE 0!
*****************************************************************/
-const int addr_list[] = {0x300, 0x280, 0x310, 0};
+static const int addr_list[] = {0x300, 0x280, 0x310, 0};
/* Dma Memory related stuff */
@@ -211,21 +215,19 @@
return inb(base_addr + PORT_STATUS);
}
-static inline unsigned char inb_control(unsigned int base_addr)
-{
- return inb(base_addr + PORT_CONTROL);
-}
-
static inline int inb_command(unsigned int base_addr)
{
return inb(base_addr + PORT_COMMAND);
}
-static inline void outb_control(unsigned char val, unsigned int base_addr)
+static inline void outb_control(unsigned char val, struct device *dev)
{
- outb(val, base_addr + PORT_CONTROL);
+ outb(val, dev->base_addr + PORT_CONTROL);
+ ((elp_device *)(dev->priv))->hcr_val = val;
}
+#define HCR_VAL(x) (((elp_device *)((x)->priv))->hcr_val)
+
static inline void outb_command(unsigned char val, unsigned int base_addr)
{
outb(val, base_addr + PORT_COMMAND);
@@ -241,48 +243,6 @@
outw(val, base_addr + PORT_DATA);
}
-
-/*****************************************************************
- *
- * structure to hold context information for adapter
- *
- *****************************************************************/
-
-#define DMA_BUFFER_SIZE 1600
-#define BACKLOG_SIZE 4
-
-typedef struct {
- volatile short got[NUM_TRANSMIT_CMDS]; /* flags for command completion */
- pcb_struct tx_pcb; /* PCB for foreground sending */
- pcb_struct rx_pcb; /* PCB for foreground receiving */
- pcb_struct itx_pcb; /* PCB for background sending */
- pcb_struct irx_pcb; /* PCB for background receiving */
- struct enet_statistics stats;
-
- void *dma_buffer;
-
- struct {
- unsigned int length[BACKLOG_SIZE];
- unsigned int in;
- unsigned int out;
- } rx_backlog;
-
- struct {
- unsigned int direction;
- unsigned int length;
- unsigned int copy_flag;
- struct sk_buff *skb;
- long int start_time;
- } current_dma;
-
- /* flags */
- unsigned long send_pcb_semaphore;
- unsigned int dmaing;
- unsigned long busy;
-
- unsigned int rx_active; /* number of receive PCBs */
-} elp_device;
-
static inline unsigned int backlog_next(unsigned int n)
{
return (n + 1) % BACKLOG_SIZE;
@@ -315,10 +275,10 @@
return stat1;
}
-static inline void set_hsf(unsigned int base_addr, int hsf)
+static inline void set_hsf(struct device *dev, int hsf)
{
cli();
- outb_control((inb_control(base_addr) & ~HSF_PCB_MASK) | hsf, base_addr);
+ outb_control((HCR_VAL(dev) & ~HSF_PCB_MASK) | hsf, dev);
sti();
}
@@ -327,11 +287,10 @@
inline static void adapter_reset(struct device *dev)
{
int timeout;
- unsigned char orig_hcr = inb_control(dev->base_addr);
-
elp_device *adapter = dev->priv;
+ unsigned char orig_hcr = adapter->hcr_val;
- outb_control(0, dev->base_addr);
+ outb_control(0, dev);
if (inb_status(dev->base_addr) & ACRF) {
do {
@@ -339,29 +298,29 @@
timeout = jiffies + 2;
while ((jiffies <= timeout) && !(inb_status(dev->base_addr) & ACRF));
} while (inb_status(dev->base_addr) & ACRF);
- set_hsf(dev->base_addr, HSF_PCB_NAK);
+ set_hsf(dev, HSF_PCB_NAK);
}
- outb_control(inb_control(dev->base_addr) | ATTN | DIR, dev->base_addr);
+ outb_control(adapter->hcr_val | ATTN | DIR, dev);
timeout = jiffies + 1;
while (jiffies <= timeout);
- outb_control(inb_control(dev->base_addr) & ~ATTN, dev->base_addr);
+ outb_control(adapter->hcr_val & ~ATTN, dev);
timeout = jiffies + 1;
while (jiffies <= timeout);
- outb_control(inb_control(dev->base_addr) | FLSH, dev->base_addr);
+ outb_control(adapter->hcr_val | FLSH, dev);
timeout = jiffies + 1;
while (jiffies <= timeout);
- outb_control(inb_control(dev->base_addr) & ~FLSH, dev->base_addr);
+ outb_control(adapter->hcr_val & ~FLSH, dev);
timeout = jiffies + 1;
while (jiffies <= timeout);
- outb_control(orig_hcr, dev->base_addr);
+ outb_control(orig_hcr, dev);
if (!start_receive(dev, &adapter->tx_pcb))
printk("%s: start receive command failed \n", dev->name);
}
-/* Check to make sure that a DMA transfer hasn't timed out. This should never happen
- * in theory, but seems to occur occasionally if the card gets prodded at the wrong
- * time.
+/* Check to make sure that a DMA transfer hasn't timed out. This should
+ * never happen in theory, but seems to occur occasionally if the card gets
+ * prodded at the wrong time.
*/
static inline void check_dma(struct device *dev)
{
@@ -376,7 +335,7 @@
disable_dma(dev->dma);
if (adapter->rx_active)
adapter->rx_active--;
- outb_control(inb_control(dev->base_addr) & ~(DMAE | TCEN | DIR), dev->base_addr);
+ outb_control(adapter->hcr_val & ~(DMAE | TCEN | DIR), dev);
restore_flags(flags);
}
}
@@ -463,7 +422,7 @@
* wait for the HCRE bit to indicate the adapter
* had read the byte
*/
- set_hsf(dev->base_addr, 0);
+ set_hsf(dev, 0);
if (send_pcb_slow(dev->base_addr, pcb->command))
goto abort;
@@ -478,7 +437,7 @@
goto sti_abort;
}
- outb_control(inb_control(dev->base_addr) | 3, dev->base_addr); /* signal end of PCB */
+ outb_control(adapter->hcr_val | 3, dev); /* signal end of PCB */
outb_command(2 + pcb->length, dev->base_addr);
/* now wait for the acknowledgement */
@@ -530,7 +489,7 @@
elp_device *adapter = dev->priv;
- set_hsf(dev->base_addr, 0);
+ set_hsf(dev, 0);
/* get the command code */
timeout = jiffies + 2;
@@ -578,14 +537,14 @@
if (total_length != (pcb->length + 2)) {
if (elp_debug >= 2)
printk("%s: mangled PCB received\n", dev->name);
- set_hsf(dev->base_addr, HSF_PCB_NAK);
+ set_hsf(dev, HSF_PCB_NAK);
return FALSE;
}
if (pcb->command == CMD_RECEIVE_PACKET_COMPLETE) {
if (set_bit(0, (void *) &adapter->busy)) {
if (backlog_next(adapter->rx_backlog.in) == adapter->rx_backlog.out) {
- set_hsf(dev->base_addr, HSF_PCB_NAK);
+ set_hsf(dev, HSF_PCB_NAK);
printk("%s: PCB rejected, transfer in progress and backlog full\n", dev->name);
pcb->command = 0;
return TRUE;
@@ -594,7 +553,7 @@
}
}
}
- set_hsf(dev->base_addr, HSF_PCB_ACK);
+ set_hsf(dev, HSF_PCB_ACK);
return TRUE;
}
@@ -637,25 +596,27 @@
{
int rlen;
elp_device *adapter = dev->priv;
- unsigned long target;
+ void *target;
struct sk_buff *skb;
rlen = (len + 1) & ~1;
skb = dev_alloc_skb(rlen + 2);
- adapter->current_dma.copy_flag = 0;
-
if (!skb) {
- printk("%s: memory squeeze, dropping packet\n", dev->name);
- target = virt_to_bus(adapter->dma_buffer);
+ printk("%s: memory squeeze, dropping packet\n", dev->name);
+ target = adapter->dma_buffer;
+ adapter->current_dma.target = NULL;
} else {
- skb_reserve(skb, 2);
- target = virt_to_bus(skb_put(skb, rlen));
- if ((target + rlen) >= MAX_DMA_ADDRESS) {
- target = virt_to_bus(adapter->dma_buffer);
- adapter->current_dma.copy_flag = 1;
- }
+ skb_reserve(skb, 2);
+ target = skb_put(skb, rlen);
+ if (virt_to_bus(target + rlen) >= MAX_DMA_ADDRESS) {
+ adapter->current_dma.target = target;
+ target = adapter->dma_buffer;
+ } else {
+ adapter->current_dma.target = NULL;
+ }
}
+
/* if this happens, we die */
if (set_bit(0, (void *) &adapter->dmaing))
printk("%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction);
@@ -665,18 +626,19 @@
adapter->current_dma.skb = skb;
adapter->current_dma.start_time = jiffies;
- outb_control(inb_control(dev->base_addr) | DIR | TCEN | DMAE, dev->base_addr);
+ outb_control(adapter->hcr_val | DIR | TCEN | DMAE, dev);
disable_dma(dev->dma);
clear_dma_ff(dev->dma);
set_dma_mode(dev->dma, 0x04); /* dma read */
- set_dma_addr(dev->dma, target);
+ set_dma_addr(dev->dma, virt_to_bus(target));
set_dma_count(dev->dma, rlen);
enable_dma(dev->dma);
if (elp_debug >= 3) {
printk("%s: rx DMA transfer started\n", dev->name);
}
+
if (adapter->rx_active)
adapter->rx_active--;
@@ -729,15 +691,17 @@
printk("%s: %s DMA complete, status %02x\n", dev->name, adapter->current_dma.direction ? "tx" : "rx", inb_status(dev->base_addr));
}
- outb_control(inb_control(dev->base_addr) & ~(DMAE | TCEN | DIR), dev->base_addr);
+ outb_control(adapter->hcr_val & ~(DMAE | TCEN | DIR),
+ dev);
if (adapter->current_dma.direction) {
dev_kfree_skb(adapter->current_dma.skb, FREE_WRITE);
} else {
struct sk_buff *skb = adapter->current_dma.skb;
if (skb) {
skb->dev = dev;
- if (adapter->current_dma.copy_flag) {
- memcpy(skb_put(skb, adapter->current_dma.length), adapter->dma_buffer, adapter->current_dma.length);
+ if (adapter->current_dma.target) {
+ /* have already done the skb_put() */
+ memcpy(adapter->current_dma.target, adapter->dma_buffer, adapter->current_dma.length);
}
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
@@ -929,7 +893,7 @@
/*
* disable interrupts on the board
*/
- outb_control(0x00, dev->base_addr);
+ outb_control(0, dev);
/*
* clear any pending interrupts
@@ -982,12 +946,7 @@
/*
* enable interrupts on the board
*/
- outb_control(CMDE, dev->base_addr);
-
- /*
- * device is now officially open!
- */
- dev->start = 1;
+ outb_control(CMDE, dev);
/*
* configure adapter memory: we need 10 multicast addresses, default==0
@@ -1032,7 +991,7 @@
}
/* enable burst-mode DMA */
- outb(0x1, dev->base_addr + PORT_AUXDMA);
+ /* outb(0x1, dev->base_addr + PORT_AUXDMA); */
/*
* queue receive commands to provide buffering
@@ -1041,6 +1000,11 @@
if (elp_debug >= 3)
printk("%s: %d receive PCBs active\n", dev->name, adapter->rx_active);
+ /*
+ * device is now officially open!
+ */
+ dev->start = 1;
+
MOD_INC_USE_COUNT;
return 0; /* Always succeed */
@@ -1100,11 +1064,11 @@
cli();
disable_dma(dev->dma);
clear_dma_ff(dev->dma);
- set_dma_mode(dev->dma, 0x08); /* dma memory -> io */
+ set_dma_mode(dev->dma, 0x48); /* dma memory -> io */
set_dma_addr(dev->dma, target);
set_dma_count(dev->dma, nlen);
+ outb_control(adapter->hcr_val | DMAE | TCEN, dev);
enable_dma(dev->dma);
- outb_control(inb_control(dev->base_addr) | DMAE | TCEN, dev->base_addr);
if (elp_debug >= 3)
printk("%s: DMA transfer started\n", dev->name);
@@ -1247,7 +1211,7 @@
/*
* disable interrupts on the board
*/
- outb_control(0x00, dev->base_addr);
+ outb_control(0, dev);
/*
* flag transmitter as busy (i.e. not available)
@@ -1385,22 +1349,20 @@
int addr = dev->base_addr;
const char *name = dev->name;
long flags;
- byte orig_HCR, orig_HSR;
+ byte orig_HSR;
if (check_region(addr, 0xf))
return -1;
- orig_HCR = inb_control(addr);
orig_HSR = inb_status(addr);
if (elp_debug > 0)
printk(search_msg, name, addr);
- if (((orig_HCR == 0xff) && (orig_HSR == 0xff)) ||
- ((orig_HCR & DIR) != (orig_HSR & DIR))) {
+ if (orig_HSR == 0xff) {
if (elp_debug > 0)
printk(notfound_msg, 1);
- return -1; /* It can't be 3c505 if HCR.DIR != HSR.DIR */
+ return -1;
}
/* Enable interrupts - we need timers! */
save_flags(flags);
@@ -1409,34 +1371,32 @@
/* Wait for a while; the adapter may still be booting up */
if (elp_debug > 0)
printk(stilllooking_msg);
- if (orig_HCR & DIR) {
+
+ if (orig_HSR & DIR) {
/* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
- outb_control(orig_HCR & ~DIR, addr);
+ outb(0, dev->base_addr + PORT_CONTROL);
timeout = jiffies + 30;
while (jiffies < timeout);
restore_flags(flags);
if (inb_status(addr) & DIR) {
- outb_control(orig_HCR, addr);
if (elp_debug > 0)
printk(notfound_msg, 2);
return -1;
}
} else {
/* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
- outb_control(orig_HCR | DIR, addr);
+ outb(DIR, dev->base_addr + PORT_CONTROL);
timeout = jiffies + 30;
while (jiffies < timeout);
restore_flags(flags);
if (!(inb_status(addr) & DIR)) {
- outb_control(orig_HCR, addr);
if (elp_debug > 0)
printk(notfound_msg, 3);
return -1;
}
}
/*
- * It certainly looks like a 3c505. If it has DMA enabled, it needs
- * a hard reset. Also, do a hard reset if selected at the compile time.
+ * It certainly looks like a 3c505.
*/
if (elp_debug > 0)
printk(found_msg);
@@ -1516,8 +1476,10 @@
return -ENODEV;
}
+ adapter->send_pcb_semaphore = 0;
+
for (tries1 = 0; tries1 < 3; tries1++) {
- outb_control((inb_control(dev->base_addr) | CMDE) & ~DIR, dev->base_addr);
+ outb_control((adapter->hcr_val | CMDE) & ~DIR, dev);
/* First try to write just one byte, to see if the card is
* responding at all normally.
*/
@@ -1549,8 +1511,8 @@
okay = 1; /* It started */
}
} else {
- /* Otherwise, it must just be in a strange state. We probably
- * need to kick it.
+ /* Otherwise, it must just be in a strange
+ * state. We probably need to kick it.
*/
printk("3c505 is sulking\n");
}
@@ -1586,8 +1548,8 @@
* and try again.
*/
printk(KERN_INFO "%s: resetting adapter\n", dev->name);
- outb_control(inb_control(dev->base_addr) | FLSH | ATTN, dev->base_addr);
- outb_control(inb_control(dev->base_addr) & ~(FLSH | ATTN), dev->base_addr);
+ outb_control(adapter->hcr_val | FLSH | ATTN, dev);
+ outb_control(adapter->hcr_val & ~(FLSH | ATTN), dev);
}
printk("%s: failed to initialise 3c505\n", dev->name);
return -ENODEV;
@@ -1597,15 +1559,14 @@
int rpt = autoirq_report(0);
if (dev->irq != rpt) {
printk("%s: warning, irq %d configured but %d detected\n", dev->name, dev->irq, rpt);
- return -ENODEV;
}
/* if dev->irq == autoirq_report(0), all is well */
- } else /* No preset IRQ; just use what we can detect */
+ } else /* No preset IRQ; just use what we can detect */
dev->irq = autoirq_report(0);
- switch (dev->irq) { /* Legal, sane? */
+ switch (dev->irq) { /* Legal, sane? */
case 0:
- printk("%s: No IRQ reported by autoirq_report().\n", dev->name);
- printk("%s: Check the jumpers of your 3c505 board.\n", dev->name);
+ printk("%s: IRQ probe failed: check 3c505 jumpers.\n",
+ dev->name);
return -ENODEV;
case 1:
case 6:
@@ -1619,7 +1580,7 @@
* Now we have the IRQ number so we can disable the interrupts from
* the board until the board is opened.
*/
- outb_control(inb_control(dev->base_addr) & ~CMDE, dev->base_addr);
+ outb_control(adapter->hcr_val & ~CMDE, dev);
/*
* copy ethernet address into structure
@@ -1627,8 +1588,16 @@
for (i = 0; i < 6; i++)
dev->dev_addr[i] = adapter->rx_pcb.data.eth_addr[i];
- /* set up the DMA channel */
- dev->dma = ELP_DMA;
+ /* find a DMA channel */
+ if (!dev->dma) {
+ if (dev->mem_start) {
+ dev->dma = dev->mem_start & 7;
+ }
+ else {
+ printk(KERN_WARNING "%s: warning, DMA channel not specified, using default\n", dev->name);
+ dev->dma = ELP_DMA;
+ }
+ }
/*
* print remainder of startup message
@@ -1649,7 +1618,7 @@
!receive_pcb(dev, &adapter->rx_pcb) ||
(adapter->rx_pcb.command != CMD_ADAPTER_INFO_RESPONSE) ||
(adapter->rx_pcb.length != 10)) {
- printk("%s: not responding to second PCB\n", dev->name);
+ printk("not responding to second PCB\n");
}
printk("rev %d.%d, %dk\n", adapter->rx_pcb.data.info.major_vers, adapter->rx_pcb.data.info.minor_vers, adapter->rx_pcb.data.info.RAM_sz);
@@ -1687,46 +1656,61 @@
}
#ifdef MODULE
-static char devicename[9] = {0,};
-static struct device dev_3c505 =
+#define NAMELEN 9
+static char devicename[ELP_MAX_CARDS][NAMELEN] = {0,};
+static struct device dev_3c505[ELP_MAX_CARDS] =
{
- devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ NULL, /* device name is inserted by net_init.c */
0, 0, 0, 0,
0, 0,
0, 0, 0, NULL, elplus_probe};
-int io = 0x300;
-int irq = 0;
+static int io[ELP_MAX_CARDS] = { 0, };
+static int irq[ELP_MAX_CARDS] = { 0, };
+static int dma[ELP_MAX_CARDS] = { 0, };
int init_module(void)
{
- if (io == 0)
- printk("3c505: You should not use auto-probing with insmod!\n");
- dev_3c505.base_addr = io;
- dev_3c505.irq = irq;
- if (register_netdev(&dev_3c505) != 0) {
- return -EIO;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) {
+ struct device *dev = &dev_3c505[this_dev];
+ dev->name = devicename[this_dev];
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ if (dma[this_dev]) {
+ dev->dma = dma[this_dev];
+ } else {
+ dev->dma = ELP_DMA;
+ printk(KERN_WARNING "3c505.c: warning, using default DMA channel,\n");
+ }
+ if (io[this_dev] == 0) {
+ if (this_dev) break;
+ printk(KERN_NOTICE "3c505.c: module autoprobe not recommended, give io=xx.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "3c505.c: Failed to register card at 0x%x.\n", io[this_dev]);
+ if (found != 0) return 0;
+ return -ENXIO;
+ }
+ found++;
}
return 0;
}
void cleanup_module(void)
{
- unregister_netdev(&dev_3c505);
- kfree(dev_3c505.priv);
- dev_3c505.priv = NULL;
-
- /* If we don't do this, we can't re-insmod it later. */
- release_region(dev_3c505.base_addr, ELP_IO_EXTENT);
+ int this_dev;
+
+ for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) {
+ struct device *dev = &dev_3c505[this_dev];
+ if (dev->priv != NULL) {
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(dev->base_addr, ELP_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
}
#endif /* MODULE */
-
-
-/*
- * Local Variables:
- * c-file-style: "linux"
- * tab-width: 8
- * compile-command: "gcc -D__KERNEL__ -I/discs/bibble/src/linux-1.3.69/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -c 3c505.c"
- * End:
- */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov