diff options
author | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2011-07-13 15:38:08 -0700 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2011-08-10 20:04:12 -0700 |
commit | 115978859272b958366d4a08c99a24f9625fa663 (patch) | |
tree | e3eb08a76c0180bd5efa36985306878cbea5845f /drivers/net/ethernet/i825xx | |
parent | ae150435b59e68de00546330241727f2fec54517 (diff) | |
download | linux-115978859272b958366d4a08c99a24f9625fa663.tar.bz2 |
i825xx: Move the Intel 82586/82593/82596 based drivers
Move the drivers that use the i82586/i82593/i82596 chipsets into
drivers/net/ethernet/i825xx/ and make the necessary Kconfig and
Makefile changes. There were 4 3Com drivers which were initially
moved into 3com/, which now reside in i825xx since they all used
the i82586 chip.
CC: Philip Blundell <philb@gnu.org>
CC: Russell King <linux@arm.linux.org.uk>
CC: <aris@cathedrallabs.org>
CC: Donald Becker <becker@scyld.com>
CC: Chris Beauregard <cpbeaure@undergrad.math.uwaterloo.ca>
CC: Richard Procter <rnp@paradise.net.nz>
CC: Andries Brouwer <aeb@cwi.nl>
CC: "M.Hipp" <hippm@informatik.uni-tuebingen.de>
CC: Richard Hirst <richard@sleepie.demon.co.uk>
CC: Sam Creasey <sammy@oh.verio.com>
CC: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/i825xx')
24 files changed, 20528 insertions, 0 deletions
diff --git a/drivers/net/ethernet/i825xx/3c505.c b/drivers/net/ethernet/i825xx/3c505.c new file mode 100644 index 000000000000..88d766ee0e1b --- /dev/null +++ b/drivers/net/ethernet/i825xx/3c505.c @@ -0,0 +1,1673 @@ +/* + * Linux Ethernet device driver for the 3Com Etherlink Plus (3C505) + * By Craig Southeren, Juha Laiho and Philip Blundell + * + * 3c505.c This module implements an interface to the 3Com + * Etherlink Plus (3c505) Ethernet card. Linux device + * driver interface reverse engineered from the Linux 3C509 + * device drivers. Some 3C505 information gleaned from + * the Crynwr packet driver. Still this driver would not + * be here without 3C505 technical reference provided by + * 3Com. + * + * $Id: 3c505.c,v 1.10 1996/04/16 13:06:27 phil Exp $ + * + * Authors: Linux 3c505 device driver by + * Craig Southeren, <craigs@ineluki.apana.org.au> + * Final debugging by + * Andrew Tridgell, <tridge@nimbus.anu.edu.au> + * Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by + * Juha Laiho, <jlaiho@ichaos.nullnet.fi> + * Linux 3C509 driver by + * Donald Becker, <becker@super.org> + * (Now at <becker@scyld.com>) + * Crynwr packet driver by + * Krishnan Gopalan and Gregg Stefancik, + * Clemson University Engineering Computer Operations. + * Portions of the code have been adapted from the 3c505 + * driver for NCSA Telnet by Bruce Orchard and later + * modified by Warren Van Houten and krus@diku.dk. + * 3C505 technical information provided by + * Terry Murphy, of 3Com Network Adapter Division + * Linux 1.3.0 changes by + * Alan Cox <Alan.Cox@linux.org> + * More debugging, DMA support, currently maintained by + * Philip Blundell <philb@gnu.org> + * Multicard/soft configurable dma channel/rev 2 hardware support + * by Christopher Collins <ccollins@pcug.org.au> + * Ethtool support (jgarzik), 11/17/2001 + */ + +#define DRV_NAME "3c505" +#define DRV_VERSION "1.10a" + + +/* 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 + * used to buffer received packets. It might seem at first that more buffers + * 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. 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 + * board will send us a "packet received" PCB and then add the data for that + * packet to the DMA queue. If a DMA transfer is not already in progress, we + * set one up to start uploading the data. We have to maintain a list of + * backlogged receive packets, because the card may decide to tell us about + * a newly-arrived packet at any time, and we may not be able to start a DMA + * transfer immediately (ie one may already be going on). We can't NAK the + * PCB, because then it would throw the packet away. + * + * Trying to send a PCB to the card at the wrong moment seems to have bad + * effects. If we send it a transmit PCB while a receive DMA is happening, + * it will just NAK the PCB and so we will have wasted our time. Worse, it + * sometimes seems to interrupt the transfer. The majority of the low-level + * code is protected by one huge semaphore -- "busy" -- which is set whenever + * it probably isn't safe to do anything to the card. The receive routine + * must gain a lock on "busy" before it can start a DMA transfer, and the + * transmit routine must gain a lock before it sends the first PCB to the card. + * The send_pcb() routine also has an internal semaphore to protect it against + * being re-entered (which would be disastrous) -- this is needed because + * several things can happen asynchronously (re-priming the receiver and + * asking the card for statistics, for example). send_pcb() will also refuse + * to talk to the card at all if a DMA upload is happening. The higher-level + * networking code will reschedule a later retry if some part of the driver + * is blocked. In practice, this doesn't seem to happen very often. + */ + +/* 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 + * to make it more reliable, and secondly to add DMA mode. Many things could + * probably be done better; the concurrency protection is particularly awful. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/in.h> +#include <linux/ioport.h> +#include <linux/spinlock.h> +#include <linux/ethtool.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/gfp.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/init.h> + +#include "3c505.h" + +/********************************************************* + * + * define debug messages here as common strings to reduce space + * + *********************************************************/ + +#define filename __FILE__ + +#define timeout_msg "*** timeout at %s:%s (line %d) ***\n" +#define TIMEOUT_MSG(lineno) \ + pr_notice(timeout_msg, filename, __func__, (lineno)) + +#define invalid_pcb_msg "*** invalid pcb length %d at %s:%s (line %d) ***\n" +#define INVALID_PCB_MSG(len) \ + pr_notice(invalid_pcb_msg, (len), filename, __func__, __LINE__) + +#define search_msg "%s: Looking for 3c505 adapter at address %#x..." + +#define stilllooking_msg "still looking..." + +#define found_msg "found.\n" + +#define notfound_msg "not found (reason = %d)\n" + +#define couldnot_msg "%s: 3c505 not found\n" + +/********************************************************* + * + * various other debug stuff + * + *********************************************************/ + +#ifdef ELP_DEBUG +static int elp_debug = ELP_DEBUG; +#else +static int elp_debug; +#endif +#define debug elp_debug + +/* + * 0 = no messages (well, some) + * 1 = messages when high level commands performed + * 2 = messages when low level commands performed + * 3 = messages when interrupts received + */ + +/***************************************************************** + * + * List of I/O-addresses we try to auto-sense + * Last element MUST BE 0! + *****************************************************************/ + +static int addr_list[] __initdata = {0x300, 0x280, 0x310, 0}; + +/* Dma Memory related stuff */ + +static unsigned long dma_mem_alloc(int size) +{ + int order = get_order(size); + return __get_dma_pages(GFP_KERNEL, order); +} + + +/***************************************************************** + * + * Functions for I/O (note the inline !) + * + *****************************************************************/ + +static inline unsigned char inb_status(unsigned int base_addr) +{ + return inb(base_addr + PORT_STATUS); +} + +static inline int inb_command(unsigned int base_addr) +{ + return inb(base_addr + PORT_COMMAND); +} + +static inline void outb_control(unsigned char val, struct net_device *dev) +{ + outb(val, dev->base_addr + PORT_CONTROL); + ((elp_device *)(netdev_priv(dev)))->hcr_val = val; +} + +#define HCR_VAL(x) (((elp_device *)(netdev_priv(x)))->hcr_val) + +static inline void outb_command(unsigned char val, unsigned int base_addr) +{ + outb(val, base_addr + PORT_COMMAND); +} + +static inline unsigned int backlog_next(unsigned int n) +{ + return (n + 1) % BACKLOG_SIZE; +} + +/***************************************************************** + * + * useful functions for accessing the adapter + * + *****************************************************************/ + +/* + * use this routine when accessing the ASF bits as they are + * changed asynchronously by the adapter + */ + +/* get adapter PCB status */ +#define GET_ASF(addr) \ + (get_status(addr)&ASF_PCB_MASK) + +static inline int get_status(unsigned int base_addr) +{ + unsigned long timeout = jiffies + 10*HZ/100; + register int stat1; + do { + stat1 = inb_status(base_addr); + } while (stat1 != inb_status(base_addr) && time_before(jiffies, timeout)); + if (time_after_eq(jiffies, timeout)) + TIMEOUT_MSG(__LINE__); + return stat1; +} + +static inline void set_hsf(struct net_device *dev, int hsf) +{ + elp_device *adapter = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&adapter->lock, flags); + outb_control((HCR_VAL(dev) & ~HSF_PCB_MASK) | hsf, dev); + spin_unlock_irqrestore(&adapter->lock, flags); +} + +static bool start_receive(struct net_device *, pcb_struct *); + +static inline void adapter_reset(struct net_device *dev) +{ + unsigned long timeout; + elp_device *adapter = netdev_priv(dev); + unsigned char orig_hcr = adapter->hcr_val; + + outb_control(0, dev); + + if (inb_status(dev->base_addr) & ACRF) { + do { + inb_command(dev->base_addr); + timeout = jiffies + 2*HZ/100; + while (time_before_eq(jiffies, timeout) && !(inb_status(dev->base_addr) & ACRF)); + } while (inb_status(dev->base_addr) & ACRF); + set_hsf(dev, HSF_PCB_NAK); + } + outb_control(adapter->hcr_val | ATTN | DIR, dev); + mdelay(10); + outb_control(adapter->hcr_val & ~ATTN, dev); + mdelay(10); + outb_control(adapter->hcr_val | FLSH, dev); + mdelay(10); + outb_control(adapter->hcr_val & ~FLSH, dev); + mdelay(10); + + outb_control(orig_hcr, dev); + if (!start_receive(dev, &adapter->tx_pcb)) + pr_err("%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. + */ +static inline void check_3c505_dma(struct net_device *dev) +{ + elp_device *adapter = netdev_priv(dev); + if (adapter->dmaing && time_after(jiffies, adapter->current_dma.start_time + 10)) { + unsigned long flags, f; + pr_err("%s: DMA %s timed out, %d bytes left\n", dev->name, + adapter->current_dma.direction ? "download" : "upload", + get_dma_residue(dev->dma)); + spin_lock_irqsave(&adapter->lock, flags); + adapter->dmaing = 0; + adapter->busy = 0; + + f=claim_dma_lock(); + disable_dma(dev->dma); + release_dma_lock(f); + + if (adapter->rx_active) + adapter->rx_active--; + outb_control(adapter->hcr_val & ~(DMAE | TCEN | DIR), dev); + spin_unlock_irqrestore(&adapter->lock, flags); + } +} + +/* Primitive functions used by send_pcb() */ +static inline bool send_pcb_slow(unsigned int base_addr, unsigned char byte) +{ + unsigned long timeout; + outb_command(byte, base_addr); + for (timeout = jiffies + 5*HZ/100; time_before(jiffies, timeout);) { + if (inb_status(base_addr) & HCRE) + return false; + } + pr_warning("3c505: send_pcb_slow timed out\n"); + return true; +} + +static inline bool send_pcb_fast(unsigned int base_addr, unsigned char byte) +{ + unsigned int timeout; + outb_command(byte, base_addr); + for (timeout = 0; timeout < 40000; timeout++) { + if (inb_status(base_addr) & HCRE) + return false; + } + pr_warning("3c505: send_pcb_fast timed out\n"); + return true; +} + +/* Check to see if the receiver needs restarting, and kick it if so */ +static inline void prime_rx(struct net_device *dev) +{ + elp_device *adapter = netdev_priv(dev); + while (adapter->rx_active < ELP_RX_PCBS && netif_running(dev)) { + if (!start_receive(dev, &adapter->itx_pcb)) + break; + } +} + +/***************************************************************** + * + * send_pcb + * Send a PCB to the adapter. + * + * output byte to command reg --<--+ + * wait until HCRE is non zero | + * loop until all bytes sent -->--+ + * set HSF1 and HSF2 to 1 + * output pcb length + * wait until ASF give ACK or NAK + * set HSF1 and HSF2 to 0 + * + *****************************************************************/ + +/* This can be quite slow -- the adapter is allowed to take up to 40ms + * to respond to the initial interrupt. + * + * We run initially with interrupts turned on, but with a semaphore set + * so that nobody tries to re-enter this code. Once the first byte has + * gone through, we turn interrupts off and then send the others (the + * timeout is reduced to 500us). + */ + +static bool send_pcb(struct net_device *dev, pcb_struct * pcb) +{ + int i; + unsigned long timeout; + elp_device *adapter = netdev_priv(dev); + unsigned long flags; + + check_3c505_dma(dev); + + if (adapter->dmaing && adapter->current_dma.direction == 0) + return false; + + /* Avoid contention */ + if (test_and_set_bit(1, &adapter->send_pcb_semaphore)) { + if (elp_debug >= 3) { + pr_debug("%s: send_pcb entered while threaded\n", dev->name); + } + return false; + } + /* + * load each byte into the command register and + * wait for the HCRE bit to indicate the adapter + * had read the byte + */ + set_hsf(dev, 0); + + if (send_pcb_slow(dev->base_addr, pcb->command)) + goto abort; + + spin_lock_irqsave(&adapter->lock, flags); + + if (send_pcb_fast(dev->base_addr, pcb->length)) + goto sti_abort; + + for (i = 0; i < pcb->length; i++) { + if (send_pcb_fast(dev->base_addr, pcb->data.raw[i])) + goto sti_abort; + } + + outb_control(adapter->hcr_val | 3, dev); /* signal end of PCB */ + outb_command(2 + pcb->length, dev->base_addr); + + /* now wait for the acknowledgement */ + spin_unlock_irqrestore(&adapter->lock, flags); + + for (timeout = jiffies + 5*HZ/100; time_before(jiffies, timeout);) { + switch (GET_ASF(dev->base_addr)) { + case ASF_PCB_ACK: + adapter->send_pcb_semaphore = 0; + return true; + + case ASF_PCB_NAK: +#ifdef ELP_DEBUG + pr_debug("%s: send_pcb got NAK\n", dev->name); +#endif + goto abort; + } + } + + if (elp_debug >= 1) + pr_debug("%s: timeout waiting for PCB acknowledge (status %02x)\n", + dev->name, inb_status(dev->base_addr)); + goto abort; + + sti_abort: + spin_unlock_irqrestore(&adapter->lock, flags); + abort: + adapter->send_pcb_semaphore = 0; + return false; +} + + +/***************************************************************** + * + * receive_pcb + * Read a PCB from the adapter + * + * wait for ACRF to be non-zero ---<---+ + * input a byte | + * if ASF1 and ASF2 were not both one | + * before byte was read, loop --->---+ + * set HSF1 and HSF2 for ack + * + *****************************************************************/ + +static bool receive_pcb(struct net_device *dev, pcb_struct * pcb) +{ + int i, j; + int total_length; + int stat; + unsigned long timeout; + unsigned long flags; + + elp_device *adapter = netdev_priv(dev); + + set_hsf(dev, 0); + + /* get the command code */ + timeout = jiffies + 2*HZ/100; + while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && time_before(jiffies, timeout)); + if (time_after_eq(jiffies, timeout)) { + TIMEOUT_MSG(__LINE__); + return false; + } + pcb->command = inb_command(dev->base_addr); + + /* read the data length */ + timeout = jiffies + 3*HZ/100; + while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && time_before(jiffies, timeout)); + if (time_after_eq(jiffies, timeout)) { + TIMEOUT_MSG(__LINE__); + pr_info("%s: status %02x\n", dev->name, stat); + return false; + } + pcb->length = inb_command(dev->base_addr); + + if (pcb->length > MAX_PCB_DATA) { + INVALID_PCB_MSG(pcb->length); + adapter_reset(dev); + return false; + } + /* read the data */ + spin_lock_irqsave(&adapter->lock, flags); + for (i = 0; i < MAX_PCB_DATA; i++) { + for (j = 0; j < 20000; j++) { + stat = get_status(dev->base_addr); + if (stat & ACRF) + break; + } + pcb->data.raw[i] = inb_command(dev->base_addr); + if ((stat & ASF_PCB_MASK) == ASF_PCB_END || j >= 20000) + break; + } + spin_unlock_irqrestore(&adapter->lock, flags); + if (i >= MAX_PCB_DATA) { + INVALID_PCB_MSG(i); + return false; + } + if (j >= 20000) { + TIMEOUT_MSG(__LINE__); + return false; + } + /* the last "data" byte was really the length! */ + total_length = pcb->data.raw[i]; + + /* safety check total length vs data length */ + if (total_length != (pcb->length + 2)) { + if (elp_debug >= 2) + pr_warning("%s: mangled PCB received\n", dev->name); + set_hsf(dev, HSF_PCB_NAK); + return false; + } + + if (pcb->command == CMD_RECEIVE_PACKET_COMPLETE) { + if (test_and_set_bit(0, (void *) &adapter->busy)) { + if (backlog_next(adapter->rx_backlog.in) == adapter->rx_backlog.out) { + set_hsf(dev, HSF_PCB_NAK); + pr_warning("%s: PCB rejected, transfer in progress and backlog full\n", dev->name); + pcb->command = 0; + return true; + } else { + pcb->command = 0xff; + } + } + } + set_hsf(dev, HSF_PCB_ACK); + return true; +} + +/****************************************************** + * + * queue a receive command on the adapter so we will get an + * interrupt when a packet is received. + * + ******************************************************/ + +static bool start_receive(struct net_device *dev, pcb_struct * tx_pcb) +{ + bool status; + elp_device *adapter = netdev_priv(dev); + + if (elp_debug >= 3) + pr_debug("%s: restarting receiver\n", dev->name); + tx_pcb->command = CMD_RECEIVE_PACKET; + tx_pcb->length = sizeof(struct Rcv_pkt); + tx_pcb->data.rcv_pkt.buf_seg + = tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */ + tx_pcb->data.rcv_pkt.buf_len = 1600; + tx_pcb->data.rcv_pkt.timeout = 0; /* set timeout to zero */ + status = send_pcb(dev, tx_pcb); + if (status) + adapter->rx_active++; + return status; +} + +/****************************************************** + * + * extract a packet from the adapter + * this routine is only called from within the interrupt + * service routine, so no cli/sti calls are needed + * note that the length is always assumed to be even + * + ******************************************************/ + +static void receive_packet(struct net_device *dev, int len) +{ + int rlen; + elp_device *adapter = netdev_priv(dev); + void *target; + struct sk_buff *skb; + unsigned long flags; + + rlen = (len + 1) & ~1; + skb = dev_alloc_skb(rlen + 2); + + if (!skb) { + pr_warning("%s: memory squeeze, dropping packet\n", dev->name); + target = adapter->dma_buffer; + adapter->current_dma.target = NULL; + /* FIXME: stats */ + return; + } + + skb_reserve(skb, 2); + target = skb_put(skb, rlen); + if ((unsigned long)(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 (test_and_set_bit(0, (void *) &adapter->dmaing)) + pr_err("%s: rx blocked, DMA in progress, dir %d\n", + dev->name, adapter->current_dma.direction); + + adapter->current_dma.direction = 0; + adapter->current_dma.length = rlen; + adapter->current_dma.skb = skb; + adapter->current_dma.start_time = jiffies; + + outb_control(adapter->hcr_val | DIR | TCEN | DMAE, dev); + + flags=claim_dma_lock(); + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, 0x04); /* dma read */ + set_dma_addr(dev->dma, isa_virt_to_bus(target)); + set_dma_count(dev->dma, rlen); + enable_dma(dev->dma); + release_dma_lock(flags); + + if (elp_debug >= 3) { + pr_debug("%s: rx DMA transfer started\n", dev->name); + } + + if (adapter->rx_active) + adapter->rx_active--; + + if (!adapter->busy) + pr_warning("%s: receive_packet called, busy not set.\n", dev->name); +} + +/****************************************************** + * + * interrupt handler + * + ******************************************************/ + +static irqreturn_t elp_interrupt(int irq, void *dev_id) +{ + int len; + int dlen; + int icount = 0; + struct net_device *dev = dev_id; + elp_device *adapter = netdev_priv(dev); + unsigned long timeout; + + spin_lock(&adapter->lock); + + do { + /* + * has a DMA transfer finished? + */ + if (inb_status(dev->base_addr) & DONE) { + if (!adapter->dmaing) + pr_warning("%s: phantom DMA completed\n", dev->name); + + if (elp_debug >= 3) + pr_debug("%s: %s DMA complete, status %02x\n", dev->name, + adapter->current_dma.direction ? "tx" : "rx", + inb_status(dev->base_addr)); + + outb_control(adapter->hcr_val & ~(DMAE | TCEN | DIR), dev); + if (adapter->current_dma.direction) { + dev_kfree_skb_irq(adapter->current_dma.skb); + } else { + struct sk_buff *skb = adapter->current_dma.skb; + if (skb) { + 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); + dev->stats.rx_bytes += skb->len; + netif_rx(skb); + } + } + adapter->dmaing = 0; + if (adapter->rx_backlog.in != adapter->rx_backlog.out) { + int t = adapter->rx_backlog.length[adapter->rx_backlog.out]; + adapter->rx_backlog.out = backlog_next(adapter->rx_backlog.out); + if (elp_debug >= 2) + pr_debug("%s: receiving backlogged packet (%d)\n", dev->name, t); + receive_packet(dev, t); + } else { + adapter->busy = 0; + } + } else { + /* has one timed out? */ + check_3c505_dma(dev); + } + + /* + * receive a PCB from the adapter + */ + timeout = jiffies + 3*HZ/100; + while ((inb_status(dev->base_addr) & ACRF) != 0 && time_before(jiffies, timeout)) { + if (receive_pcb(dev, &adapter->irx_pcb)) { + switch (adapter->irx_pcb.command) + { + case 0: + break; + /* + * received a packet - this must be handled fast + */ + case 0xff: + case CMD_RECEIVE_PACKET_COMPLETE: + /* if the device isn't open, don't pass packets up the stack */ + if (!netif_running(dev)) + break; + len = adapter->irx_pcb.data.rcv_resp.pkt_len; + dlen = adapter->irx_pcb.data.rcv_resp.buf_len; + if (adapter->irx_pcb.data.rcv_resp.timeout != 0) { + pr_err("%s: interrupt - packet not received correctly\n", dev->name); + } else { + if (elp_debug >= 3) { + pr_debug("%s: interrupt - packet received of length %i (%i)\n", + dev->name, len, dlen); + } + if (adapter->irx_pcb.command == 0xff) { + if (elp_debug >= 2) + pr_debug("%s: adding packet to backlog (len = %d)\n", + dev->name, dlen); + adapter->rx_backlog.length[adapter->rx_backlog.in] = dlen; + adapter->rx_backlog.in = backlog_next(adapter->rx_backlog.in); + } else { + receive_packet(dev, dlen); + } + if (elp_debug >= 3) + pr_debug("%s: packet received\n", dev->name); + } + break; + + /* + * 82586 configured correctly + */ + case CMD_CONFIGURE_82586_RESPONSE: + adapter->got[CMD_CONFIGURE_82586] = 1; + if (elp_debug >= 3) + pr_debug("%s: interrupt - configure response received\n", dev->name); + break; + + /* + * Adapter memory configuration + */ + case CMD_CONFIGURE_ADAPTER_RESPONSE: + adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1; + if (elp_debug >= 3) + pr_debug("%s: Adapter memory configuration %s.\n", dev->name, + adapter->irx_pcb.data.failed ? "failed" : "succeeded"); + break; + + /* + * Multicast list loading + */ + case CMD_LOAD_MULTICAST_RESPONSE: + adapter->got[CMD_LOAD_MULTICAST_LIST] = 1; + if (elp_debug >= 3) + pr_debug("%s: Multicast address list loading %s.\n", dev->name, + adapter->irx_pcb.data.failed ? "failed" : "succeeded"); + break; + + /* + * Station address setting + */ + case CMD_SET_ADDRESS_RESPONSE: + adapter->got[CMD_SET_STATION_ADDRESS] = 1; + if (elp_debug >= 3) + pr_debug("%s: Ethernet address setting %s.\n", dev->name, + adapter->irx_pcb.data.failed ? "failed" : "succeeded"); + break; + + + /* + * received board statistics + */ + case CMD_NETWORK_STATISTICS_RESPONSE: + dev->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv; + dev->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit; + dev->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC; + dev->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align; + dev->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun; + dev->stats.rx_over_errors += adapter->irx_pcb.data.netstat.err_res; + adapter->got[CMD_NETWORK_STATISTICS] = 1; + if (elp_debug >= 3) + pr_debug("%s: interrupt - statistics response received\n", dev->name); + break; + + /* + * sent a packet + */ + case CMD_TRANSMIT_PACKET_COMPLETE: + if (elp_debug >= 3) + pr_debug("%s: interrupt - packet sent\n", dev->name); + if (!netif_running(dev)) + break; + switch (adapter->irx_pcb.data.xmit_resp.c_stat) { + case 0xffff: + dev->stats.tx_aborted_errors++; + pr_info("%s: transmit timed out, network cable problem?\n", dev->name); + break; + case 0xfffe: + dev->stats.tx_fifo_errors++; + pr_info("%s: transmit timed out, FIFO underrun\n", dev->name); + break; + } + netif_wake_queue(dev); + break; + + /* + * some unknown PCB + */ + default: + pr_debug("%s: unknown PCB received - %2.2x\n", + dev->name, adapter->irx_pcb.command); + break; + } + } else { + pr_warning("%s: failed to read PCB on interrupt\n", dev->name); + adapter_reset(dev); + } + } + + } while (icount++ < 5 && (inb_status(dev->base_addr) & (ACRF | DONE))); + + prime_rx(dev); + + /* + * indicate no longer in interrupt routine + */ + spin_unlock(&adapter->lock); + return IRQ_HANDLED; +} + + +/****************************************************** + * + * open the board + * + ******************************************************/ + +static int elp_open(struct net_device *dev) +{ + elp_device *adapter = netdev_priv(dev); + int retval; + + if (elp_debug >= 3) + pr_debug("%s: request to open device\n", dev->name); + + /* + * make sure we actually found the device + */ + if (adapter == NULL) { + pr_err("%s: Opening a non-existent physical device\n", dev->name); + return -EAGAIN; + } + /* + * disable interrupts on the board + */ + outb_control(0, dev); + + /* + * clear any pending interrupts + */ + inb_command(dev->base_addr); + adapter_reset(dev); + + /* + * no receive PCBs active + */ + adapter->rx_active = 0; + + adapter->busy = 0; + adapter->send_pcb_semaphore = 0; + adapter->rx_backlog.in = 0; + adapter->rx_backlog.out = 0; + + spin_lock_init(&adapter->lock); + + /* + * install our interrupt service routine + */ + if ((retval = request_irq(dev->irq, elp_interrupt, 0, dev->name, dev))) { + pr_err("%s: could not allocate IRQ%d\n", dev->name, dev->irq); + return retval; + } + if ((retval = request_dma(dev->dma, dev->name))) { + free_irq(dev->irq, dev); + pr_err("%s: could not allocate DMA%d channel\n", dev->name, dev->dma); + return retval; + } + adapter->dma_buffer = (void *) dma_mem_alloc(DMA_BUFFER_SIZE); + if (!adapter->dma_buffer) { + pr_err("%s: could not allocate DMA buffer\n", dev->name); + free_dma(dev->dma); + free_irq(dev->irq, dev); + return -ENOMEM; + } + adapter->dmaing = 0; + + /* + * enable interrupts on the board + */ + outb_control(CMDE, dev); + + /* + * configure adapter memory: we need 10 multicast addresses, default==0 + */ + if (elp_debug >= 3) + pr_debug("%s: sending 3c505 memory configuration command\n", dev->name); + adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY; + adapter->tx_pcb.data.memconf.cmd_q = 10; + adapter->tx_pcb.data.memconf.rcv_q = 20; + adapter->tx_pcb.data.memconf.mcast = 10; + adapter->tx_pcb.data.memconf.frame = 20; + adapter->tx_pcb.data.memconf.rcv_b = 20; + adapter->tx_pcb.data.memconf.progs = 0; + adapter->tx_pcb.length = sizeof(struct Memconf); + adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0; + if (!send_pcb(dev, &adapter->tx_pcb)) + pr_err("%s: couldn't send memory configuration command\n", dev->name); + else { + unsigned long timeout = jiffies + TIMEOUT; + while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && time_before(jiffies, timeout)); + if (time_after_eq(jiffies, timeout)) + TIMEOUT_MSG(__LINE__); + } + + + /* + * configure adapter to receive broadcast messages and wait for response + */ + if (elp_debug >= 3) + pr_debug("%s: sending 82586 configure command\n", dev->name); + adapter->tx_pcb.command = CMD_CONFIGURE_82586; + adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD; + adapter->tx_pcb.length = 2; + adapter->got[CMD_CONFIGURE_82586] = 0; + if (!send_pcb(dev, &adapter->tx_pcb)) + pr_err("%s: couldn't send 82586 configure command\n", dev->name); + else { + unsigned long timeout = jiffies + TIMEOUT; + while (adapter->got[CMD_CONFIGURE_82586] == 0 && time_before(jiffies, timeout)); + if (time_after_eq(jiffies, timeout)) + TIMEOUT_MSG(__LINE__); + } + + /* enable burst-mode DMA */ + /* outb(0x1, dev->base_addr + PORT_AUXDMA); */ + + /* + * queue receive commands to provide buffering + */ + prime_rx(dev); + if (elp_debug >= 3) + pr_debug("%s: %d receive PCBs active\n", dev->name, adapter->rx_active); + + /* + * device is now officially open! + */ + + netif_start_queue(dev); + return 0; +} + + +/****************************************************** + * + * send a packet to the adapter + * + ******************************************************/ + +static netdev_tx_t send_packet(struct net_device *dev, struct sk_buff *skb) +{ + elp_device *adapter = netdev_priv(dev); + unsigned long target; + unsigned long flags; + + /* + * make sure the length is even and no shorter than 60 bytes + */ + unsigned int nlen = (((skb->len < 60) ? 60 : skb->len) + 1) & (~1); + + if (test_and_set_bit(0, (void *) &adapter->busy)) { + if (elp_debug >= 2) + pr_debug("%s: transmit blocked\n", dev->name); + return false; + } + + dev->stats.tx_bytes += nlen; + + /* + * send the adapter a transmit packet command. Ignore segment and offset + * and make sure the length is even + */ + adapter->tx_pcb.command = CMD_TRANSMIT_PACKET; + adapter->tx_pcb.length = sizeof(struct Xmit_pkt); + adapter->tx_pcb.data.xmit_pkt.buf_ofs + = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */ + adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen; + + if (!send_pcb(dev, &adapter->tx_pcb)) { + adapter->busy = 0; + return false; + } + /* if this happens, we die */ + if (test_and_set_bit(0, (void *) &adapter->dmaing)) + pr_debug("%s: tx: DMA %d in progress\n", dev->name, adapter->current_dma.direction); + + adapter->current_dma.direction = 1; + adapter->current_dma.start_time = jiffies; + + if ((unsigned long)(skb->data + nlen) >= MAX_DMA_ADDRESS || nlen != skb->len) { + skb_copy_from_linear_data(skb, adapter->dma_buffer, nlen); + memset(adapter->dma_buffer+skb->len, 0, nlen-skb->len); + target = isa_virt_to_bus(adapter->dma_buffer); + } + else { + target = isa_virt_to_bus(skb->data); + } + adapter->current_dma.skb = skb; + + flags=claim_dma_lock(); + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + 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); + release_dma_lock(flags); + + if (elp_debug >= 3) + pr_debug("%s: DMA transfer started\n", dev->name); + + return true; +} + +/* + * The upper layer thinks we timed out + */ + +static void elp_timeout(struct net_device *dev) +{ + int stat; + + stat = inb_status(dev->base_addr); + pr_warning("%s: transmit timed out, lost %s?\n", dev->name, + (stat & ACRF) ? "interrupt" : "command"); + if (elp_debug >= 1) + pr_debug("%s: status %#02x\n", dev->name, stat); + dev->trans_start = jiffies; /* prevent tx timeout */ + dev->stats.tx_dropped++; + netif_wake_queue(dev); +} + +/****************************************************** + * + * start the transmitter + * return 0 if sent OK, else return 1 + * + ******************************************************/ + +static netdev_tx_t elp_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long flags; + elp_device *adapter = netdev_priv(dev); + + spin_lock_irqsave(&adapter->lock, flags); + check_3c505_dma(dev); + + if (elp_debug >= 3) + pr_debug("%s: request to send packet of length %d\n", dev->name, (int) skb->len); + + netif_stop_queue(dev); + + /* + * send the packet at skb->data for skb->len + */ + if (!send_packet(dev, skb)) { + if (elp_debug >= 2) { + pr_debug("%s: failed to transmit packet\n", dev->name); + } + spin_unlock_irqrestore(&adapter->lock, flags); + return NETDEV_TX_BUSY; + } + if (elp_debug >= 3) + pr_debug("%s: packet of length %d sent\n", dev->name, (int) skb->len); + + prime_rx(dev); + spin_unlock_irqrestore(&adapter->lock, flags); + netif_start_queue(dev); + return NETDEV_TX_OK; +} + +/****************************************************** + * + * return statistics on the board + * + ******************************************************/ + +static struct net_device_stats *elp_get_stats(struct net_device *dev) +{ + elp_device *adapter = netdev_priv(dev); + + if (elp_debug >= 3) + pr_debug("%s: request for stats\n", dev->name); + + /* If the device is closed, just return the latest stats we have, + - we cannot ask from the adapter without interrupts */ + if (!netif_running(dev)) + return &dev->stats; + + /* send a get statistics command to the board */ + adapter->tx_pcb.command = CMD_NETWORK_STATISTICS; + adapter->tx_pcb.length = 0; + adapter->got[CMD_NETWORK_STATISTICS] = 0; + if (!send_pcb(dev, &adapter->tx_pcb)) + pr_err("%s: couldn't send get statistics command\n", dev->name); + else { + unsigned long timeout = jiffies + TIMEOUT; + while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && time_before(jiffies, timeout)); + if (time_after_eq(jiffies, timeout)) { + TIMEOUT_MSG(__LINE__); + return &dev->stats; + } + } + + /* statistics are now up to date */ + return &dev->stats; +} + + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); +} + +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return debug; +} + +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + debug = level; +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; + +/****************************************************** + * + * close the board + * + ******************************************************/ + +static int elp_close(struct net_device *dev) +{ + elp_device *adapter = netdev_priv(dev); + + if (elp_debug >= 3) + pr_debug("%s: request to close device\n", dev->name); + + netif_stop_queue(dev); + + /* Someone may request the device statistic information even when + * the interface is closed. The following will update the statistics + * structure in the driver, so we'll be able to give current statistics. + */ + (void) elp_get_stats(dev); + + /* + * disable interrupts on the board + */ + outb_control(0, dev); + + /* + * release the IRQ + */ + free_irq(dev->irq, dev); + + free_dma(dev->dma); + free_pages((unsigned long) adapter->dma_buffer, get_order(DMA_BUFFER_SIZE)); + + return 0; +} + + +/************************************************************ + * + * Set multicast list + * num_addrs==0: clear mc_list + * num_addrs==-1: set promiscuous mode + * num_addrs>0: set mc_list + * + ************************************************************/ + +static void elp_set_mc_list(struct net_device *dev) +{ + elp_device *adapter = netdev_priv(dev); + struct netdev_hw_addr *ha; + int i; + unsigned long flags; + + if (elp_debug >= 3) + pr_debug("%s: request to set multicast list\n", dev->name); + + spin_lock_irqsave(&adapter->lock, flags); + + if (!(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + /* send a "load multicast list" command to the board, max 10 addrs/cmd */ + /* if num_addrs==0 the list will be cleared */ + adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST; + adapter->tx_pcb.length = 6 * netdev_mc_count(dev); + i = 0; + netdev_for_each_mc_addr(ha, dev) + memcpy(adapter->tx_pcb.data.multicast[i++], + ha->addr, 6); + adapter->got[CMD_LOAD_MULTICAST_LIST] = 0; + if (!send_pcb(dev, &adapter->tx_pcb)) + pr_err("%s: couldn't send set_multicast command\n", dev->name); + else { + unsigned long timeout = jiffies + TIMEOUT; + while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && time_before(jiffies, timeout)); + if (time_after_eq(jiffies, timeout)) { + TIMEOUT_MSG(__LINE__); + } + } + if (!netdev_mc_empty(dev)) + adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI; + else /* num_addrs == 0 */ + adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD; + } else + adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_PROMISC; + /* + * configure adapter to receive messages (as specified above) + * and wait for response + */ + if (elp_debug >= 3) + pr_debug("%s: sending 82586 configure command\n", dev->name); + adapter->tx_pcb.command = CMD_CONFIGURE_82586; + adapter->tx_pcb.length = 2; + adapter->got[CMD_CONFIGURE_82586] = 0; + if (!send_pcb(dev, &adapter->tx_pcb)) + { + spin_unlock_irqrestore(&adapter->lock, flags); + pr_err("%s: couldn't send 82586 configure command\n", dev->name); + } + else { + unsigned long timeout = jiffies + TIMEOUT; + spin_unlock_irqrestore(&adapter->lock, flags); + while (adapter->got[CMD_CONFIGURE_82586] == 0 && time_before(jiffies, timeout)); + if (time_after_eq(jiffies, timeout)) + TIMEOUT_MSG(__LINE__); + } +} + +/************************************************************ + * + * A couple of tests to see if there's 3C505 or not + * Called only by elp_autodetect + ************************************************************/ + +static int __init elp_sense(struct net_device *dev) +{ + int addr = dev->base_addr; + const char *name = dev->name; + byte orig_HSR; + + if (!request_region(addr, ELP_IO_EXTENT, "3c505")) + return -ENODEV; + + orig_HSR = inb_status(addr); + + if (elp_debug > 0) + pr_debug(search_msg, name, addr); + + if (orig_HSR == 0xff) { + if (elp_debug > 0) + pr_cont(notfound_msg, 1); + goto out; + } + + /* Wait for a while; the adapter may still be booting up */ + if (elp_debug > 0) + pr_cont(stilllooking_msg); + + if (orig_HSR & DIR) { + /* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */ + outb(0, dev->base_addr + PORT_CONTROL); + msleep(300); + if (inb_status(addr) & DIR) { + if (elp_debug > 0) + pr_cont(notfound_msg, 2); + goto out; + } + } else { + /* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */ + outb(DIR, dev->base_addr + PORT_CONTROL); + msleep(300); + if (!(inb_status(addr) & DIR)) { + if (elp_debug > 0) + pr_cont(notfound_msg, 3); + goto out; + } + } + /* + * It certainly looks like a 3c505. + */ + if (elp_debug > 0) + pr_cont(found_msg); + + return 0; +out: + release_region(addr, ELP_IO_EXTENT); + return -ENODEV; +} + +/************************************************************* + * + * Search through addr_list[] and try to find a 3C505 + * Called only by eplus_probe + *************************************************************/ + +static int __init elp_autodetect(struct net_device *dev) +{ + int idx = 0; + + /* if base address set, then only check that address + otherwise, run through the table */ + if (dev->base_addr != 0) { /* dev->base_addr == 0 ==> plain autodetect */ + if (elp_sense(dev) == 0) + return dev->base_addr; + } else + while ((dev->base_addr = addr_list[idx++])) { + if (elp_sense(dev) == 0) + return dev->base_addr; + } + + /* could not find an adapter */ + if (elp_debug > 0) + pr_debug(couldnot_msg, dev->name); + + return 0; /* Because of this, the layer above will return -ENODEV */ +} + +static const struct net_device_ops elp_netdev_ops = { + .ndo_open = elp_open, + .ndo_stop = elp_close, + .ndo_get_stats = elp_get_stats, + .ndo_start_xmit = elp_start_xmit, + .ndo_tx_timeout = elp_timeout, + .ndo_set_multicast_list = elp_set_mc_list, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +/****************************************************** + * + * probe for an Etherlink Plus board at the specified address + * + ******************************************************/ + +/* There are three situations we need to be able to detect here: + + * a) the card is idle + * b) the card is still booting up + * c) the card is stuck in a strange state (some DOS drivers do this) + * + * In case (a), all is well. In case (b), we wait 10 seconds to see if the + * card finishes booting, and carry on if so. In case (c), we do a hard reset, + * loop round, and hope for the best. + * + * This is all very unpleasant, but hopefully avoids the problems with the old + * probe code (which had a 15-second delay if the card was idle, and didn't + * work at all if it was in a weird state). + */ + +static int __init elplus_setup(struct net_device *dev) +{ + elp_device *adapter = netdev_priv(dev); + int i, tries, tries1, okay; + unsigned long timeout; + unsigned long cookie = 0; + int err = -ENODEV; + + /* + * setup adapter structure + */ + + dev->base_addr = elp_autodetect(dev); + if (!dev->base_addr) + return -ENODEV; + + adapter->send_pcb_semaphore = 0; + + for (tries1 = 0; tries1 < 3; tries1++) { + 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. + */ + timeout = jiffies + 5*HZ/100; + okay = 0; + while (time_before(jiffies, timeout) && !(inb_status(dev->base_addr) & HCRE)); + if ((inb_status(dev->base_addr) & HCRE)) { + outb_command(0, dev->base_addr); /* send a spurious byte */ + timeout = jiffies + 5*HZ/100; + while (time_before(jiffies, timeout) && !(inb_status(dev->base_addr) & HCRE)); + if (inb_status(dev->base_addr) & HCRE) + okay = 1; + } + if (!okay) { + /* Nope, it's ignoring the command register. This means that + * either it's still booting up, or it's died. + */ + pr_err("%s: command register wouldn't drain, ", dev->name); + if ((inb_status(dev->base_addr) & 7) == 3) { + /* If the adapter status is 3, it *could* still be booting. + * Give it the benefit of the doubt for 10 seconds. + */ + pr_cont("assuming 3c505 still starting\n"); + timeout = jiffies + 10*HZ; + while (time_before(jiffies, timeout) && (inb_status(dev->base_addr) & 7)); + if (inb_status(dev->base_addr) & 7) { + pr_err("%s: 3c505 failed to start\n", dev->name); + } else { + okay = 1; /* It started */ + } + } else { + /* Otherwise, it must just be in a strange + * state. We probably need to kick it. + */ + pr_cont("3c505 is sulking\n"); + } + } + for (tries = 0; tries < 5 && okay; tries++) { + + /* + * Try to set the Ethernet address, to make sure that the board + * is working. + */ + adapter->tx_pcb.command = CMD_STATION_ADDRESS; + adapter->tx_pcb.length = 0; + cookie = probe_irq_on(); + if (!send_pcb(dev, &adapter->tx_pcb)) { + pr_err("%s: could not send first PCB\n", dev->name); + probe_irq_off(cookie); + continue; + } + if (!receive_pcb(dev, &adapter->rx_pcb)) { + pr_err("%s: could not read first PCB\n", dev->name); + probe_irq_off(cookie); + continue; + } + if ((adapter->rx_pcb.command != CMD_ADDRESS_RESPONSE) || + (adapter->rx_pcb.length != 6)) { + pr_err("%s: first PCB wrong (%d, %d)\n", dev->name, + adapter->rx_pcb.command, adapter->rx_pcb.length); + probe_irq_off(cookie); + continue; + } + goto okay; + } + /* It's broken. Do a hard reset to re-initialise the board, + * and try again. + */ + pr_info("%s: resetting adapter\n", dev->name); + outb_control(adapter->hcr_val | FLSH | ATTN, dev); + outb_control(adapter->hcr_val & ~(FLSH | ATTN), dev); + } + pr_err("%s: failed to initialise 3c505\n", dev->name); + goto out; + + okay: + if (dev->irq) { /* Is there a preset IRQ? */ + int rpt = probe_irq_off(cookie); + if (dev->irq != rpt) { + pr_warning("%s: warning, irq %d configured but %d detected\n", dev->name, dev->irq, rpt); + } + /* if dev->irq == probe_irq_off(cookie), all is well */ + } else /* No preset IRQ; just use what we can detect */ + dev->irq = probe_irq_off(cookie); + switch (dev->irq) { /* Legal, sane? */ + case 0: + pr_err("%s: IRQ probe failed: check 3c505 jumpers.\n", + dev->name); + goto out; + case 1: + case 6: + case 8: + case 13: + pr_err("%s: Impossible IRQ %d reported by probe_irq_off().\n", + dev->name, dev->irq); + goto out; + } + /* + * Now we have the IRQ number so we can disable the interrupts from + * the board until the board is opened. + */ + outb_control(adapter->hcr_val & ~CMDE, dev); + + /* + * copy Ethernet address into structure + */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = adapter->rx_pcb.data.eth_addr[i]; + + /* find a DMA channel */ + if (!dev->dma) { + if (dev->mem_start) { + dev->dma = dev->mem_start & 7; + } + else { + pr_warning("%s: warning, DMA channel not specified, using default\n", dev->name); + dev->dma = ELP_DMA; + } + } + + /* + * print remainder of startup message + */ + pr_info("%s: 3c505 at %#lx, irq %d, dma %d, addr %pM, ", + dev->name, dev->base_addr, dev->irq, dev->dma, dev->dev_addr); + /* + * read more information from the adapter + */ + + adapter->tx_pcb.command = CMD_ADAPTER_INFO; + adapter->tx_pcb.length = 0; + if (!send_pcb(dev, &adapter->tx_pcb) || + !receive_pcb(dev, &adapter->rx_pcb) || + (adapter->rx_pcb.command != CMD_ADAPTER_INFO_RESPONSE) || + (adapter->rx_pcb.length != 10)) { + pr_cont("not responding to second PCB\n"); + } + pr_cont("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); + + /* + * reconfigure the adapter memory to better suit our purposes + */ + adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY; + adapter->tx_pcb.length = 12; + adapter->tx_pcb.data.memconf.cmd_q = 8; + adapter->tx_pcb.data.memconf.rcv_q = 8; + adapter->tx_pcb.data.memconf.mcast = 10; + adapter->tx_pcb.data.memconf.frame = 10; + adapter->tx_pcb.data.memconf.rcv_b = 10; + adapter->tx_pcb.data.memconf.progs = 0; + if (!send_pcb(dev, &adapter->tx_pcb) || + !receive_pcb(dev, &adapter->rx_pcb) || + (adapter->rx_pcb.command != CMD_CONFIGURE_ADAPTER_RESPONSE) || + (adapter->rx_pcb.length != 2)) { + pr_err("%s: could not configure adapter memory\n", dev->name); + } + if (adapter->rx_pcb.data.configure) { + pr_err("%s: adapter configuration failed\n", dev->name); + } + + dev->netdev_ops = &elp_netdev_ops; + dev->watchdog_timeo = 10*HZ; + dev->ethtool_ops = &netdev_ethtool_ops; /* local */ + + dev->mem_start = dev->mem_end = 0; + + err = register_netdev(dev); + if (err) + goto out; + + return 0; +out: + release_region(dev->base_addr, ELP_IO_EXTENT); + return err; +} + +#ifndef MODULE +struct net_device * __init elplus_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(elp_device)); + int err; + if (!dev) + return ERR_PTR(-ENOMEM); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = elplus_setup(dev); + if (err) { + free_netdev(dev); + return ERR_PTR(err); + } + return dev; +} + +#else +static struct net_device *dev_3c505[ELP_MAX_CARDS]; +static int io[ELP_MAX_CARDS]; +static int irq[ELP_MAX_CARDS]; +static int dma[ELP_MAX_CARDS]; +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param_array(dma, int, NULL, 0); +MODULE_PARM_DESC(io, "EtherLink Plus I/O base address(es)"); +MODULE_PARM_DESC(irq, "EtherLink Plus IRQ number(s) (assigned)"); +MODULE_PARM_DESC(dma, "EtherLink Plus DMA channel(s)"); + +int __init init_module(void) +{ + int this_dev, found = 0; + + for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) { + struct net_device *dev = alloc_etherdev(sizeof(elp_device)); + if (!dev) + break; + + 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; + pr_warning("3c505.c: warning, using default DMA channel,\n"); + } + if (io[this_dev] == 0) { + if (this_dev) { + free_netdev(dev); + break; + } + pr_notice("3c505.c: module autoprobe not recommended, give io=xx.\n"); + } + if (elplus_setup(dev) != 0) { + pr_warning("3c505.c: Failed to register card at 0x%x.\n", io[this_dev]); + free_netdev(dev); + break; + } + dev_3c505[this_dev] = dev; + found++; + } + if (!found) + return -ENODEV; + return 0; +} + +void __exit cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) { + struct net_device *dev = dev_3c505[this_dev]; + if (dev) { + unregister_netdev(dev); + release_region(dev->base_addr, ELP_IO_EXTENT); + free_netdev(dev); + } + } +} + +#endif /* MODULE */ +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/i825xx/3c505.h b/drivers/net/ethernet/i825xx/3c505.h new file mode 100644 index 000000000000..04df2a9002b6 --- /dev/null +++ b/drivers/net/ethernet/i825xx/3c505.h @@ -0,0 +1,292 @@ +/***************************************************************** + * + * defines for 3Com Etherlink Plus adapter + * + *****************************************************************/ + +#define ELP_DMA 6 +#define ELP_RX_PCBS 4 +#define ELP_MAX_CARDS 4 + +/* + * I/O register offsets + */ +#define PORT_COMMAND 0x00 /* read/write, 8-bit */ +#define PORT_STATUS 0x02 /* read only, 8-bit */ +#define PORT_AUXDMA 0x02 /* write only, 8-bit */ +#define PORT_DATA 0x04 /* read/write, 16-bit */ +#define PORT_CONTROL 0x06 /* read/write, 8-bit */ + +#define ELP_IO_EXTENT 0x10 /* size of used IO registers */ + +/* + * host control registers bits + */ +#define ATTN 0x80 /* attention */ +#define FLSH 0x40 /* flush data register */ +#define DMAE 0x20 /* DMA enable */ +#define DIR 0x10 /* direction */ +#define TCEN 0x08 /* terminal count interrupt enable */ +#define CMDE 0x04 /* command register interrupt enable */ +#define HSF2 0x02 /* host status flag 2 */ +#define HSF1 0x01 /* host status flag 1 */ + +/* + * combinations of HSF flags used for PCB transmission + */ +#define HSF_PCB_ACK HSF1 +#define HSF_PCB_NAK HSF2 +#define HSF_PCB_END (HSF2|HSF1) +#define HSF_PCB_MASK (HSF2|HSF1) + +/* + * host status register bits + */ +#define HRDY 0x80 /* data register ready */ +#define HCRE 0x40 /* command register empty */ +#define ACRF 0x20 /* adapter command register full */ +/* #define DIR 0x10 direction - same as in control register */ +#define DONE 0x08 /* DMA done */ +#define ASF3 0x04 /* adapter status flag 3 */ +#define ASF2 0x02 /* adapter status flag 2 */ +#define ASF1 0x01 /* adapter status flag 1 */ + +/* + * combinations of ASF flags used for PCB reception + */ +#define ASF_PCB_ACK ASF1 +#define ASF_PCB_NAK ASF2 +#define ASF_PCB_END (ASF2|ASF1) +#define ASF_PCB_MASK (ASF2|ASF1) + +/* + * host aux DMA register bits + */ +#define DMA_BRST 0x01 /* DMA burst */ + +/* + * maximum amount of data allowed in a PCB + */ +#define MAX_PCB_DATA 62 + +/***************************************************************** + * + * timeout value + * this is a rough value used for loops to stop them from + * locking up the whole machine in the case of failure or + * error conditions + * + *****************************************************************/ + +#define TIMEOUT 300 + +/***************************************************************** + * + * PCB commands + * + *****************************************************************/ + +enum { + /* + * host PCB commands + */ + CMD_CONFIGURE_ADAPTER_MEMORY = 0x01, + CMD_CONFIGURE_82586 = 0x02, + CMD_STATION_ADDRESS = 0x03, + CMD_DMA_DOWNLOAD = 0x04, + CMD_DMA_UPLOAD = 0x05, + CMD_PIO_DOWNLOAD = 0x06, + CMD_PIO_UPLOAD = 0x07, + CMD_RECEIVE_PACKET = 0x08, + CMD_TRANSMIT_PACKET = 0x09, + CMD_NETWORK_STATISTICS = 0x0a, + CMD_LOAD_MULTICAST_LIST = 0x0b, + CMD_CLEAR_PROGRAM = 0x0c, + CMD_DOWNLOAD_PROGRAM = 0x0d, + CMD_EXECUTE_PROGRAM = 0x0e, + CMD_SELF_TEST = 0x0f, + CMD_SET_STATION_ADDRESS = 0x10, + CMD_ADAPTER_INFO = 0x11, + NUM_TRANSMIT_CMDS, + + /* + * adapter PCB commands + */ + CMD_CONFIGURE_ADAPTER_RESPONSE = 0x31, + CMD_CONFIGURE_82586_RESPONSE = 0x32, + CMD_ADDRESS_RESPONSE = 0x33, + CMD_DOWNLOAD_DATA_REQUEST = 0x34, + CMD_UPLOAD_DATA_REQUEST = 0x35, + CMD_RECEIVE_PACKET_COMPLETE = 0x38, + CMD_TRANSMIT_PACKET_COMPLETE = 0x39, + CMD_NETWORK_STATISTICS_RESPONSE = 0x3a, + CMD_LOAD_MULTICAST_RESPONSE = 0x3b, + CMD_CLEAR_PROGRAM_RESPONSE = 0x3c, + CMD_DOWNLOAD_PROGRAM_RESPONSE = 0x3d, + CMD_EXECUTE_RESPONSE = 0x3e, + CMD_SELF_TEST_RESPONSE = 0x3f, + CMD_SET_ADDRESS_RESPONSE = 0x40, + CMD_ADAPTER_INFO_RESPONSE = 0x41 +}; + +/* Definitions for the PCB data structure */ + +/* Data units */ +typedef unsigned char byte; +typedef unsigned short int word; +typedef unsigned long int dword; + +/* Data structures */ +struct Memconf { + word cmd_q, + rcv_q, + mcast, + frame, + rcv_b, + progs; +}; + +struct Rcv_pkt { + word buf_ofs, + buf_seg, + buf_len, + timeout; +}; + +struct Xmit_pkt { + word buf_ofs, + buf_seg, + pkt_len; +}; + +struct Rcv_resp { + word buf_ofs, + buf_seg, + buf_len, + pkt_len, + timeout, + status; + dword timetag; +}; + +struct Xmit_resp { + word buf_ofs, + buf_seg, + c_stat, + status; +}; + + +struct Netstat { + dword tot_recv, + tot_xmit; + word err_CRC, + err_align, + err_res, + err_ovrrun; +}; + + +struct Selftest { + word error; + union { + word ROM_cksum; + struct { + word ofs, seg; + } RAM; + word i82586; + } failure; +}; + +struct Info { + byte minor_vers, + major_vers; + word ROM_cksum, + RAM_sz, + free_ofs, + free_seg; +}; + +struct Memdump { + word size, + off, + seg; +}; + +/* +Primary Command Block. The most important data structure. All communication +between the host and the adapter is done with these. (Except for the actual +Ethernet data, which has different packaging.) +*/ +typedef struct { + byte command; + byte length; + union { + struct Memconf memconf; + word configure; + struct Rcv_pkt rcv_pkt; + struct Xmit_pkt xmit_pkt; + byte multicast[10][6]; + byte eth_addr[6]; + byte failed; + struct Rcv_resp rcv_resp; + struct Xmit_resp xmit_resp; + struct Netstat netstat; + struct Selftest selftest; + struct Info info; + struct Memdump memdump; + byte raw[62]; + } data; +} pcb_struct; + +/* These defines for 'configure' */ +#define RECV_STATION 0x00 +#define RECV_BROAD 0x01 +#define RECV_MULTI 0x02 +#define RECV_PROMISC 0x04 +#define NO_LOOPBACK 0x00 +#define INT_LOOPBACK 0x08 +#define EXT_LOOPBACK 0x10 + +/***************************************************************** + * + * 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 */ + + void *dma_buffer; + + struct { + unsigned int length[BACKLOG_SIZE]; + unsigned int in; + unsigned int out; + } rx_backlog; + + struct { + unsigned int direction; + unsigned int length; + struct sk_buff *skb; + void *target; + unsigned long start_time; + } current_dma; + + /* flags */ + unsigned long send_pcb_semaphore; + unsigned long dmaing; + unsigned long busy; + + unsigned int rx_active; /* number of receive PCBs */ + volatile unsigned char hcr_val; /* what we think the HCR contains */ + spinlock_t lock; /* Interrupt v tx lock */ +} elp_device; diff --git a/drivers/net/ethernet/i825xx/3c507.c b/drivers/net/ethernet/i825xx/3c507.c new file mode 100644 index 000000000000..1e945551c144 --- /dev/null +++ b/drivers/net/ethernet/i825xx/3c507.c @@ -0,0 +1,939 @@ +/* 3c507.c: An EtherLink16 device driver for Linux. */ +/* + Written 1993,1994 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + + Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings) + and jrs@world.std.com (Rick Sladkey) for testing and bugfixes. + Mark Salazar <leslie@access.digex.net> made the changes for cards with + only 16K packet buffers. + + Things remaining to do: + Verify that the tx and rx buffers don't have fencepost errors. + Move the theory of operation and memory map documentation. + The statistics need to be updated correctly. +*/ + +#define DRV_NAME "3c507" +#define DRV_VERSION "1.10a" +#define DRV_RELDATE "11/17/2001" + +static const char version[] = + DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Donald Becker (becker@scyld.com)\n"; + +/* + Sources: + This driver wouldn't have been written with the availability of the + Crynwr driver source code. It provided a known-working implementation + that filled in the gaping holes of the Intel documentation. Three cheers + for Russ Nelson. + + Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough + info that the casual reader might think that it documents the i82586 :-<. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <linux/ethtool.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_ether.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/bitops.h> + +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +/* use 0 for production, 1 for verification, 2..7 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 1 +#endif +static unsigned int net_debug = NET_DEBUG; +#define debug net_debug + + +/* + Details of the i82586. + + You'll really need the databook to understand the details of this part, + but the outline is that the i82586 has two separate processing units. + Both are started from a list of three configuration tables, of which only + the last, the System Control Block (SCB), is used after reset-time. The SCB + has the following fields: + Status word + Command word + Tx/Command block addr. + Rx block addr. + The command word accepts the following controls for the Tx and Rx units: + */ + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 + +/* The Rx unit uses a list of frame descriptors and a list of data buffer + descriptors. We use full-sized (1518 byte) data buffers, so there is + a one-to-one pairing of frame descriptors to buffer descriptors. + + The Tx ("command") unit executes a list of commands that look like: + Status word Written by the 82586 when the command is done. + Command word Command in lower 3 bits, post-command action in upper 3 + Link word The address of the next command. + Parameters (as needed). + + Some definitions related to the Command Word are: + */ +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +enum commands { + CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7}; + +/* Information that need to be kept for each board. */ +struct net_local { + int last_restart; + ushort rx_head; + ushort rx_tail; + ushort tx_head; + ushort tx_cmd_link; + ushort tx_reap; + ushort tx_pkts_in_ring; + spinlock_t lock; + void __iomem *base; +}; + +/* + Details of the EtherLink16 Implementation + The 3c507 is a generic shared-memory i82586 implementation. + The host can map 16K, 32K, 48K, or 64K of the 64K memory into + 0x0[CD][08]0000, or all 64K into 0xF[02468]0000. + */ + +/* Offsets from the base I/O address. */ +#define SA_DATA 0 /* Station address data, or 3Com signature. */ +#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */ +#define RESET_IRQ 10 /* Reset the latched IRQ line. */ +#define SIGNAL_CA 11 /* Frob the 82586 Channel Attention line. */ +#define ROM_CONFIG 13 +#define MEM_CONFIG 14 +#define IRQ_CONFIG 15 +#define EL16_IO_EXTENT 16 + +/* The ID port is used at boot-time to locate the ethercard. */ +#define ID_PORT 0x100 + +/* Offsets to registers in the mailbox (SCB). */ +#define iSCB_STATUS 0x8 +#define iSCB_CMD 0xA +#define iSCB_CBL 0xC /* Command BLock offset. */ +#define iSCB_RFA 0xE /* Rx Frame Area offset. */ + +/* Since the 3c507 maps the shared memory window so that the last byte is + at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or + 48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively. + We can account for this be setting the 'SBC Base' entry in the ISCP table + below for all the 16 bit offset addresses, and also adding the 'SCB Base' + value to all 24 bit physical addresses (in the SCP table and the TX and RX + Buffer Descriptors). + -Mark + */ +#define SCB_BASE ((unsigned)64*1024 - (dev->mem_end - dev->mem_start)) + +/* + What follows in 'init_words[]' is the "program" that is downloaded to the + 82586 memory. It's mostly tables and command blocks, and starts at the + reset address 0xfffff6. This is designed to be similar to the EtherExpress, + thus the unusual location of the SCB at 0x0008. + + Even with the additional "don't care" values, doing it this way takes less + program space than initializing the individual tables, and I feel it's much + cleaner. + + The databook is particularly useless for the first two structures, I had + to use the Crynwr driver as an example. + + The memory setup is as follows: + */ + +#define CONFIG_CMD 0x0018 +#define SET_SA_CMD 0x0024 +#define SA_OFFSET 0x002A +#define IDLELOOP 0x30 +#define TDR_CMD 0x38 +#define TDR_TIME 0x3C +#define DUMP_CMD 0x40 +#define DIAG_CMD 0x48 +#define SET_MC_CMD 0x4E +#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */ + +#define TX_BUF_START 0x0100 +#define NUM_TX_BUFS 5 +#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */ + +#define RX_BUF_START 0x2000 +#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */ +#define RX_BUF_END (dev->mem_end - dev->mem_start) + +#define TX_TIMEOUT (HZ/20) + +/* + That's it: only 86 bytes to set up the beast, including every extra + command available. The 170 byte buffer at DUMP_DATA is shared between the + Dump command (called only by the diagnostic program) and the SetMulticastList + command. + + To complete the memory setup you only have to write the station address at + SA_OFFSET and create the Tx & Rx buffer lists. + + The Tx command chain and buffer list is setup as follows: + A Tx command table, with the data buffer pointing to... + A Tx data buffer descriptor. The packet is in a single buffer, rather than + chaining together several smaller buffers. + A NoOp command, which initially points to itself, + And the packet data. + + A transmit is done by filling in the Tx command table and data buffer, + re-writing the NoOp command, and finally changing the offset of the last + command to point to the current Tx command. When the Tx command is finished, + it jumps to the NoOp, when it loops until the next Tx command changes the + "link offset" in the NoOp. This way the 82586 never has to go through the + slow restart sequence. + + The Rx buffer list is set up in the obvious ring structure. We have enough + memory (and low enough interrupt latency) that we can avoid the complicated + Rx buffer linked lists by alway associating a full-size Rx data buffer with + each Rx data frame. + + I current use four transmit buffers starting at TX_BUF_START (0x0100), and + use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers. + + */ + +static unsigned short init_words[] = { + /* System Configuration Pointer (SCP). */ + 0x0000, /* Set bus size to 16 bits. */ + 0,0, /* pad words. */ + 0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */ + + /* Intermediate System Configuration Pointer (ISCP). */ + 0x0001, /* Status word that's cleared when init is done. */ + 0x0008,0,0, /* SCB offset, (skip, skip) */ + + /* System Control Block (SCB). */ + 0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */ + CONFIG_CMD, /* Command list pointer, points to Configure. */ + RX_BUF_START, /* Rx block list. */ + 0,0,0,0, /* Error count: CRC, align, buffer, overrun. */ + + /* 0x0018: Configure command. Change to put MAC data with packet. */ + 0, CmdConfigure, /* Status, command. */ + SET_SA_CMD, /* Next command is Set Station Addr. */ + 0x0804, /* "4" bytes of config data, 8 byte FIFO. */ + 0x2e40, /* Magic values, including MAC data location. */ + 0, /* Unused pad word. */ + + /* 0x0024: Setup station address command. */ + 0, CmdSASetup, + SET_MC_CMD, /* Next command. */ + 0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */ + + /* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */ + 0, CmdNOp, IDLELOOP, 0 /* pad */, + + /* 0x0038: A unused Time-Domain Reflectometer command. */ + 0, CmdTDR, IDLELOOP, 0, + + /* 0x0040: An unused Dump State command. */ + 0, CmdDump, IDLELOOP, DUMP_DATA, + + /* 0x0048: An unused Diagnose command. */ + 0, CmdDiagnose, IDLELOOP, + + /* 0x004E: An empty set-multicast-list command. */ + 0, CmdMulticastList, IDLELOOP, 0, +}; + +/* Index to functions, as function prototypes. */ + +static int el16_probe1(struct net_device *dev, int ioaddr); +static int el16_open(struct net_device *dev); +static netdev_tx_t el16_send_packet(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t el16_interrupt(int irq, void *dev_id); +static void el16_rx(struct net_device *dev); +static int el16_close(struct net_device *dev); +static void el16_tx_timeout (struct net_device *dev); + +static void hardware_send_packet(struct net_device *dev, void *buf, short length, short pad); +static void init_82586_mem(struct net_device *dev); +static const struct ethtool_ops netdev_ethtool_ops; +static void init_rx_bufs(struct net_device *); + +static int io = 0x300; +static int irq; +static int mem_start; + + +/* Check for a network adaptor of this type, and return '0' iff one exists. + If dev->base_addr == 0, probe all likely locations. + If dev->base_addr == 1, always return failure. + If dev->base_addr == 2, (detachable devices only) allocate space for the + device and return success. + */ + +struct net_device * __init el16_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); + static const unsigned ports[] = { 0x300, 0x320, 0x340, 0x280, 0}; + const unsigned *port; + int err = -ENODEV; + + if (!dev) + return ERR_PTR(-ENODEV); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + io = dev->base_addr; + irq = dev->irq; + mem_start = dev->mem_start & 15; + } + + if (io > 0x1ff) /* Check a single specified location. */ + err = el16_probe1(dev, io); + else if (io != 0) + err = -ENXIO; /* Don't probe at all. */ + else { + for (port = ports; *port; port++) { + err = el16_probe1(dev, *port); + if (!err) + break; + } + } + + if (err) + goto out; + err = register_netdev(dev); + if (err) + goto out1; + return dev; +out1: + free_irq(dev->irq, dev); + iounmap(((struct net_local *)netdev_priv(dev))->base); + release_region(dev->base_addr, EL16_IO_EXTENT); +out: + free_netdev(dev); + return ERR_PTR(err); +} + +static const struct net_device_ops netdev_ops = { + .ndo_open = el16_open, + .ndo_stop = el16_close, + .ndo_start_xmit = el16_send_packet, + .ndo_tx_timeout = el16_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __init el16_probe1(struct net_device *dev, int ioaddr) +{ + static unsigned char init_ID_done; + int i, irq, irqval, retval; + struct net_local *lp; + + if (init_ID_done == 0) { + ushort lrs_state = 0xff; + /* Send the ID sequence to the ID_PORT to enable the board(s). */ + outb(0x00, ID_PORT); + for(i = 0; i < 255; i++) { + outb(lrs_state, ID_PORT); + lrs_state <<= 1; + if (lrs_state & 0x100) + lrs_state ^= 0xe7; + } + outb(0x00, ID_PORT); + init_ID_done = 1; + } + + if (!request_region(ioaddr, EL16_IO_EXTENT, DRV_NAME)) + return -ENODEV; + + if ((inb(ioaddr) != '*') || (inb(ioaddr + 1) != '3') || + (inb(ioaddr + 2) != 'C') || (inb(ioaddr + 3) != 'O')) { + retval = -ENODEV; + goto out; + } + + pr_info("%s: 3c507 at %#x,", dev->name, ioaddr); + + /* We should make a few more checks here, like the first three octets of + the S.A. for the manufacturer's code. */ + + irq = inb(ioaddr + IRQ_CONFIG) & 0x0f; + + irqval = request_irq(irq, el16_interrupt, 0, DRV_NAME, dev); + if (irqval) { + pr_cont("\n"); + pr_err("3c507: unable to get IRQ %d (irqval=%d).\n", irq, irqval); + retval = -EAGAIN; + goto out; + } + + /* We've committed to using the board, and can start filling in *dev. */ + dev->base_addr = ioaddr; + + outb(0x01, ioaddr + MISC_CTRL); + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inb(ioaddr + i); + pr_cont(" %pM", dev->dev_addr); + + if (mem_start) + net_debug = mem_start & 7; + +#ifdef MEM_BASE + dev->mem_start = MEM_BASE; + dev->mem_end = dev->mem_start + 0x10000; +#else + { + int base; + int size; + char mem_config = inb(ioaddr + MEM_CONFIG); + if (mem_config & 0x20) { + size = 64*1024; + base = 0xf00000 + (mem_config & 0x08 ? 0x080000 + : ((mem_config & 3) << 17)); + } else { + size = ((mem_config & 3) + 1) << 14; + base = 0x0c0000 + ( (mem_config & 0x18) << 12); + } + dev->mem_start = base; + dev->mem_end = base + size; + } +#endif + + dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0; + dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f; + + pr_cont(", IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->irq, + dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1); + + if (net_debug) + pr_debug("%s", version); + + lp = netdev_priv(dev); + spin_lock_init(&lp->lock); + lp->base = ioremap(dev->mem_start, RX_BUF_END); + if (!lp->base) { + pr_err("3c507: unable to remap memory\n"); + retval = -EAGAIN; + goto out1; + } + + dev->netdev_ops = &netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + dev->ethtool_ops = &netdev_ethtool_ops; + dev->flags &= ~IFF_MULTICAST; /* Multicast doesn't work */ + return 0; +out1: + free_irq(dev->irq, dev); +out: + release_region(ioaddr, EL16_IO_EXTENT); + return retval; +} + +static int el16_open(struct net_device *dev) +{ + /* Initialize the 82586 memory and start it. */ + init_82586_mem(dev); + + netif_start_queue(dev); + return 0; +} + + +static void el16_tx_timeout (struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + void __iomem *shmem = lp->base; + + if (net_debug > 1) + pr_debug("%s: transmit timed out, %s? ", dev->name, + readw(shmem + iSCB_STATUS) & 0x8000 ? "IRQ conflict" : + "network cable problem"); + /* Try to restart the adaptor. */ + if (lp->last_restart == dev->stats.tx_packets) { + if (net_debug > 1) + pr_cont("Resetting board.\n"); + /* Completely reset the adaptor. */ + init_82586_mem (dev); + lp->tx_pkts_in_ring = 0; + } else { + /* Issue the channel attention signal and hope it "gets better". */ + if (net_debug > 1) + pr_cont("Kicking board.\n"); + writew(0xf000 | CUC_START | RX_START, shmem + iSCB_CMD); + outb (0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */ + lp->last_restart = dev->stats.tx_packets; + } + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue (dev); +} + + +static netdev_tx_t el16_send_packet (struct sk_buff *skb, + struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + unsigned long flags; + short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned char *buf = skb->data; + + netif_stop_queue (dev); + + spin_lock_irqsave (&lp->lock, flags); + + dev->stats.tx_bytes += length; + /* Disable the 82586's input to the interrupt line. */ + outb (0x80, ioaddr + MISC_CTRL); + + hardware_send_packet (dev, buf, skb->len, length - skb->len); + + /* Enable the 82586 interrupt input. */ + outb (0x84, ioaddr + MISC_CTRL); + + spin_unlock_irqrestore (&lp->lock, flags); + + dev_kfree_skb (skb); + + /* You might need to clean up and record Tx statistics here. */ + + return NETDEV_TX_OK; +} + +/* The typical workload of the driver: + Handle the network interface interrupts. */ +static irqreturn_t el16_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct net_local *lp; + int ioaddr, status, boguscount = 0; + ushort ack_cmd = 0; + void __iomem *shmem; + + if (dev == NULL) { + pr_err("net_interrupt(): irq %d for unknown device.\n", irq); + return IRQ_NONE; + } + + ioaddr = dev->base_addr; + lp = netdev_priv(dev); + shmem = lp->base; + + spin_lock(&lp->lock); + + status = readw(shmem+iSCB_STATUS); + + if (net_debug > 4) { + pr_debug("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status); + } + + /* Disable the 82586's input to the interrupt line. */ + outb(0x80, ioaddr + MISC_CTRL); + + /* Reap the Tx packet buffers. */ + while (lp->tx_pkts_in_ring) { + unsigned short tx_status = readw(shmem+lp->tx_reap); + if (!(tx_status & 0x8000)) { + if (net_debug > 5) + pr_debug("Tx command incomplete (%#x).\n", lp->tx_reap); + break; + } + /* Tx unsuccessful or some interesting status bit set. */ + if (!(tx_status & 0x2000) || (tx_status & 0x0f3f)) { + dev->stats.tx_errors++; + if (tx_status & 0x0600) dev->stats.tx_carrier_errors++; + if (tx_status & 0x0100) dev->stats.tx_fifo_errors++; + if (!(tx_status & 0x0040)) dev->stats.tx_heartbeat_errors++; + if (tx_status & 0x0020) dev->stats.tx_aborted_errors++; + dev->stats.collisions += tx_status & 0xf; + } + dev->stats.tx_packets++; + if (net_debug > 5) + pr_debug("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status); + lp->tx_reap += TX_BUF_SIZE; + if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE) + lp->tx_reap = TX_BUF_START; + + lp->tx_pkts_in_ring--; + /* There is always more space in the Tx ring buffer now. */ + netif_wake_queue(dev); + + if (++boguscount > 10) + break; + } + + if (status & 0x4000) { /* Packet received. */ + if (net_debug > 5) + pr_debug("Received packet, rx_head %04x.\n", lp->rx_head); + el16_rx(dev); + } + + /* Acknowledge the interrupt sources. */ + ack_cmd = status & 0xf000; + + if ((status & 0x0700) != 0x0200 && netif_running(dev)) { + if (net_debug) + pr_debug("%s: Command unit stopped, status %04x, restarting.\n", + dev->name, status); + /* If this ever occurs we should really re-write the idle loop, reset + the Tx list, and do a complete restart of the command unit. + For now we rely on the Tx timeout if the resume doesn't work. */ + ack_cmd |= CUC_RESUME; + } + + if ((status & 0x0070) != 0x0040 && netif_running(dev)) { + /* The Rx unit is not ready, it must be hung. Restart the receiver by + initializing the rx buffers, and issuing an Rx start command. */ + if (net_debug) + pr_debug("%s: Rx unit stopped, status %04x, restarting.\n", + dev->name, status); + init_rx_bufs(dev); + writew(RX_BUF_START,shmem+iSCB_RFA); + ack_cmd |= RX_START; + } + + writew(ack_cmd,shmem+iSCB_CMD); + outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */ + + /* Clear the latched interrupt. */ + outb(0, ioaddr + RESET_IRQ); + + /* Enable the 82586's interrupt input. */ + outb(0x84, ioaddr + MISC_CTRL); + spin_unlock(&lp->lock); + return IRQ_HANDLED; +} + +static int el16_close(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + void __iomem *shmem = lp->base; + + netif_stop_queue(dev); + + /* Flush the Tx and disable Rx. */ + writew(RX_SUSPEND | CUC_SUSPEND,shmem+iSCB_CMD); + outb(0, ioaddr + SIGNAL_CA); + + /* Disable the 82586's input to the interrupt line. */ + outb(0x80, ioaddr + MISC_CTRL); + + /* We always physically use the IRQ line, so we don't do free_irq(). */ + + /* Update the statistics here. */ + + return 0; +} + +/* Initialize the Rx-block list. */ +static void init_rx_bufs(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + void __iomem *write_ptr; + unsigned short SCB_base = SCB_BASE; + + int cur_rxbuf = lp->rx_head = RX_BUF_START; + + /* Initialize each Rx frame + data buffer. */ + do { /* While there is room for one more. */ + + write_ptr = lp->base + cur_rxbuf; + + writew(0x0000,write_ptr); /* Status */ + writew(0x0000,write_ptr+=2); /* Command */ + writew(cur_rxbuf + RX_BUF_SIZE,write_ptr+=2); /* Link */ + writew(cur_rxbuf + 22,write_ptr+=2); /* Buffer offset */ + writew(0x0000,write_ptr+=2); /* Pad for dest addr. */ + writew(0x0000,write_ptr+=2); + writew(0x0000,write_ptr+=2); + writew(0x0000,write_ptr+=2); /* Pad for source addr. */ + writew(0x0000,write_ptr+=2); + writew(0x0000,write_ptr+=2); + writew(0x0000,write_ptr+=2); /* Pad for protocol. */ + + writew(0x0000,write_ptr+=2); /* Buffer: Actual count */ + writew(-1,write_ptr+=2); /* Buffer: Next (none). */ + writew(cur_rxbuf + 0x20 + SCB_base,write_ptr+=2);/* Buffer: Address low */ + writew(0x0000,write_ptr+=2); + /* Finally, the number of bytes in the buffer. */ + writew(0x8000 + RX_BUF_SIZE-0x20,write_ptr+=2); + + lp->rx_tail = cur_rxbuf; + cur_rxbuf += RX_BUF_SIZE; + } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE); + + /* Terminate the list by setting the EOL bit, and wrap the pointer to make + the list a ring. */ + write_ptr = lp->base + lp->rx_tail + 2; + writew(0xC000,write_ptr); /* Command, mark as last. */ + writew(lp->rx_head,write_ptr+2); /* Link */ +} + +static void init_82586_mem(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + short ioaddr = dev->base_addr; + void __iomem *shmem = lp->base; + + /* Enable loopback to protect the wire while starting up, + and hold the 586 in reset during the memory initialization. */ + outb(0x20, ioaddr + MISC_CTRL); + + /* Fix the ISCP address and base. */ + init_words[3] = SCB_BASE; + init_words[7] = SCB_BASE; + + /* Write the words at 0xfff6 (address-aliased to 0xfffff6). */ + memcpy_toio(lp->base + RX_BUF_END - 10, init_words, 10); + + /* Write the words at 0x0000. */ + memcpy_toio(lp->base, init_words + 5, sizeof(init_words) - 10); + + /* Fill in the station address. */ + memcpy_toio(lp->base+SA_OFFSET, dev->dev_addr, ETH_ALEN); + + /* The Tx-block list is written as needed. We just set up the values. */ + lp->tx_cmd_link = IDLELOOP + 4; + lp->tx_head = lp->tx_reap = TX_BUF_START; + + init_rx_bufs(dev); + + /* Start the 586 by releasing the reset line, but leave loopback. */ + outb(0xA0, ioaddr + MISC_CTRL); + + /* This was time consuming to track down: you need to give two channel + attention signals to reliably start up the i82586. */ + outb(0, ioaddr + SIGNAL_CA); + + { + int boguscnt = 50; + while (readw(shmem+iSCB_STATUS) == 0) + if (--boguscnt == 0) { + pr_warning("%s: i82586 initialization timed out with status %04x, cmd %04x.\n", + dev->name, readw(shmem+iSCB_STATUS), readw(shmem+iSCB_CMD)); + break; + } + /* Issue channel-attn -- the 82586 won't start. */ + outb(0, ioaddr + SIGNAL_CA); + } + + /* Disable loopback and enable interrupts. */ + outb(0x84, ioaddr + MISC_CTRL); + if (net_debug > 4) + pr_debug("%s: Initialized 82586, status %04x.\n", dev->name, + readw(shmem+iSCB_STATUS)); +} + +static void hardware_send_packet(struct net_device *dev, void *buf, short length, short pad) +{ + struct net_local *lp = netdev_priv(dev); + short ioaddr = dev->base_addr; + ushort tx_block = lp->tx_head; + void __iomem *write_ptr = lp->base + tx_block; + static char padding[ETH_ZLEN]; + + /* Set the write pointer to the Tx block, and put out the header. */ + writew(0x0000,write_ptr); /* Tx status */ + writew(CMD_INTR|CmdTx,write_ptr+=2); /* Tx command */ + writew(tx_block+16,write_ptr+=2); /* Next command is a NoOp. */ + writew(tx_block+8,write_ptr+=2); /* Data Buffer offset. */ + + /* Output the data buffer descriptor. */ + writew((pad + length) | 0x8000,write_ptr+=2); /* Byte count parameter. */ + writew(-1,write_ptr+=2); /* No next data buffer. */ + writew(tx_block+22+SCB_BASE,write_ptr+=2); /* Buffer follows the NoOp command. */ + writew(0x0000,write_ptr+=2); /* Buffer address high bits (always zero). */ + + /* Output the Loop-back NoOp command. */ + writew(0x0000,write_ptr+=2); /* Tx status */ + writew(CmdNOp,write_ptr+=2); /* Tx command */ + writew(tx_block+16,write_ptr+=2); /* Next is myself. */ + + /* Output the packet at the write pointer. */ + memcpy_toio(write_ptr+2, buf, length); + if (pad) + memcpy_toio(write_ptr+length+2, padding, pad); + + /* Set the old command link pointing to this send packet. */ + writew(tx_block,lp->base + lp->tx_cmd_link); + lp->tx_cmd_link = tx_block + 20; + + /* Set the next free tx region. */ + lp->tx_head = tx_block + TX_BUF_SIZE; + if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE) + lp->tx_head = TX_BUF_START; + + if (net_debug > 4) { + pr_debug("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n", + dev->name, ioaddr, length, tx_block, lp->tx_head); + } + + /* Grimly block further packets if there has been insufficient reaping. */ + if (++lp->tx_pkts_in_ring < NUM_TX_BUFS) + netif_wake_queue(dev); +} + +static void el16_rx(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + void __iomem *shmem = lp->base; + ushort rx_head = lp->rx_head; + ushort rx_tail = lp->rx_tail; + ushort boguscount = 10; + short frame_status; + + while ((frame_status = readw(shmem+rx_head)) < 0) { /* Command complete */ + void __iomem *read_frame = lp->base + rx_head; + ushort rfd_cmd = readw(read_frame+2); + ushort next_rx_frame = readw(read_frame+4); + ushort data_buffer_addr = readw(read_frame+6); + void __iomem *data_frame = lp->base + data_buffer_addr; + ushort pkt_len = readw(data_frame); + + if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 || + (pkt_len & 0xC000) != 0xC000) { + pr_err("%s: Rx frame at %#x corrupted, " + "status %04x cmd %04x next %04x " + "data-buf @%04x %04x.\n", + dev->name, rx_head, frame_status, rfd_cmd, + next_rx_frame, data_buffer_addr, pkt_len); + } else if ((frame_status & 0x2000) == 0) { + /* Frame Rxed, but with error. */ + dev->stats.rx_errors++; + if (frame_status & 0x0800) dev->stats.rx_crc_errors++; + if (frame_status & 0x0400) dev->stats.rx_frame_errors++; + if (frame_status & 0x0200) dev->stats.rx_fifo_errors++; + if (frame_status & 0x0100) dev->stats.rx_over_errors++; + if (frame_status & 0x0080) dev->stats.rx_length_errors++; + } else { + /* Malloc up new buffer. */ + struct sk_buff *skb; + + pkt_len &= 0x3fff; + skb = dev_alloc_skb(pkt_len+2); + if (skb == NULL) { + pr_err("%s: Memory squeeze, dropping packet.\n", + dev->name); + dev->stats.rx_dropped++; + break; + } + + skb_reserve(skb,2); + + /* 'skb->data' points to the start of sk_buff data area. */ + memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len); + + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + + /* Clear the status word and set End-of-List on the rx frame. */ + writew(0,read_frame); + writew(0xC000,read_frame+2); + /* Clear the end-of-list on the prev. RFD. */ + writew(0x0000,lp->base + rx_tail + 2); + + rx_tail = rx_head; + rx_head = next_rx_frame; + if (--boguscount == 0) + break; + } + + lp->rx_head = rx_head; + lp->rx_tail = rx_tail; +} + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); +} + +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return debug; +} + +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + debug = level; +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; + +#ifdef MODULE +static struct net_device *dev_3c507; +module_param(io, int, 0); +module_param(irq, int, 0); +MODULE_PARM_DESC(io, "EtherLink16 I/O base address"); +MODULE_PARM_DESC(irq, "(ignored)"); + +int __init init_module(void) +{ + if (io == 0) + pr_notice("3c507: You should not use auto-probing with insmod!\n"); + dev_3c507 = el16_probe(-1); + return IS_ERR(dev_3c507) ? PTR_ERR(dev_3c507) : 0; +} + +void __exit +cleanup_module(void) +{ + struct net_device *dev = dev_3c507; + unregister_netdev(dev); + free_irq(dev->irq, dev); + iounmap(((struct net_local *)netdev_priv(dev))->base); + release_region(dev->base_addr, EL16_IO_EXTENT); + free_netdev(dev); +} +#endif /* MODULE */ +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/i825xx/3c523.c b/drivers/net/ethernet/i825xx/3c523.c new file mode 100644 index 000000000000..bc0d1a1c2e28 --- /dev/null +++ b/drivers/net/ethernet/i825xx/3c523.c @@ -0,0 +1,1312 @@ +/* + net-3-driver for the 3c523 Etherlink/MC card (i82586 Ethernet chip) + + + This is an extension to the Linux operating system, and is covered by the + same GNU General Public License that covers that work. + + Copyright 1995, 1996 by Chris Beauregard (cpbeaure@undergrad.math.uwaterloo.ca) + + This is basically Michael Hipp's ni52 driver, with a new probing + algorithm and some minor changes to the 82586 CA and reset routines. + Thanks a lot Michael for a really clean i82586 implementation! Unless + otherwise documented in ni52.c, any bugs are mine. + + Contrary to the Ethernet-HOWTO, this isn't based on the 3c507 driver in + any way. The ni52 is a lot easier to modify. + + sources: + ni52.c + + Crynwr packet driver collection was a great reference for my first + attempt at this sucker. The 3c507 driver also helped, until I noticed + that ni52.c was a lot nicer. + + EtherLink/MC: Micro Channel Ethernet Adapter Technical Reference + Manual, courtesy of 3Com CardFacts, documents the 3c523-specific + stuff. Information on CardFacts is found in the Ethernet HOWTO. + Also see <a href="http://www.3com.com/"> + + Microprocessor Communications Support Chips, T.J. Byers, ISBN + 0-444-01224-9, has a section on the i82586. It tells you just enough + to know that you really don't want to learn how to program the chip. + + The original device probe code was stolen from ps2esdi.c + + Known Problems: + Since most of the code was stolen from ni52.c, you'll run across the + same bugs in the 0.62 version of ni52.c, plus maybe a few because of + the 3c523 idiosynchacies. The 3c523 has 16K of RAM though, so there + shouldn't be the overrun problem that the 8K ni52 has. + + This driver is for a 16K adapter. It should work fine on the 64K + adapters, but it will only use one of the 4 banks of RAM. Modifying + this for the 64K version would require a lot of heinous bank + switching, which I'm sure not interested in doing. If you try to + implement a bank switching version, you'll basically have to remember + what bank is enabled and do a switch every time you access a memory + location that's not current. You'll also have to remap pointers on + the driver side, because it only knows about 16K of the memory. + Anyone desperate or masochistic enough to try? + + It seems to be stable now when multiple transmit buffers are used. I + can't see any performance difference, but then I'm working on a 386SX. + + Multicast doesn't work. It doesn't even pretend to work. Don't use + it. Don't compile your kernel with multicast support. I don't know + why. + + Features: + This driver is useable as a loadable module. If you try to specify an + IRQ or a IO address (via insmod 3c523.o irq=xx io=0xyyy), it will + search the MCA slots until it finds a 3c523 with the specified + parameters. + + This driver does support multiple ethernet cards when used as a module + (up to MAX_3C523_CARDS, the default being 4) + + This has been tested with both BNC and TP versions, internal and + external transceivers. Haven't tested with the 64K version (that I + know of). + + History: + Jan 1st, 1996 + first public release + Feb 4th, 1996 + update to 1.3.59, incorporated multicast diffs from ni52.c + Feb 15th, 1996 + added shared irq support + Apr 1999 + added support for multiple cards when used as a module + added option to disable multicast as is causes problems + Ganesh Sittampalam <ganesh.sittampalam@magdalen.oxford.ac.uk> + Stuart Adamson <stuart.adamson@compsoc.net> + Nov 2001 + added support for ethtool (jgarzik) + + $Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $ + */ + +#define DRV_NAME "3c523" +#define DRV_VERSION "17-Nov-2001" + +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/skbuff.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/mca-legacy.h> +#include <linux/ethtool.h> +#include <linux/bitops.h> +#include <linux/jiffies.h> + +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/io.h> + +#include "3c523.h" + +/*************************************************************************/ +#define DEBUG /* debug on */ +#define SYSBUSVAL 0 /* 1 = 8 Bit, 0 = 16 bit - 3c523 only does 16 bit */ +#undef ELMC_MULTICAST /* Disable multicast support as it is somewhat seriously broken at the moment */ + +#define make32(ptr16) (p->memtop + (short) (ptr16) ) +#define make24(ptr32) ((char *) (ptr32) - p->base) +#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop )) + +/*************************************************************************/ +/* + Tables to which we can map values in the configuration registers. + */ +static int irq_table[] __initdata = { + 12, 7, 3, 9 +}; + +static int csr_table[] __initdata = { + 0x300, 0x1300, 0x2300, 0x3300 +}; + +static int shm_table[] __initdata = { + 0x0c0000, 0x0c8000, 0x0d0000, 0x0d8000 +}; + +/******************* how to calculate the buffers ***************************** + + + * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works + * --------------- in a different (more stable?) mode. Only in this mode it's + * possible to configure the driver with 'NO_NOPCOMMANDS' + +sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8; +sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT +sizeof(rfd) = 24; sizeof(rbd) = 12; +sizeof(tbd) = 8; sizeof(transmit_cmd) = 16; +sizeof(nop_cmd) = 8; + + * if you don't know the driver, better do not change this values: */ + +#define RECV_BUFF_SIZE 1524 /* slightly oversized */ +#define XMIT_BUFF_SIZE 1524 /* slightly oversized */ +#define NUM_XMIT_BUFFS 1 /* config for both, 8K and 16K shmem */ +#define NUM_RECV_BUFFS_8 4 /* config for 8K shared mem */ +#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */ + +#if (NUM_XMIT_BUFFS == 1) +#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */ +#endif + +/**************************************************************************/ + +#define DELAY(x) { mdelay(32 * x); } + +/* a much shorter delay: */ +#define DELAY_16(); { udelay(16) ; } + +/* wait for command with timeout: */ +#define WAIT_4_SCB_CMD() { int i; \ + for(i=0;i<1024;i++) { \ + if(!p->scb->cmd) break; \ + DELAY_16(); \ + if(i == 1023) { \ + pr_warning("%s:%d: scb_cmd timed out .. resetting i82586\n",\ + dev->name,__LINE__); \ + elmc_id_reset586(); } } } + +static irqreturn_t elmc_interrupt(int irq, void *dev_id); +static int elmc_open(struct net_device *dev); +static int elmc_close(struct net_device *dev); +static netdev_tx_t elmc_send_packet(struct sk_buff *, struct net_device *); +static struct net_device_stats *elmc_get_stats(struct net_device *dev); +static void elmc_timeout(struct net_device *dev); +#ifdef ELMC_MULTICAST +static void set_multicast_list(struct net_device *dev); +#endif +static const struct ethtool_ops netdev_ethtool_ops; + +/* helper-functions */ +static int init586(struct net_device *dev); +static int check586(struct net_device *dev, unsigned long where, unsigned size); +static void alloc586(struct net_device *dev); +static void startrecv586(struct net_device *dev); +static void *alloc_rfa(struct net_device *dev, void *ptr); +static void elmc_rcv_int(struct net_device *dev); +static void elmc_xmt_int(struct net_device *dev); +static void elmc_rnr_int(struct net_device *dev); + +struct priv { + unsigned long base; + char *memtop; + unsigned long mapped_start; /* Start of ioremap */ + volatile struct rfd_struct *rfd_last, *rfd_top, *rfd_first; + volatile struct scp_struct *scp; /* volatile is important */ + volatile struct iscp_struct *iscp; /* volatile is important */ + volatile struct scb_struct *scb; /* volatile is important */ + volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS]; +#if (NUM_XMIT_BUFFS == 1) + volatile struct transmit_cmd_struct *xmit_cmds[2]; + volatile struct nop_cmd_struct *nop_cmds[2]; +#else + volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS]; + volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS]; +#endif + volatile int nop_point, num_recv_buffs; + volatile char *xmit_cbuffs[NUM_XMIT_BUFFS]; + volatile int xmit_count, xmit_last; + volatile int slot; +}; + +#define elmc_attn586() {elmc_do_attn586(dev->base_addr,ELMC_CTRL_INTE);} +#define elmc_reset586() {elmc_do_reset586(dev->base_addr,ELMC_CTRL_INTE);} + +/* with interrupts disabled - this will clear the interrupt bit in the + 3c523 control register, and won't put it back. This effectively + disables interrupts on the card. */ +#define elmc_id_attn586() {elmc_do_attn586(dev->base_addr,0);} +#define elmc_id_reset586() {elmc_do_reset586(dev->base_addr,0);} + +/*************************************************************************/ +/* + Do a Channel Attention on the 3c523. This is extremely board dependent. + */ +static void elmc_do_attn586(int ioaddr, int ints) +{ + /* the 3c523 requires a minimum of 500 ns. The delays here might be + a little too large, and hence they may cut the performance of the + card slightly. If someone who knows a little more about Linux + timing would care to play with these, I'd appreciate it. */ + + /* this bit masking stuff is crap. I'd rather have separate + registers with strobe triggers for each of these functions. <sigh> + Ya take what ya got. */ + + outb(ELMC_CTRL_RST | 0x3 | ELMC_CTRL_CA | ints, ioaddr + ELMC_CTRL); + DELAY_16(); /* > 500 ns */ + outb(ELMC_CTRL_RST | 0x3 | ints, ioaddr + ELMC_CTRL); +} + +/*************************************************************************/ +/* + Reset the 82586 on the 3c523. Also very board dependent. + */ +static void elmc_do_reset586(int ioaddr, int ints) +{ + /* toggle the RST bit low then high */ + outb(0x3 | ELMC_CTRL_LBK, ioaddr + ELMC_CTRL); + DELAY_16(); /* > 500 ns */ + outb(ELMC_CTRL_RST | ELMC_CTRL_LBK | 0x3, ioaddr + ELMC_CTRL); + + elmc_do_attn586(ioaddr, ints); +} + +/********************************************** + * close device + */ + +static int elmc_close(struct net_device *dev) +{ + netif_stop_queue(dev); + elmc_id_reset586(); /* the hard way to stop the receiver */ + free_irq(dev->irq, dev); + return 0; +} + +/********************************************** + * open device + */ + +static int elmc_open(struct net_device *dev) +{ + int ret; + + elmc_id_attn586(); /* disable interrupts */ + + ret = request_irq(dev->irq, elmc_interrupt, IRQF_SHARED, + dev->name, dev); + if (ret) { + pr_err("%s: couldn't get irq %d\n", dev->name, dev->irq); + elmc_id_reset586(); + return ret; + } + alloc586(dev); + init586(dev); + startrecv586(dev); + netif_start_queue(dev); + return 0; /* most done by init */ +} + +/********************************************** + * Check to see if there's an 82586 out there. + */ + +static int __init check586(struct net_device *dev, unsigned long where, unsigned size) +{ + struct priv *p = netdev_priv(dev); + char *iscp_addrs[2]; + int i = 0; + + p->base = (unsigned long) isa_bus_to_virt((unsigned long)where) + size - 0x01000000; + p->memtop = isa_bus_to_virt((unsigned long)where) + size; + p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS); + memset((char *) p->scp, 0, sizeof(struct scp_struct)); + p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */ + + iscp_addrs[0] = isa_bus_to_virt((unsigned long)where); + iscp_addrs[1] = (char *) p->scp - sizeof(struct iscp_struct); + + for (i = 0; i < 2; i++) { + p->iscp = (struct iscp_struct *) iscp_addrs[i]; + memset((char *) p->iscp, 0, sizeof(struct iscp_struct)); + + p->scp->iscp = make24(p->iscp); + p->iscp->busy = 1; + + elmc_id_reset586(); + + /* reset586 does an implicit CA */ + + /* apparently, you sometimes have to kick the 82586 twice... */ + elmc_id_attn586(); + DELAY(1); + + if (p->iscp->busy) { /* i82586 clears 'busy' after successful init */ + return 0; + } + } + return 1; +} + +/****************************************************************** + * set iscp at the right place, called by elmc_probe and open586. + */ + +static void alloc586(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + elmc_id_reset586(); + DELAY(2); + + p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS); + p->scb = (struct scb_struct *) isa_bus_to_virt(dev->mem_start); + p->iscp = (struct iscp_struct *) ((char *) p->scp - sizeof(struct iscp_struct)); + + memset((char *) p->iscp, 0, sizeof(struct iscp_struct)); + memset((char *) p->scp, 0, sizeof(struct scp_struct)); + + p->scp->iscp = make24(p->iscp); + p->scp->sysbus = SYSBUSVAL; + p->iscp->scb_offset = make16(p->scb); + + p->iscp->busy = 1; + elmc_id_reset586(); + elmc_id_attn586(); + + DELAY(2); + + if (p->iscp->busy) + pr_err("%s: Init-Problems (alloc).\n", dev->name); + + memset((char *) p->scb, 0, sizeof(struct scb_struct)); +} + +/*****************************************************************/ + +static int elmc_getinfo(char *buf, int slot, void *d) +{ + int len = 0; + struct net_device *dev = d; + + if (dev == NULL) + return len; + + len += sprintf(buf + len, "Revision: 0x%x\n", + inb(dev->base_addr + ELMC_REVISION) & 0xf); + len += sprintf(buf + len, "IRQ: %d\n", dev->irq); + len += sprintf(buf + len, "IO Address: %#lx-%#lx\n", dev->base_addr, + dev->base_addr + ELMC_IO_EXTENT); + len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start, + dev->mem_end - 1); + len += sprintf(buf + len, "Transceiver: %s\n", dev->if_port ? + "External" : "Internal"); + len += sprintf(buf + len, "Device: %s\n", dev->name); + len += sprintf(buf + len, "Hardware Address: %pM\n", + dev->dev_addr); + + return len; +} /* elmc_getinfo() */ + +static const struct net_device_ops netdev_ops = { + .ndo_open = elmc_open, + .ndo_stop = elmc_close, + .ndo_get_stats = elmc_get_stats, + .ndo_start_xmit = elmc_send_packet, + .ndo_tx_timeout = elmc_timeout, +#ifdef ELMC_MULTICAST + .ndo_set_multicast_list = set_multicast_list, +#endif + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +/*****************************************************************/ + +static int __init do_elmc_probe(struct net_device *dev) +{ + static int slot; + int base_addr = dev->base_addr; + int irq = dev->irq; + u_char status = 0; + u_char revision = 0; + int i = 0; + unsigned int size = 0; + int retval; + struct priv *pr = netdev_priv(dev); + + if (MCA_bus == 0) { + return -ENODEV; + } + /* search through the slots for the 3c523. */ + slot = mca_find_adapter(ELMC_MCA_ID, 0); + while (slot != -1) { + status = mca_read_stored_pos(slot, 2); + + dev->irq=irq_table[(status & ELMC_STATUS_IRQ_SELECT) >> 6]; + dev->base_addr=csr_table[(status & ELMC_STATUS_CSR_SELECT) >> 1]; + + /* + If we're trying to match a specified irq or IO address, + we'll reject a match unless it's what we're looking for. + Also reject it if the card is already in use. + */ + + if ((irq && irq != dev->irq) || + (base_addr && base_addr != dev->base_addr)) { + slot = mca_find_adapter(ELMC_MCA_ID, slot + 1); + continue; + } + if (!request_region(dev->base_addr, ELMC_IO_EXTENT, DRV_NAME)) { + slot = mca_find_adapter(ELMC_MCA_ID, slot + 1); + continue; + } + + /* found what we're looking for... */ + break; + } + + /* we didn't find any 3c523 in the slots we checked for */ + if (slot == MCA_NOTFOUND) + return (base_addr || irq) ? -ENXIO : -ENODEV; + + mca_set_adapter_name(slot, "3Com 3c523 Etherlink/MC"); + mca_set_adapter_procfn(slot, (MCA_ProcFn) elmc_getinfo, dev); + + /* if we get this far, adapter has been found - carry on */ + pr_info("%s: 3c523 adapter found in slot %d\n", dev->name, slot + 1); + + /* Now we extract configuration info from the card. + The 3c523 provides information in two of the POS registers, but + the second one is only needed if we want to tell the card what IRQ + to use. I suspect that whoever sets the thing up initially would + prefer we don't screw with those things. + + Note that we read the status info when we found the card... + + See 3c523.h for more details. + */ + + /* revision is stored in the first 4 bits of the revision register */ + revision = inb(dev->base_addr + ELMC_REVISION) & 0xf; + + /* according to docs, we read the interrupt and write it back to + the IRQ select register, since the POST might not configure the IRQ + properly. */ + switch (dev->irq) { + case 3: + mca_write_pos(slot, 3, 0x04); + break; + case 7: + mca_write_pos(slot, 3, 0x02); + break; + case 9: + mca_write_pos(slot, 3, 0x08); + break; + case 12: + mca_write_pos(slot, 3, 0x01); + break; + } + + pr->slot = slot; + + pr_info("%s: 3Com 3c523 Rev 0x%x at %#lx\n", dev->name, (int) revision, + dev->base_addr); + + /* Determine if we're using the on-board transceiver (i.e. coax) or + an external one. The information is pretty much useless, but I + guess it's worth brownie points. */ + dev->if_port = (status & ELMC_STATUS_DISABLE_THIN); + + /* The 3c523 has a 24K chunk of memory. The first 16K is the + shared memory, while the last 8K is for the EtherStart BIOS ROM. + Which we don't care much about here. We'll just tell Linux that + we're using 16K. MCA won't permit address space conflicts caused + by not mapping the other 8K. */ + dev->mem_start = shm_table[(status & ELMC_STATUS_MEMORY_SELECT) >> 3]; + + /* We're using MCA, so it's a given that the information about memory + size is correct. The Crynwr drivers do something like this. */ + + elmc_id_reset586(); /* seems like a good idea before checking it... */ + + size = 0x4000; /* check for 16K mem */ + if (!check586(dev, dev->mem_start, size)) { + pr_err("%s: memprobe, Can't find memory at 0x%lx!\n", dev->name, + dev->mem_start); + retval = -ENODEV; + goto err_out; + } + dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */ + + pr->memtop = isa_bus_to_virt(dev->mem_start) + size; + pr->base = (unsigned long) isa_bus_to_virt(dev->mem_start) + size - 0x01000000; + alloc586(dev); + + elmc_id_reset586(); /* make sure it doesn't generate spurious ints */ + + /* set number of receive-buffs according to memsize */ + pr->num_recv_buffs = NUM_RECV_BUFFS_16; + + /* dump all the assorted information */ + pr_info("%s: IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->name, + dev->irq, dev->if_port ? "ex" : "in", + dev->mem_start, dev->mem_end - 1); + + /* The hardware address for the 3c523 is stored in the first six + bytes of the IO address. */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inb(dev->base_addr + i); + + pr_info("%s: hardware address %pM\n", + dev->name, dev->dev_addr); + + dev->netdev_ops = &netdev_ops; + dev->watchdog_timeo = HZ; + dev->ethtool_ops = &netdev_ethtool_ops; + + /* note that we haven't actually requested the IRQ from the kernel. + That gets done in elmc_open(). I'm not sure that's such a good idea, + but it works, so I'll go with it. */ + +#ifndef ELMC_MULTICAST + dev->flags&=~IFF_MULTICAST; /* Multicast doesn't work */ +#endif + + retval = register_netdev(dev); + if (retval) + goto err_out; + + return 0; +err_out: + mca_set_adapter_procfn(slot, NULL, NULL); + release_region(dev->base_addr, ELMC_IO_EXTENT); + return retval; +} + +#ifdef MODULE +static void cleanup_card(struct net_device *dev) +{ + mca_set_adapter_procfn(((struct priv *)netdev_priv(dev))->slot, + NULL, NULL); + release_region(dev->base_addr, ELMC_IO_EXTENT); +} +#else +struct net_device * __init elmc_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct priv)); + int err; + + if (!dev) + return ERR_PTR(-ENOMEM); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = do_elmc_probe(dev); + if (err) + goto out; + return dev; +out: + free_netdev(dev); + return ERR_PTR(err); +} +#endif + +/********************************************** + * init the chip (elmc-interrupt should be disabled?!) + * needs a correct 'allocated' memory + */ + +static int init586(struct net_device *dev) +{ + void *ptr; + unsigned long s; + int i, result = 0; + struct priv *p = netdev_priv(dev); + volatile struct configure_cmd_struct *cfg_cmd; + volatile struct iasetup_cmd_struct *ias_cmd; + volatile struct tdr_cmd_struct *tdr_cmd; + volatile struct mcsetup_cmd_struct *mc_cmd; + struct netdev_hw_addr *ha; + int num_addrs = netdev_mc_count(dev); + + ptr = (void *) ((char *) p->scb + sizeof(struct scb_struct)); + + cfg_cmd = (struct configure_cmd_struct *) ptr; /* configure-command */ + cfg_cmd->cmd_status = 0; + cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST; + cfg_cmd->cmd_link = 0xffff; + + cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */ + cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */ + cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */ + cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */ + cfg_cmd->priority = 0x00; + cfg_cmd->ifs = 0x60; + cfg_cmd->time_low = 0x00; + cfg_cmd->time_high = 0xf2; + cfg_cmd->promisc = 0; + if (dev->flags & (IFF_ALLMULTI | IFF_PROMISC)) + cfg_cmd->promisc = 1; + cfg_cmd->carr_coll = 0x00; + + p->scb->cbl_offset = make16(cfg_cmd); + + p->scb->cmd = CUC_START; /* cmd.-unit start */ + elmc_id_attn586(); + + s = jiffies; /* warning: only active with interrupts on !! */ + while (!(cfg_cmd->cmd_status & STAT_COMPL)) { + if (time_after(jiffies, s + 30*HZ/100)) + break; + } + + if ((cfg_cmd->cmd_status & (STAT_OK | STAT_COMPL)) != (STAT_COMPL | STAT_OK)) { + pr_warning("%s (elmc): configure command failed: %x\n", dev->name, cfg_cmd->cmd_status); + return 1; + } + /* + * individual address setup + */ + ias_cmd = (struct iasetup_cmd_struct *) ptr; + + ias_cmd->cmd_status = 0; + ias_cmd->cmd_cmd = CMD_IASETUP | CMD_LAST; + ias_cmd->cmd_link = 0xffff; + + memcpy((char *) &ias_cmd->iaddr, (char *) dev->dev_addr, ETH_ALEN); + + p->scb->cbl_offset = make16(ias_cmd); + + p->scb->cmd = CUC_START; /* cmd.-unit start */ + elmc_id_attn586(); + + s = jiffies; + while (!(ias_cmd->cmd_status & STAT_COMPL)) { + if (time_after(jiffies, s + 30*HZ/100)) + break; + } + + if ((ias_cmd->cmd_status & (STAT_OK | STAT_COMPL)) != (STAT_OK | STAT_COMPL)) { + pr_warning("%s (elmc): individual address setup command failed: %04x\n", + dev->name, ias_cmd->cmd_status); + return 1; + } + /* + * TDR, wire check .. e.g. no resistor e.t.c + */ + tdr_cmd = (struct tdr_cmd_struct *) ptr; + + tdr_cmd->cmd_status = 0; + tdr_cmd->cmd_cmd = CMD_TDR | CMD_LAST; + tdr_cmd->cmd_link = 0xffff; + tdr_cmd->status = 0; + + p->scb->cbl_offset = make16(tdr_cmd); + + p->scb->cmd = CUC_START; /* cmd.-unit start */ + elmc_attn586(); + + s = jiffies; + while (!(tdr_cmd->cmd_status & STAT_COMPL)) { + if (time_after(jiffies, s + 30*HZ/100)) { + pr_warning("%s: %d Problems while running the TDR.\n", dev->name, __LINE__); + result = 1; + break; + } + } + + if (!result) { + DELAY(2); /* wait for result */ + result = tdr_cmd->status; + + p->scb->cmd = p->scb->status & STAT_MASK; + elmc_id_attn586(); /* ack the interrupts */ + + if (result & TDR_LNK_OK) { + /* empty */ + } else if (result & TDR_XCVR_PRB) { + pr_warning("%s: TDR: Transceiver problem!\n", dev->name); + } else if (result & TDR_ET_OPN) { + pr_warning("%s: TDR: No correct termination %d clocks away.\n", dev->name, result & TDR_TIMEMASK); + } else if (result & TDR_ET_SRT) { + if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */ + pr_warning("%s: TDR: Detected a short circuit %d clocks away.\n", dev->name, result & TDR_TIMEMASK); + } else { + pr_warning("%s: TDR: Unknown status %04x\n", dev->name, result); + } + } + /* + * ack interrupts + */ + p->scb->cmd = p->scb->status & STAT_MASK; + elmc_id_attn586(); + + /* + * alloc nop/xmit-cmds + */ +#if (NUM_XMIT_BUFFS == 1) + for (i = 0; i < 2; i++) { + p->nop_cmds[i] = (struct nop_cmd_struct *) ptr; + p->nop_cmds[i]->cmd_cmd = CMD_NOP; + p->nop_cmds[i]->cmd_status = 0; + p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i])); + ptr = (char *) ptr + sizeof(struct nop_cmd_struct); + } + p->xmit_cmds[0] = (struct transmit_cmd_struct *) ptr; /* transmit cmd/buff 0 */ + ptr = (char *) ptr + sizeof(struct transmit_cmd_struct); +#else + for (i = 0; i < NUM_XMIT_BUFFS; i++) { + p->nop_cmds[i] = (struct nop_cmd_struct *) ptr; + p->nop_cmds[i]->cmd_cmd = CMD_NOP; + p->nop_cmds[i]->cmd_status = 0; + p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i])); + ptr = (char *) ptr + sizeof(struct nop_cmd_struct); + p->xmit_cmds[i] = (struct transmit_cmd_struct *) ptr; /*transmit cmd/buff 0 */ + ptr = (char *) ptr + sizeof(struct transmit_cmd_struct); + } +#endif + + ptr = alloc_rfa(dev, (void *) ptr); /* init receive-frame-area */ + + /* + * Multicast setup + */ + + if (num_addrs) { + /* I don't understand this: do we really need memory after the init? */ + int len = ((char *) p->iscp - (char *) ptr - 8) / 6; + if (len <= 0) { + pr_err("%s: Ooooops, no memory for MC-Setup!\n", dev->name); + } else { + if (len < num_addrs) { + num_addrs = len; + pr_warning("%s: Sorry, can only apply %d MC-Address(es).\n", + dev->name, num_addrs); + } + mc_cmd = (struct mcsetup_cmd_struct *) ptr; + mc_cmd->cmd_status = 0; + mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST; + mc_cmd->cmd_link = 0xffff; + mc_cmd->mc_cnt = num_addrs * 6; + i = 0; + netdev_for_each_mc_addr(ha, dev) + memcpy((char *) mc_cmd->mc_list[i++], + ha->addr, 6); + p->scb->cbl_offset = make16(mc_cmd); + p->scb->cmd = CUC_START; + elmc_id_attn586(); + s = jiffies; + while (!(mc_cmd->cmd_status & STAT_COMPL)) { + if (time_after(jiffies, s + 30*HZ/100)) + break; + } + if (!(mc_cmd->cmd_status & STAT_COMPL)) { + pr_warning("%s: Can't apply multicast-address-list.\n", dev->name); + } + } + } + /* + * alloc xmit-buffs / init xmit_cmds + */ + for (i = 0; i < NUM_XMIT_BUFFS; i++) { + p->xmit_cbuffs[i] = (char *) ptr; /* char-buffs */ + ptr = (char *) ptr + XMIT_BUFF_SIZE; + p->xmit_buffs[i] = (struct tbd_struct *) ptr; /* TBD */ + ptr = (char *) ptr + sizeof(struct tbd_struct); + if ((void *) ptr > (void *) p->iscp) { + pr_err("%s: not enough shared-mem for your configuration!\n", dev->name); + return 1; + } + memset((char *) (p->xmit_cmds[i]), 0, sizeof(struct transmit_cmd_struct)); + memset((char *) (p->xmit_buffs[i]), 0, sizeof(struct tbd_struct)); + p->xmit_cmds[i]->cmd_status = STAT_COMPL; + p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT; + p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i])); + p->xmit_buffs[i]->next = 0xffff; + p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i])); + } + + p->xmit_count = 0; + p->xmit_last = 0; +#ifndef NO_NOPCOMMANDS + p->nop_point = 0; +#endif + + /* + * 'start transmitter' (nop-loop) + */ +#ifndef NO_NOPCOMMANDS + p->scb->cbl_offset = make16(p->nop_cmds[0]); + p->scb->cmd = CUC_START; + elmc_id_attn586(); + WAIT_4_SCB_CMD(); +#else + p->xmit_cmds[0]->cmd_link = 0xffff; + p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_LAST | CMD_INT; +#endif + + return 0; +} + +/****************************************************** + * This is a helper routine for elmc_rnr_int() and init586(). + * It sets up the Receive Frame Area (RFA). + */ + +static void *alloc_rfa(struct net_device *dev, void *ptr) +{ + volatile struct rfd_struct *rfd = (struct rfd_struct *) ptr; + volatile struct rbd_struct *rbd; + int i; + struct priv *p = netdev_priv(dev); + + memset((char *) rfd, 0, sizeof(struct rfd_struct) * p->num_recv_buffs); + p->rfd_first = rfd; + + for (i = 0; i < p->num_recv_buffs; i++) { + rfd[i].next = make16(rfd + (i + 1) % p->num_recv_buffs); + } + rfd[p->num_recv_buffs - 1].last = RFD_SUSP; /* RU suspend */ + + ptr = (void *) (rfd + p->num_recv_buffs); + + rbd = (struct rbd_struct *) ptr; + ptr = (void *) (rbd + p->num_recv_buffs); + + /* clr descriptors */ + memset((char *) rbd, 0, sizeof(struct rbd_struct) * p->num_recv_buffs); + + for (i = 0; i < p->num_recv_buffs; i++) { + rbd[i].next = make16((rbd + (i + 1) % p->num_recv_buffs)); + rbd[i].size = RECV_BUFF_SIZE; + rbd[i].buffer = make24(ptr); + ptr = (char *) ptr + RECV_BUFF_SIZE; + } + + p->rfd_top = p->rfd_first; + p->rfd_last = p->rfd_first + p->num_recv_buffs - 1; + + p->scb->rfa_offset = make16(p->rfd_first); + p->rfd_first->rbd_offset = make16(rbd); + + return ptr; +} + + +/************************************************** + * Interrupt Handler ... + */ + +static irqreturn_t +elmc_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + unsigned short stat; + struct priv *p; + + if (!netif_running(dev)) { + /* The 3c523 has this habit of generating interrupts during the + reset. I'm not sure if the ni52 has this same problem, but it's + really annoying if we haven't finished initializing it. I was + hoping all the elmc_id_* commands would disable this, but I + might have missed a few. */ + + elmc_id_attn586(); /* ack inter. and disable any more */ + return IRQ_HANDLED; + } else if (!(ELMC_CTRL_INT & inb(dev->base_addr + ELMC_CTRL))) { + /* wasn't this device */ + return IRQ_NONE; + } + /* reading ELMC_CTRL also clears the INT bit. */ + + p = netdev_priv(dev); + + while ((stat = p->scb->status & STAT_MASK)) + { + p->scb->cmd = stat; + elmc_attn586(); /* ack inter. */ + + if (stat & STAT_CX) { + /* command with I-bit set complete */ + elmc_xmt_int(dev); + } + if (stat & STAT_FR) { + /* received a frame */ + elmc_rcv_int(dev); + } +#ifndef NO_NOPCOMMANDS + if (stat & STAT_CNA) { + /* CU went 'not ready' */ + if (netif_running(dev)) { + pr_warning("%s: oops! CU has left active state. stat: %04x/%04x.\n", + dev->name, (int) stat, (int) p->scb->status); + } + } +#endif + + if (stat & STAT_RNR) { + /* RU went 'not ready' */ + + if (p->scb->status & RU_SUSPEND) { + /* special case: RU_SUSPEND */ + + WAIT_4_SCB_CMD(); + p->scb->cmd = RUC_RESUME; + elmc_attn586(); + } else { + pr_warning("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n", + dev->name, (int) stat, (int) p->scb->status); + elmc_rnr_int(dev); + } + } + WAIT_4_SCB_CMD(); /* wait for ack. (elmc_xmt_int can be faster than ack!!) */ + if (p->scb->cmd) { /* timed out? */ + break; + } + } + return IRQ_HANDLED; +} + +/******************************************************* + * receive-interrupt + */ + +static void elmc_rcv_int(struct net_device *dev) +{ + int status; + unsigned short totlen; + struct sk_buff *skb; + struct rbd_struct *rbd; + struct priv *p = netdev_priv(dev); + + for (; (status = p->rfd_top->status) & STAT_COMPL;) { + rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset); + + if (status & STAT_OK) { /* frame received without error? */ + if ((totlen = rbd->status) & RBD_LAST) { /* the first and the last buffer? */ + totlen &= RBD_MASK; /* length of this frame */ + rbd->status = 0; + skb = (struct sk_buff *) dev_alloc_skb(totlen + 2); + if (skb != NULL) { + skb_reserve(skb, 2); /* 16 byte alignment */ + skb_put(skb,totlen); + skb_copy_to_linear_data(skb, (char *) p->base+(unsigned long) rbd->buffer,totlen); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += totlen; + } else { + dev->stats.rx_dropped++; + } + } else { + pr_warning("%s: received oversized frame.\n", dev->name); + dev->stats.rx_dropped++; + } + } else { /* frame !(ok), only with 'save-bad-frames' */ + pr_warning("%s: oops! rfd-error-status: %04x\n", dev->name, status); + dev->stats.rx_errors++; + } + p->rfd_top->status = 0; + p->rfd_top->last = RFD_SUSP; + p->rfd_last->last = 0; /* delete RU_SUSP */ + p->rfd_last = p->rfd_top; + p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */ + } +} + +/********************************************************** + * handle 'Receiver went not ready'. + */ + +static void elmc_rnr_int(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + dev->stats.rx_errors++; + + WAIT_4_SCB_CMD(); /* wait for the last cmd */ + p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */ + elmc_attn586(); + WAIT_4_SCB_CMD(); /* wait for accept cmd. */ + + alloc_rfa(dev, (char *) p->rfd_first); + startrecv586(dev); /* restart RU */ + + pr_warning("%s: Receive-Unit restarted. Status: %04x\n", dev->name, p->scb->status); + +} + +/********************************************************** + * handle xmit - interrupt + */ + +static void elmc_xmt_int(struct net_device *dev) +{ + int status; + struct priv *p = netdev_priv(dev); + + status = p->xmit_cmds[p->xmit_last]->cmd_status; + if (!(status & STAT_COMPL)) { + pr_warning("%s: strange .. xmit-int without a 'COMPLETE'\n", dev->name); + } + if (status & STAT_OK) { + dev->stats.tx_packets++; + dev->stats.collisions += (status & TCMD_MAXCOLLMASK); + } else { + dev->stats.tx_errors++; + if (status & TCMD_LATECOLL) { + pr_warning("%s: late collision detected.\n", dev->name); + dev->stats.collisions++; + } else if (status & TCMD_NOCARRIER) { + dev->stats.tx_carrier_errors++; + pr_warning("%s: no carrier detected.\n", dev->name); + } else if (status & TCMD_LOSTCTS) { + pr_warning("%s: loss of CTS detected.\n", dev->name); + } else if (status & TCMD_UNDERRUN) { + dev->stats.tx_fifo_errors++; + pr_warning("%s: DMA underrun detected.\n", dev->name); + } else if (status & TCMD_MAXCOLL) { + pr_warning("%s: Max. collisions exceeded.\n", dev->name); + dev->stats.collisions += 16; + } + } + +#if (NUM_XMIT_BUFFS != 1) + if ((++p->xmit_last) == NUM_XMIT_BUFFS) { + p->xmit_last = 0; + } +#endif + + netif_wake_queue(dev); +} + +/*********************************************************** + * (re)start the receiver + */ + +static void startrecv586(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + p->scb->rfa_offset = make16(p->rfd_first); + p->scb->cmd = RUC_START; + elmc_attn586(); /* start cmd. */ + WAIT_4_SCB_CMD(); /* wait for accept cmd. (no timeout!!) */ +} + +/****************************************************** + * timeout + */ + +static void elmc_timeout(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + /* COMMAND-UNIT active? */ + if (p->scb->status & CU_ACTIVE) { + pr_debug("%s: strange ... timeout with CU active?!?\n", dev->name); + pr_debug("%s: X0: %04x N0: %04x N1: %04x %d\n", dev->name, + (int)p->xmit_cmds[0]->cmd_status, + (int)p->nop_cmds[0]->cmd_status, + (int)p->nop_cmds[1]->cmd_status, (int)p->nop_point); + p->scb->cmd = CUC_ABORT; + elmc_attn586(); + WAIT_4_SCB_CMD(); + p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]); + p->scb->cmd = CUC_START; + elmc_attn586(); + WAIT_4_SCB_CMD(); + netif_wake_queue(dev); + } else { + pr_debug("%s: xmitter timed out, try to restart! stat: %04x\n", + dev->name, p->scb->status); + pr_debug("%s: command-stats: %04x %04x\n", dev->name, + p->xmit_cmds[0]->cmd_status, p->xmit_cmds[1]->cmd_status); + elmc_close(dev); + elmc_open(dev); + } +} + +/****************************************************** + * send frame + */ + +static netdev_tx_t elmc_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + int len; + int i; +#ifndef NO_NOPCOMMANDS + int next_nop; +#endif + struct priv *p = netdev_priv(dev); + + netif_stop_queue(dev); + + len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + + if (len != skb->len) + memset((char *) p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN); + skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len); + +#if (NUM_XMIT_BUFFS == 1) +#ifdef NO_NOPCOMMANDS + p->xmit_buffs[0]->size = TBD_LAST | len; + for (i = 0; i < 16; i++) { + p->scb->cbl_offset = make16(p->xmit_cmds[0]); + p->scb->cmd = CUC_START; + p->xmit_cmds[0]->cmd_status = 0; + elmc_attn586(); + if (!i) { + dev_kfree_skb(skb); + } + WAIT_4_SCB_CMD(); + if ((p->scb->status & CU_ACTIVE)) { /* test it, because CU sometimes doesn't start immediately */ + break; + } + if (p->xmit_cmds[0]->cmd_status) { + break; + } + if (i == 15) { + pr_warning("%s: Can't start transmit-command.\n", dev->name); + } + } +#else + next_nop = (p->nop_point + 1) & 0x1; + p->xmit_buffs[0]->size = TBD_LAST | len; + + p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link + = make16((p->nop_cmds[next_nop])); + p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0; + + p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0])); + p->nop_point = next_nop; + dev_kfree_skb(skb); +#endif +#else + p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len; + if ((next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS) { + next_nop = 0; + } + p->xmit_cmds[p->xmit_count]->cmd_status = 0; + p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link + = make16((p->nop_cmds[next_nop])); + p->nop_cmds[next_nop]->cmd_status = 0; + p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count])); + p->xmit_count = next_nop; + if (p->xmit_count != p->xmit_last) + netif_wake_queue(dev); + dev_kfree_skb(skb); +#endif + return NETDEV_TX_OK; +} + +/******************************************* + * Someone wanna have the statistics + */ + +static struct net_device_stats *elmc_get_stats(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + unsigned short crc, aln, rsc, ovrn; + + crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */ + p->scb->crc_errs -= crc; + aln = p->scb->aln_errs; + p->scb->aln_errs -= aln; + rsc = p->scb->rsc_errs; + p->scb->rsc_errs -= rsc; + ovrn = p->scb->ovrn_errs; + p->scb->ovrn_errs -= ovrn; + + dev->stats.rx_crc_errors += crc; + dev->stats.rx_fifo_errors += ovrn; + dev->stats.rx_frame_errors += aln; + dev->stats.rx_dropped += rsc; + + return &dev->stats; +} + +/******************************************************** + * Set MC list .. + */ + +#ifdef ELMC_MULTICAST +static void set_multicast_list(struct net_device *dev) +{ + if (!dev->start) { + /* without a running interface, promiscuous doesn't work */ + return; + } + dev->start = 0; + alloc586(dev); + init586(dev); + startrecv586(dev); + dev->start = 1; +} +#endif + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "MCA 0x%lx", dev->base_addr); +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + +#ifdef MODULE + +/* Increase if needed ;) */ +#define MAX_3C523_CARDS 4 + +static struct net_device *dev_elmc[MAX_3C523_CARDS]; +static int irq[MAX_3C523_CARDS]; +static int io[MAX_3C523_CARDS]; +module_param_array(irq, int, NULL, 0); +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io, "EtherLink/MC I/O base address(es)"); +MODULE_PARM_DESC(irq, "EtherLink/MC IRQ number(s)"); +MODULE_LICENSE("GPL"); + +int __init init_module(void) +{ + int this_dev,found = 0; + + /* Loop until we either can't find any more cards, or we have MAX_3C523_CARDS */ + for(this_dev=0; this_dev<MAX_3C523_CARDS; this_dev++) { + struct net_device *dev = alloc_etherdev(sizeof(struct priv)); + if (!dev) + break; + dev->irq=irq[this_dev]; + dev->base_addr=io[this_dev]; + if (do_elmc_probe(dev) == 0) { + dev_elmc[this_dev] = dev; + found++; + continue; + } + free_netdev(dev); + if (io[this_dev]==0) + break; + pr_warning("3c523.c: No 3c523 card found at io=%#x\n",io[this_dev]); + } + + if(found==0) { + if (io[0]==0) + pr_notice("3c523.c: No 3c523 cards found\n"); + return -ENXIO; + } else return 0; +} + +void __exit cleanup_module(void) +{ + int this_dev; + for (this_dev=0; this_dev<MAX_3C523_CARDS; this_dev++) { + struct net_device *dev = dev_elmc[this_dev]; + if (dev) { + unregister_netdev(dev); + cleanup_card(dev); + free_netdev(dev); + } + } +} + +#endif /* MODULE */ diff --git a/drivers/net/ethernet/i825xx/3c523.h b/drivers/net/ethernet/i825xx/3c523.h new file mode 100644 index 000000000000..6956441687b9 --- /dev/null +++ b/drivers/net/ethernet/i825xx/3c523.h @@ -0,0 +1,355 @@ +#ifndef _3c523_INCLUDE_ +#define _3c523_INCLUDE_ +/* + This is basically a hacked version of ni52.h, for the 3c523 + Etherlink/MC. +*/ + +/* + * Intel i82586 Ethernet definitions + * + * This is an extension to the Linux operating system, and is covered by the + * same GNU General Public License that covers that work. + * + * Copyright 1995 by Chris Beauregard (cpbeaure@undergrad.math.uwaterloo.ca) + * + * See 3c523.c for details. + * + * $Header: /home/chrisb/linux-1.2.13-3c523/drivers/net/RCS/3c523.h,v 1.6 1996/01/20 05:09:00 chrisb Exp chrisb $ + */ + +/* + * where to find the System Configuration Pointer (SCP) + */ +#define SCP_DEFAULT_ADDRESS 0xfffff4 + + +/* + * System Configuration Pointer Struct + */ + +struct scp_struct +{ + unsigned short zero_dum0; /* has to be zero */ + unsigned char sysbus; /* 0=16Bit,1=8Bit */ + unsigned char zero_dum1; /* has to be zero for 586 */ + unsigned short zero_dum2; + unsigned short zero_dum3; + char *iscp; /* pointer to the iscp-block */ +}; + + +/* + * Intermediate System Configuration Pointer (ISCP) + */ +struct iscp_struct +{ + unsigned char busy; /* 586 clears after successful init */ + unsigned char zero_dummy; /* hast to be zero */ + unsigned short scb_offset; /* pointeroffset to the scb_base */ + char *scb_base; /* base-address of all 16-bit offsets */ +}; + +/* + * System Control Block (SCB) + */ +struct scb_struct +{ + unsigned short status; /* status word */ + unsigned short cmd; /* command word */ + unsigned short cbl_offset; /* pointeroffset, command block list */ + unsigned short rfa_offset; /* pointeroffset, receive frame area */ + unsigned short crc_errs; /* CRC-Error counter */ + unsigned short aln_errs; /* alignmenterror counter */ + unsigned short rsc_errs; /* Resourceerror counter */ + unsigned short ovrn_errs; /* OVerrunerror counter */ +}; + +/* + * possible command values for the command word + */ +#define RUC_MASK 0x0070 /* mask for RU commands */ +#define RUC_NOP 0x0000 /* NOP-command */ +#define RUC_START 0x0010 /* start RU */ +#define RUC_RESUME 0x0020 /* resume RU after suspend */ +#define RUC_SUSPEND 0x0030 /* suspend RU */ +#define RUC_ABORT 0x0040 /* abort receiver operation immediately */ + +#define CUC_MASK 0x0700 /* mask for CU command */ +#define CUC_NOP 0x0000 /* NOP-command */ +#define CUC_START 0x0100 /* start execution of 1. cmd on the CBL */ +#define CUC_RESUME 0x0200 /* resume after suspend */ +#define CUC_SUSPEND 0x0300 /* Suspend CU */ +#define CUC_ABORT 0x0400 /* abort command operation immediately */ + +#define ACK_MASK 0xf000 /* mask for ACK command */ +#define ACK_CX 0x8000 /* acknowledges STAT_CX */ +#define ACK_FR 0x4000 /* ack. STAT_FR */ +#define ACK_CNA 0x2000 /* ack. STAT_CNA */ +#define ACK_RNR 0x1000 /* ack. STAT_RNR */ + +/* + * possible status values for the status word + */ +#define STAT_MASK 0xf000 /* mask for cause of interrupt */ +#define STAT_CX 0x8000 /* CU finished cmd with its I bit set */ +#define STAT_FR 0x4000 /* RU finished receiving a frame */ +#define STAT_CNA 0x2000 /* CU left active state */ +#define STAT_RNR 0x1000 /* RU left ready state */ + +#define CU_STATUS 0x700 /* CU status, 0=idle */ +#define CU_SUSPEND 0x100 /* CU is suspended */ +#define CU_ACTIVE 0x200 /* CU is active */ + +#define RU_STATUS 0x70 /* RU status, 0=idle */ +#define RU_SUSPEND 0x10 /* RU suspended */ +#define RU_NOSPACE 0x20 /* RU no resources */ +#define RU_READY 0x40 /* RU is ready */ + +/* + * Receive Frame Descriptor (RFD) + */ +struct rfd_struct +{ + unsigned short status; /* status word */ + unsigned short last; /* Bit15,Last Frame on List / Bit14,suspend */ + unsigned short next; /* linkoffset to next RFD */ + unsigned short rbd_offset; /* pointeroffset to RBD-buffer */ + unsigned char dest[6]; /* ethernet-address, destination */ + unsigned char source[6]; /* ethernet-address, source */ + unsigned short length; /* 802.3 frame-length */ + unsigned short zero_dummy; /* dummy */ +}; + +#define RFD_LAST 0x8000 /* last: last rfd in the list */ +#define RFD_SUSP 0x4000 /* last: suspend RU after */ +#define RFD_ERRMASK 0x0fe1 /* status: errormask */ +#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA */ +#define RFD_RNR 0x0200 /* status: receiver out of resources */ + +/* + * Receive Buffer Descriptor (RBD) + */ +struct rbd_struct +{ + unsigned short status; /* status word,number of used bytes in buff */ + unsigned short next; /* pointeroffset to next RBD */ + char *buffer; /* receive buffer address pointer */ + unsigned short size; /* size of this buffer */ + unsigned short zero_dummy; /* dummy */ +}; + +#define RBD_LAST 0x8000 /* last buffer */ +#define RBD_USED 0x4000 /* this buffer has data */ +#define RBD_MASK 0x3fff /* size-mask for length */ + +/* + * Statusvalues for Commands/RFD + */ +#define STAT_COMPL 0x8000 /* status: frame/command is complete */ +#define STAT_BUSY 0x4000 /* status: frame/command is busy */ +#define STAT_OK 0x2000 /* status: frame/command is ok */ + +/* + * Action-Commands + */ +#define CMD_NOP 0x0000 /* NOP */ +#define CMD_IASETUP 0x0001 /* initial address setup command */ +#define CMD_CONFIGURE 0x0002 /* configure command */ +#define CMD_MCSETUP 0x0003 /* MC setup command */ +#define CMD_XMIT 0x0004 /* transmit command */ +#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */ +#define CMD_DUMP 0x0006 /* dump command */ +#define CMD_DIAGNOSE 0x0007 /* diagnose command */ + +/* + * Action command bits + */ +#define CMD_LAST 0x8000 /* indicates last command in the CBL */ +#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */ +#define CMD_INT 0x2000 /* generate interrupt after execution */ + +/* + * NOP - command + */ +struct nop_cmd_struct +{ + unsigned short cmd_status; /* status of this command */ + unsigned short cmd_cmd; /* the command itself (+bits) */ + unsigned short cmd_link; /* offsetpointer to next command */ +}; + +/* + * IA Setup command + */ +struct iasetup_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned char iaddr[6]; +}; + +/* + * Configure command + */ +struct configure_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned char byte_cnt; /* size of the config-cmd */ + unsigned char fifo; /* fifo/recv monitor */ + unsigned char sav_bf; /* save bad frames (bit7=1)*/ + unsigned char adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/ + unsigned char priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */ + unsigned char ifs; /* inter frame spacing */ + unsigned char time_low; /* slot time low */ + unsigned char time_high; /* slot time high(0-2) and max. retries(4-7) */ + unsigned char promisc; /* promisc-mode(0) , et al (1-7) */ + unsigned char carr_coll; /* carrier(0-3)/collision(4-7) stuff */ + unsigned char fram_len; /* minimal frame len */ + unsigned char dummy; /* dummy */ +}; + +/* + * Multicast Setup command + */ +struct mcsetup_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short mc_cnt; /* number of bytes in the MC-List */ + unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */ +}; + +/* + * transmit command + */ +struct transmit_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short tbd_offset; /* pointeroffset to TBD */ + unsigned char dest[6]; /* destination address of the frame */ + unsigned short length; /* user defined: 802.3 length / Ether type */ +}; + +#define TCMD_ERRMASK 0x0fa0 +#define TCMD_MAXCOLLMASK 0x000f +#define TCMD_MAXCOLL 0x0020 +#define TCMD_HEARTBEAT 0x0040 +#define TCMD_DEFERRED 0x0080 +#define TCMD_UNDERRUN 0x0100 +#define TCMD_LOSTCTS 0x0200 +#define TCMD_NOCARRIER 0x0400 +#define TCMD_LATECOLL 0x0800 + +struct tdr_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short status; +}; + +#define TDR_LNK_OK 0x8000 /* No link problem identified */ +#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */ +#define TDR_ET_OPN 0x2000 /* open, no correct termination */ +#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */ +#define TDR_TIMEMASK 0x07ff /* mask for the time field */ + +/* + * Transmit Buffer Descriptor (TBD) + */ +struct tbd_struct +{ + unsigned short size; /* size + EOF-Flag(15) */ + unsigned short next; /* pointeroffset to next TBD */ + char *buffer; /* pointer to buffer */ +}; + +#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */ + +/*************************************************************************/ +/* +Verbatim from the Crynwyr stuff: + + The 3c523 responds with adapter code 0x6042 at slot +registers xxx0 and xxx1. The setup register is at xxx2 and +contains the following bits: + +0: card enable +2,1: csr address select + 00 = 0300 + 01 = 1300 + 10 = 2300 + 11 = 3300 +4,3: shared memory address select + 00 = 0c0000 + 01 = 0c8000 + 10 = 0d0000 + 11 = 0d8000 +5: set to disable on-board thinnet +7,6: (read-only) shows selected irq + 00 = 12 + 01 = 7 + 10 = 3 + 11 = 9 + +The interrupt-select register is at xxx3 and uses one bit per irq. + +0: int 12 +1: int 7 +2: int 3 +3: int 9 + + Again, the documentation stresses that the setup register +should never be written. The interrupt-select register may be +written with the value corresponding to bits 7.6 in +the setup register to insure corret setup. +*/ + +/* Offsets from the base I/O address. */ +#define ELMC_SA 0 /* first 6 bytes are IEEE network address */ +#define ELMC_CTRL 6 /* control & status register */ +#define ELMC_REVISION 7 /* revision register, first 4 bits only */ +#define ELMC_IO_EXTENT 8 + +/* these are the bit selects for the port register 2 */ +#define ELMC_STATUS_ENABLED 0x01 +#define ELMC_STATUS_CSR_SELECT 0x06 +#define ELMC_STATUS_MEMORY_SELECT 0x18 +#define ELMC_STATUS_DISABLE_THIN 0x20 +#define ELMC_STATUS_IRQ_SELECT 0xc0 + +/* this is the card id used in the detection code. You might recognize +it from @6042.adf */ +#define ELMC_MCA_ID 0x6042 + +/* + The following define the bits for the control & status register + + The bank select registers can be used if more than 16K of memory is + on the card. For some stupid reason, bank 3 is the one for the + bottom 16K, and the card defaults to bank 0. So we have to set the + bank to 3 before the card will even think of operating. To get bank + 3, set BS0 and BS1 to high (of course...) +*/ +#define ELMC_CTRL_BS0 0x01 /* RW bank select */ +#define ELMC_CTRL_BS1 0x02 /* RW bank select */ +#define ELMC_CTRL_INTE 0x04 /* RW interrupt enable, assert high */ +#define ELMC_CTRL_INT 0x08 /* R interrupt active, assert high */ +/*#define ELMC_CTRL_* 0x10*/ /* reserved */ +#define ELMC_CTRL_LBK 0x20 /* RW loopback enable, assert high */ +#define ELMC_CTRL_CA 0x40 /* RW channel attention, assert high */ +#define ELMC_CTRL_RST 0x80 /* RW 82586 reset, assert low */ + +/* some handy compound bits */ + +/* normal operation should have bank 3 and RST high, ints enabled */ +#define ELMC_NORMAL (ELMC_CTRL_INTE|ELMC_CTRL_RST|0x3) + +#endif /* _3c523_INCLUDE_ */ diff --git a/drivers/net/ethernet/i825xx/3c527.c b/drivers/net/ethernet/i825xx/3c527.c new file mode 100644 index 000000000000..d9d056d207f3 --- /dev/null +++ b/drivers/net/ethernet/i825xx/3c527.c @@ -0,0 +1,1661 @@ +/* 3c527.c: 3Com Etherlink/MC32 driver for Linux 2.4 and 2.6. + * + * (c) Copyright 1998 Red Hat Software Inc + * Written by Alan Cox. + * Further debugging by Carl Drougge. + * Initial SMP support by Felipe W Damasio <felipewd@terra.com.br> + * Heavily modified by Richard Procter <rnp@paradise.net.nz> + * + * Based on skeleton.c written 1993-94 by Donald Becker and ne2.c + * (for the MCA stuff) written by Wim Dumon. + * + * Thanks to 3Com for making this possible by providing me with the + * documentation. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#define DRV_NAME "3c527" +#define DRV_VERSION "0.7-SMP" +#define DRV_RELDATE "2003/09/21" + +static const char *version = +DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Richard Procter <rnp@paradise.net.nz>\n"; + +/** + * DOC: Traps for the unwary + * + * The diagram (Figure 1-1) and the POS summary disagree with the + * "Interrupt Level" section in the manual. + * + * The manual contradicts itself when describing the minimum number + * buffers in the 'configure lists' command. + * My card accepts a buffer config of 4/4. + * + * Setting the SAV BP bit does not save bad packets, but + * only enables RX on-card stats collection. + * + * The documentation in places seems to miss things. In actual fact + * I've always eventually found everything is documented, it just + * requires careful study. + * + * DOC: Theory Of Operation + * + * The 3com 3c527 is a 32bit MCA bus mastering adapter with a large + * amount of on board intelligence that housekeeps a somewhat dumber + * Intel NIC. For performance we want to keep the transmit queue deep + * as the card can transmit packets while fetching others from main + * memory by bus master DMA. Transmission and reception are driven by + * circular buffer queues. + * + * The mailboxes can be used for controlling how the card traverses + * its buffer rings, but are used only for initial setup in this + * implementation. The exec mailbox allows a variety of commands to + * be executed. Each command must complete before the next is + * executed. Primarily we use the exec mailbox for controlling the + * multicast lists. We have to do a certain amount of interesting + * hoop jumping as the multicast list changes can occur in interrupt + * state when the card has an exec command pending. We defer such + * events until the command completion interrupt. + * + * A copy break scheme (taken from 3c59x.c) is employed whereby + * received frames exceeding a configurable length are passed + * directly to the higher networking layers without incuring a copy, + * in what amounts to a time/space trade-off. + * + * The card also keeps a large amount of statistical information + * on-board. In a perfect world, these could be used safely at no + * cost. However, lacking information to the contrary, processing + * them without races would involve so much extra complexity as to + * make it unworthwhile to do so. In the end, a hybrid SW/HW + * implementation was made necessary --- see mc32_update_stats(). + * + * DOC: Notes + * + * It should be possible to use two or more cards, but at this stage + * only by loading two copies of the same module. + * + * The on-board 82586 NIC has trouble receiving multiple + * back-to-back frames and so is likely to drop packets from fast + * senders. +**/ + +#include <linux/module.h> + +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_ether.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/mca-legacy.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/wait.h> +#include <linux/ethtool.h> +#include <linux/completion.h> +#include <linux/bitops.h> +#include <linux/semaphore.h> + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include "3c527.h" + +MODULE_LICENSE("GPL"); + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ +static const char* cardname = DRV_NAME; + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 2 +#endif + +static unsigned int mc32_debug = NET_DEBUG; + +/* The number of low I/O ports used by the ethercard. */ +#define MC32_IO_EXTENT 8 + +/* As implemented, values must be a power-of-2 -- 4/8/16/32 */ +#define TX_RING_LEN 32 /* Typically the card supports 37 */ +#define RX_RING_LEN 8 /* " " " */ + +/* Copy break point, see above for details. + * Setting to > 1512 effectively disables this feature. */ +#define RX_COPYBREAK 200 /* Value from 3c59x.c */ + +/* Issue the 82586 workaround command - this is for "busy lans", but + * basically means for all lans now days - has a performance (latency) + * cost, but best set. */ +static const int WORKAROUND_82586=1; + +/* Pointers to buffers and their on-card records */ +struct mc32_ring_desc +{ + volatile struct skb_header *p; + struct sk_buff *skb; +}; + +/* Information that needs to be kept for each board. */ +struct mc32_local +{ + int slot; + + u32 base; + volatile struct mc32_mailbox *rx_box; + volatile struct mc32_mailbox *tx_box; + volatile struct mc32_mailbox *exec_box; + volatile struct mc32_stats *stats; /* Start of on-card statistics */ + u16 tx_chain; /* Transmit list start offset */ + u16 rx_chain; /* Receive list start offset */ + u16 tx_len; /* Transmit list count */ + u16 rx_len; /* Receive list count */ + + u16 xceiver_desired_state; /* HALTED or RUNNING */ + u16 cmd_nonblocking; /* Thread is uninterested in command result */ + u16 mc_reload_wait; /* A multicast load request is pending */ + u32 mc_list_valid; /* True when the mclist is set */ + + struct mc32_ring_desc tx_ring[TX_RING_LEN]; /* Host Transmit ring */ + struct mc32_ring_desc rx_ring[RX_RING_LEN]; /* Host Receive ring */ + + atomic_t tx_count; /* buffers left */ + atomic_t tx_ring_head; /* index to tx en-queue end */ + u16 tx_ring_tail; /* index to tx de-queue end */ + + u16 rx_ring_tail; /* index to rx de-queue end */ + + struct semaphore cmd_mutex; /* Serialises issuing of execute commands */ + struct completion execution_cmd; /* Card has completed an execute command */ + struct completion xceiver_cmd; /* Card has completed a tx or rx command */ +}; + +/* The station (ethernet) address prefix, used for a sanity check. */ +#define SA_ADDR0 0x02 +#define SA_ADDR1 0x60 +#define SA_ADDR2 0xAC + +struct mca_adapters_t { + unsigned int id; + char *name; +}; + +static const struct mca_adapters_t mc32_adapters[] = { + { 0x0041, "3COM EtherLink MC/32" }, + { 0x8EF5, "IBM High Performance Lan Adapter" }, + { 0x0000, NULL } +}; + + +/* Macros for ring index manipulations */ +static inline u16 next_rx(u16 rx) { return (rx+1)&(RX_RING_LEN-1); }; +static inline u16 prev_rx(u16 rx) { return (rx-1)&(RX_RING_LEN-1); }; + +static inline u16 next_tx(u16 tx) { return (tx+1)&(TX_RING_LEN-1); }; + + +/* Index to functions, as function prototypes. */ +static int mc32_probe1(struct net_device *dev, int ioaddr); +static int mc32_command(struct net_device *dev, u16 cmd, void *data, int len); +static int mc32_open(struct net_device *dev); +static void mc32_timeout(struct net_device *dev); +static netdev_tx_t mc32_send_packet(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t mc32_interrupt(int irq, void *dev_id); +static int mc32_close(struct net_device *dev); +static struct net_device_stats *mc32_get_stats(struct net_device *dev); +static void mc32_set_multicast_list(struct net_device *dev); +static void mc32_reset_multicast_list(struct net_device *dev); +static const struct ethtool_ops netdev_ethtool_ops; + +static void cleanup_card(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + unsigned slot = lp->slot; + mca_mark_as_unused(slot); + mca_set_adapter_name(slot, NULL); + free_irq(dev->irq, dev); + release_region(dev->base_addr, MC32_IO_EXTENT); +} + +/** + * mc32_probe - Search for supported boards + * @unit: interface number to use + * + * Because MCA bus is a real bus and we can scan for cards we could do a + * single scan for all boards here. Right now we use the passed in device + * structure and scan for only one board. This needs fixing for modules + * in particular. + */ + +struct net_device *__init mc32_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct mc32_local)); + static int current_mca_slot = -1; + int i; + int err; + + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) + sprintf(dev->name, "eth%d", unit); + + /* Do not check any supplied i/o locations. + POS registers usually don't fail :) */ + + /* MCA cards have POS registers. + Autodetecting MCA cards is extremely simple. + Just search for the card. */ + + for(i = 0; (mc32_adapters[i].name != NULL); i++) { + current_mca_slot = + mca_find_unused_adapter(mc32_adapters[i].id, 0); + + if(current_mca_slot != MCA_NOTFOUND) { + if(!mc32_probe1(dev, current_mca_slot)) + { + mca_set_adapter_name(current_mca_slot, + mc32_adapters[i].name); + mca_mark_as_used(current_mca_slot); + err = register_netdev(dev); + if (err) { + cleanup_card(dev); + free_netdev(dev); + dev = ERR_PTR(err); + } + return dev; + } + + } + } + free_netdev(dev); + return ERR_PTR(-ENODEV); +} + +static const struct net_device_ops netdev_ops = { + .ndo_open = mc32_open, + .ndo_stop = mc32_close, + .ndo_start_xmit = mc32_send_packet, + .ndo_get_stats = mc32_get_stats, + .ndo_set_multicast_list = mc32_set_multicast_list, + .ndo_tx_timeout = mc32_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +/** + * mc32_probe1 - Check a given slot for a board and test the card + * @dev: Device structure to fill in + * @slot: The MCA bus slot being used by this card + * + * Decode the slot data and configure the card structures. Having done this we + * can reset the card and configure it. The card does a full self test cycle + * in firmware so we have to wait for it to return and post us either a + * failure case or some addresses we use to find the board internals. + */ + +static int __init mc32_probe1(struct net_device *dev, int slot) +{ + static unsigned version_printed; + int i, err; + u8 POS; + u32 base; + struct mc32_local *lp = netdev_priv(dev); + static const u16 mca_io_bases[] = { + 0x7280,0x7290, + 0x7680,0x7690, + 0x7A80,0x7A90, + 0x7E80,0x7E90 + }; + static const u32 mca_mem_bases[] = { + 0x00C0000, + 0x00C4000, + 0x00C8000, + 0x00CC000, + 0x00D0000, + 0x00D4000, + 0x00D8000, + 0x00DC000 + }; + static const char * const failures[] = { + "Processor instruction", + "Processor data bus", + "Processor data bus", + "Processor data bus", + "Adapter bus", + "ROM checksum", + "Base RAM", + "Extended RAM", + "82586 internal loopback", + "82586 initialisation failure", + "Adapter list configuration error" + }; + + /* Time to play MCA games */ + + if (mc32_debug && version_printed++ == 0) + pr_debug("%s", version); + + pr_info("%s: %s found in slot %d: ", dev->name, cardname, slot); + + POS = mca_read_stored_pos(slot, 2); + + if(!(POS&1)) + { + pr_cont("disabled.\n"); + return -ENODEV; + } + + /* Fill in the 'dev' fields. */ + dev->base_addr = mca_io_bases[(POS>>1)&7]; + dev->mem_start = mca_mem_bases[(POS>>4)&7]; + + POS = mca_read_stored_pos(slot, 4); + if(!(POS&1)) + { + pr_cont("memory window disabled.\n"); + return -ENODEV; + } + + POS = mca_read_stored_pos(slot, 5); + + i=(POS>>4)&3; + if(i==3) + { + pr_cont("invalid memory window.\n"); + return -ENODEV; + } + + i*=16384; + i+=16384; + + dev->mem_end=dev->mem_start + i; + + dev->irq = ((POS>>2)&3)+9; + + if(!request_region(dev->base_addr, MC32_IO_EXTENT, cardname)) + { + pr_cont("io 0x%3lX, which is busy.\n", dev->base_addr); + return -EBUSY; + } + + pr_cont("io 0x%3lX irq %d mem 0x%lX (%dK)\n", + dev->base_addr, dev->irq, dev->mem_start, i/1024); + + + /* We ought to set the cache line size here.. */ + + + /* + * Go PROM browsing + */ + + /* Retrieve and print the ethernet address. */ + for (i = 0; i < 6; i++) + { + mca_write_pos(slot, 6, i+12); + mca_write_pos(slot, 7, 0); + + dev->dev_addr[i] = mca_read_pos(slot,3); + } + + pr_info("%s: Address %pM ", dev->name, dev->dev_addr); + + mca_write_pos(slot, 6, 0); + mca_write_pos(slot, 7, 0); + + POS = mca_read_stored_pos(slot, 4); + + if(POS&2) + pr_cont(": BNC port selected.\n"); + else + pr_cont(": AUI port selected.\n"); + + POS=inb(dev->base_addr+HOST_CTRL); + POS|=HOST_CTRL_ATTN|HOST_CTRL_RESET; + POS&=~HOST_CTRL_INTE; + outb(POS, dev->base_addr+HOST_CTRL); + /* Reset adapter */ + udelay(100); + /* Reset off */ + POS&=~(HOST_CTRL_ATTN|HOST_CTRL_RESET); + outb(POS, dev->base_addr+HOST_CTRL); + + udelay(300); + + /* + * Grab the IRQ + */ + + err = request_irq(dev->irq, mc32_interrupt, IRQF_SHARED, DRV_NAME, dev); + if (err) { + release_region(dev->base_addr, MC32_IO_EXTENT); + pr_err("%s: unable to get IRQ %d.\n", DRV_NAME, dev->irq); + goto err_exit_ports; + } + + memset(lp, 0, sizeof(struct mc32_local)); + lp->slot = slot; + + i=0; + + base = inb(dev->base_addr); + + while(base == 0xFF) + { + i++; + if(i == 1000) + { + pr_err("%s: failed to boot adapter.\n", dev->name); + err = -ENODEV; + goto err_exit_irq; + } + udelay(1000); + if(inb(dev->base_addr+2)&(1<<5)) + base = inb(dev->base_addr); + } + + if(base>0) + { + if(base < 0x0C) + pr_err("%s: %s%s.\n", dev->name, failures[base-1], + base<0x0A?" test failure":""); + else + pr_err("%s: unknown failure %d.\n", dev->name, base); + err = -ENODEV; + goto err_exit_irq; + } + + base=0; + for(i=0;i<4;i++) + { + int n=0; + + while(!(inb(dev->base_addr+2)&(1<<5))) + { + n++; + udelay(50); + if(n>100) + { + pr_err("%s: mailbox read fail (%d).\n", dev->name, i); + err = -ENODEV; + goto err_exit_irq; + } + } + + base|=(inb(dev->base_addr)<<(8*i)); + } + + lp->exec_box=isa_bus_to_virt(dev->mem_start+base); + + base=lp->exec_box->data[1]<<16|lp->exec_box->data[0]; + + lp->base = dev->mem_start+base; + + lp->rx_box=isa_bus_to_virt(lp->base + lp->exec_box->data[2]); + lp->tx_box=isa_bus_to_virt(lp->base + lp->exec_box->data[3]); + + lp->stats = isa_bus_to_virt(lp->base + lp->exec_box->data[5]); + + /* + * Descriptor chains (card relative) + */ + + lp->tx_chain = lp->exec_box->data[8]; /* Transmit list start offset */ + lp->rx_chain = lp->exec_box->data[10]; /* Receive list start offset */ + lp->tx_len = lp->exec_box->data[9]; /* Transmit list count */ + lp->rx_len = lp->exec_box->data[11]; /* Receive list count */ + + sema_init(&lp->cmd_mutex, 0); + init_completion(&lp->execution_cmd); + init_completion(&lp->xceiver_cmd); + + pr_info("%s: Firmware Rev %d. %d RX buffers, %d TX buffers. Base of 0x%08X.\n", + dev->name, lp->exec_box->data[12], lp->rx_len, lp->tx_len, lp->base); + + dev->netdev_ops = &netdev_ops; + dev->watchdog_timeo = HZ*5; /* Board does all the work */ + dev->ethtool_ops = &netdev_ethtool_ops; + + return 0; + +err_exit_irq: + free_irq(dev->irq, dev); +err_exit_ports: + release_region(dev->base_addr, MC32_IO_EXTENT); + return err; +} + + +/** + * mc32_ready_poll - wait until we can feed it a command + * @dev: The device to wait for + * + * Wait until the card becomes ready to accept a command via the + * command register. This tells us nothing about the completion + * status of any pending commands and takes very little time at all. + */ + +static inline void mc32_ready_poll(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); +} + + +/** + * mc32_command_nowait - send a command non blocking + * @dev: The 3c527 to issue the command to + * @cmd: The command word to write to the mailbox + * @data: A data block if the command expects one + * @len: Length of the data block + * + * Send a command from interrupt state. If there is a command + * currently being executed then we return an error of -1. It + * simply isn't viable to wait around as commands may be + * slow. This can theoretically be starved on SMP, but it's hard + * to see a realistic situation. We do not wait for the command + * to complete --- we rely on the interrupt handler to tidy up + * after us. + */ + +static int mc32_command_nowait(struct net_device *dev, u16 cmd, void *data, int len) +{ + struct mc32_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + int ret = -1; + + if (down_trylock(&lp->cmd_mutex) == 0) + { + lp->cmd_nonblocking=1; + lp->exec_box->mbox=0; + lp->exec_box->mbox=cmd; + memcpy((void *)lp->exec_box->data, data, len); + barrier(); /* the memcpy forgot the volatile so be sure */ + + /* Send the command */ + mc32_ready_poll(dev); + outb(1<<6, ioaddr+HOST_CMD); + + ret = 0; + + /* Interrupt handler will signal mutex on completion */ + } + + return ret; +} + + +/** + * mc32_command - send a command and sleep until completion + * @dev: The 3c527 card to issue the command to + * @cmd: The command word to write to the mailbox + * @data: A data block if the command expects one + * @len: Length of the data block + * + * Sends exec commands in a user context. This permits us to wait around + * for the replies and also to wait for the command buffer to complete + * from a previous command before we execute our command. After our + * command completes we will attempt any pending multicast reload + * we blocked off by hogging the exec buffer. + * + * You feed the card a command, you wait, it interrupts you get a + * reply. All well and good. The complication arises because you use + * commands for filter list changes which come in at bh level from things + * like IPV6 group stuff. + */ + +static int mc32_command(struct net_device *dev, u16 cmd, void *data, int len) +{ + struct mc32_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + int ret = 0; + + down(&lp->cmd_mutex); + + /* + * My Turn + */ + + lp->cmd_nonblocking=0; + lp->exec_box->mbox=0; + lp->exec_box->mbox=cmd; + memcpy((void *)lp->exec_box->data, data, len); + barrier(); /* the memcpy forgot the volatile so be sure */ + + mc32_ready_poll(dev); + outb(1<<6, ioaddr+HOST_CMD); + + wait_for_completion(&lp->execution_cmd); + + if(lp->exec_box->mbox&(1<<13)) + ret = -1; + + up(&lp->cmd_mutex); + + /* + * A multicast set got blocked - try it now + */ + + if(lp->mc_reload_wait) + { + mc32_reset_multicast_list(dev); + } + + return ret; +} + + +/** + * mc32_start_transceiver - tell board to restart tx/rx + * @dev: The 3c527 card to issue the command to + * + * This may be called from the interrupt state, where it is used + * to restart the rx ring if the card runs out of rx buffers. + * + * We must first check if it's ok to (re)start the transceiver. See + * mc32_close for details. + */ + +static void mc32_start_transceiver(struct net_device *dev) { + + struct mc32_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + /* Ignore RX overflow on device closure */ + if (lp->xceiver_desired_state==HALTED) + return; + + /* Give the card the offset to the post-EOL-bit RX descriptor */ + mc32_ready_poll(dev); + lp->rx_box->mbox=0; + lp->rx_box->data[0]=lp->rx_ring[prev_rx(lp->rx_ring_tail)].p->next; + outb(HOST_CMD_START_RX, ioaddr+HOST_CMD); + + mc32_ready_poll(dev); + lp->tx_box->mbox=0; + outb(HOST_CMD_RESTRT_TX, ioaddr+HOST_CMD); /* card ignores this on RX restart */ + + /* We are not interrupted on start completion */ +} + + +/** + * mc32_halt_transceiver - tell board to stop tx/rx + * @dev: The 3c527 card to issue the command to + * + * We issue the commands to halt the card's transceiver. In fact, + * after some experimenting we now simply tell the card to + * suspend. When issuing aborts occasionally odd things happened. + * + * We then sleep until the card has notified us that both rx and + * tx have been suspended. + */ + +static void mc32_halt_transceiver(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + mc32_ready_poll(dev); + lp->rx_box->mbox=0; + outb(HOST_CMD_SUSPND_RX, ioaddr+HOST_CMD); + wait_for_completion(&lp->xceiver_cmd); + + mc32_ready_poll(dev); + lp->tx_box->mbox=0; + outb(HOST_CMD_SUSPND_TX, ioaddr+HOST_CMD); + wait_for_completion(&lp->xceiver_cmd); +} + + +/** + * mc32_load_rx_ring - load the ring of receive buffers + * @dev: 3c527 to build the ring for + * + * This initialises the on-card and driver datastructures to + * the point where mc32_start_transceiver() can be called. + * + * The card sets up the receive ring for us. We are required to use the + * ring it provides, although the size of the ring is configurable. + * + * We allocate an sk_buff for each ring entry in turn and + * initialise its house-keeping info. At the same time, we read + * each 'next' pointer in our rx_ring array. This reduces slow + * shared-memory reads and makes it easy to access predecessor + * descriptors. + * + * We then set the end-of-list bit for the last entry so that the + * card will know when it has run out of buffers. + */ + +static int mc32_load_rx_ring(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + int i; + u16 rx_base; + volatile struct skb_header *p; + + rx_base=lp->rx_chain; + + for(i=0; i<RX_RING_LEN; i++) { + lp->rx_ring[i].skb=alloc_skb(1532, GFP_KERNEL); + if (lp->rx_ring[i].skb==NULL) { + for (;i>=0;i--) + kfree_skb(lp->rx_ring[i].skb); + return -ENOBUFS; + } + skb_reserve(lp->rx_ring[i].skb, 18); + + p=isa_bus_to_virt(lp->base+rx_base); + + p->control=0; + p->data=isa_virt_to_bus(lp->rx_ring[i].skb->data); + p->status=0; + p->length=1532; + + lp->rx_ring[i].p=p; + rx_base=p->next; + } + + lp->rx_ring[i-1].p->control |= CONTROL_EOL; + + lp->rx_ring_tail=0; + + return 0; +} + + +/** + * mc32_flush_rx_ring - free the ring of receive buffers + * @lp: Local data of 3c527 to flush the rx ring of + * + * Free the buffer for each ring slot. This may be called + * before mc32_load_rx_ring(), eg. on error in mc32_open(). + * Requires rx skb pointers to point to a valid skb, or NULL. + */ + +static void mc32_flush_rx_ring(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + int i; + + for(i=0; i < RX_RING_LEN; i++) + { + if (lp->rx_ring[i].skb) { + dev_kfree_skb(lp->rx_ring[i].skb); + lp->rx_ring[i].skb = NULL; + } + lp->rx_ring[i].p=NULL; + } +} + + +/** + * mc32_load_tx_ring - load transmit ring + * @dev: The 3c527 card to issue the command to + * + * This sets up the host transmit data-structures. + * + * First, we obtain from the card it's current position in the tx + * ring, so that we will know where to begin transmitting + * packets. + * + * Then, we read the 'next' pointers from the on-card tx ring into + * our tx_ring array to reduce slow shared-mem reads. Finally, we + * intitalise the tx house keeping variables. + * + */ + +static void mc32_load_tx_ring(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + volatile struct skb_header *p; + int i; + u16 tx_base; + + tx_base=lp->tx_box->data[0]; + + for(i=0 ; i<TX_RING_LEN ; i++) + { + p=isa_bus_to_virt(lp->base+tx_base); + lp->tx_ring[i].p=p; + lp->tx_ring[i].skb=NULL; + + tx_base=p->next; + } + + /* -1 so that tx_ring_head cannot "lap" tx_ring_tail */ + /* see mc32_tx_ring */ + + atomic_set(&lp->tx_count, TX_RING_LEN-1); + atomic_set(&lp->tx_ring_head, 0); + lp->tx_ring_tail=0; +} + + +/** + * mc32_flush_tx_ring - free transmit ring + * @lp: Local data of 3c527 to flush the tx ring of + * + * If the ring is non-empty, zip over the it, freeing any + * allocated skb_buffs. The tx ring house-keeping variables are + * then reset. Requires rx skb pointers to point to a valid skb, + * or NULL. + */ + +static void mc32_flush_tx_ring(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + int i; + + for (i=0; i < TX_RING_LEN; i++) + { + if (lp->tx_ring[i].skb) + { + dev_kfree_skb(lp->tx_ring[i].skb); + lp->tx_ring[i].skb = NULL; + } + } + + atomic_set(&lp->tx_count, 0); + atomic_set(&lp->tx_ring_head, 0); + lp->tx_ring_tail=0; +} + + +/** + * mc32_open - handle 'up' of card + * @dev: device to open + * + * The user is trying to bring the card into ready state. This requires + * a brief dialogue with the card. Firstly we enable interrupts and then + * 'indications'. Without these enabled the card doesn't bother telling + * us what it has done. This had me puzzled for a week. + * + * We configure the number of card descriptors, then load the network + * address and multicast filters. Turn on the workaround mode. This + * works around a bug in the 82586 - it asks the firmware to do + * so. It has a performance (latency) hit but is needed on busy + * [read most] lans. We load the ring with buffers then we kick it + * all off. + */ + +static int mc32_open(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct mc32_local *lp = netdev_priv(dev); + u8 one=1; + u8 regs; + u16 descnumbuffs[2] = {TX_RING_LEN, RX_RING_LEN}; + + /* + * Interrupts enabled + */ + + regs=inb(ioaddr+HOST_CTRL); + regs|=HOST_CTRL_INTE; + outb(regs, ioaddr+HOST_CTRL); + + /* + * Allow ourselves to issue commands + */ + + up(&lp->cmd_mutex); + + + /* + * Send the indications on command + */ + + mc32_command(dev, 4, &one, 2); + + /* + * Poke it to make sure it's really dead. + */ + + mc32_halt_transceiver(dev); + mc32_flush_tx_ring(dev); + + /* + * Ask card to set up on-card descriptors to our spec + */ + + if(mc32_command(dev, 8, descnumbuffs, 4)) { + pr_info("%s: %s rejected our buffer configuration!\n", + dev->name, cardname); + mc32_close(dev); + return -ENOBUFS; + } + + /* Report new configuration */ + mc32_command(dev, 6, NULL, 0); + + lp->tx_chain = lp->exec_box->data[8]; /* Transmit list start offset */ + lp->rx_chain = lp->exec_box->data[10]; /* Receive list start offset */ + lp->tx_len = lp->exec_box->data[9]; /* Transmit list count */ + lp->rx_len = lp->exec_box->data[11]; /* Receive list count */ + + /* Set Network Address */ + mc32_command(dev, 1, dev->dev_addr, 6); + + /* Set the filters */ + mc32_set_multicast_list(dev); + + if (WORKAROUND_82586) { + u16 zero_word=0; + mc32_command(dev, 0x0D, &zero_word, 2); /* 82586 bug workaround on */ + } + + mc32_load_tx_ring(dev); + + if(mc32_load_rx_ring(dev)) + { + mc32_close(dev); + return -ENOBUFS; + } + + lp->xceiver_desired_state = RUNNING; + + /* And finally, set the ball rolling... */ + mc32_start_transceiver(dev); + + netif_start_queue(dev); + + return 0; +} + + +/** + * mc32_timeout - handle a timeout from the network layer + * @dev: 3c527 that timed out + * + * Handle a timeout on transmit from the 3c527. This normally means + * bad things as the hardware handles cable timeouts and mess for + * us. + * + */ + +static void mc32_timeout(struct net_device *dev) +{ + pr_warning("%s: transmit timed out?\n", dev->name); + /* Try to restart the adaptor. */ + netif_wake_queue(dev); +} + + +/** + * mc32_send_packet - queue a frame for transmit + * @skb: buffer to transmit + * @dev: 3c527 to send it out of + * + * Transmit a buffer. This normally means throwing the buffer onto + * the transmit queue as the queue is quite large. If the queue is + * full then we set tx_busy and return. Once the interrupt handler + * gets messages telling it to reclaim transmit queue entries, we will + * clear tx_busy and the kernel will start calling this again. + * + * We do not disable interrupts or acquire any locks; this can + * run concurrently with mc32_tx_ring(), and the function itself + * is serialised at a higher layer. However, similarly for the + * card itself, we must ensure that we update tx_ring_head only + * after we've established a valid packet on the tx ring (and + * before we let the card "see" it, to prevent it racing with the + * irq handler). + * + */ + +static netdev_tx_t mc32_send_packet(struct sk_buff *skb, + struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + u32 head = atomic_read(&lp->tx_ring_head); + + volatile struct skb_header *p, *np; + + netif_stop_queue(dev); + + if(atomic_read(&lp->tx_count)==0) { + return NETDEV_TX_BUSY; + } + + if (skb_padto(skb, ETH_ZLEN)) { + netif_wake_queue(dev); + return NETDEV_TX_OK; + } + + atomic_dec(&lp->tx_count); + + /* P is the last sending/sent buffer as a pointer */ + p=lp->tx_ring[head].p; + + head = next_tx(head); + + /* NP is the buffer we will be loading */ + np=lp->tx_ring[head].p; + + /* We will need this to flush the buffer out */ + lp->tx_ring[head].skb=skb; + + np->length = unlikely(skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; + np->data = isa_virt_to_bus(skb->data); + np->status = 0; + np->control = CONTROL_EOP | CONTROL_EOL; + wmb(); + + /* + * The new frame has been setup; we can now + * let the interrupt handler and card "see" it + */ + + atomic_set(&lp->tx_ring_head, head); + p->control &= ~CONTROL_EOL; + + netif_wake_queue(dev); + return NETDEV_TX_OK; +} + + +/** + * mc32_update_stats - pull off the on board statistics + * @dev: 3c527 to service + * + * + * Query and reset the on-card stats. There's the small possibility + * of a race here, which would result in an underestimation of + * actual errors. As such, we'd prefer to keep all our stats + * collection in software. As a rule, we do. However it can't be + * used for rx errors and collisions as, by default, the card discards + * bad rx packets. + * + * Setting the SAV BP in the rx filter command supposedly + * stops this behaviour. However, testing shows that it only seems to + * enable the collation of on-card rx statistics --- the driver + * never sees an RX descriptor with an error status set. + * + */ + +static void mc32_update_stats(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + volatile struct mc32_stats *st = lp->stats; + + u32 rx_errors=0; + + rx_errors+=dev->stats.rx_crc_errors +=st->rx_crc_errors; + st->rx_crc_errors=0; + rx_errors+=dev->stats.rx_fifo_errors +=st->rx_overrun_errors; + st->rx_overrun_errors=0; + rx_errors+=dev->stats.rx_frame_errors +=st->rx_alignment_errors; + st->rx_alignment_errors=0; + rx_errors+=dev->stats.rx_length_errors+=st->rx_tooshort_errors; + st->rx_tooshort_errors=0; + rx_errors+=dev->stats.rx_missed_errors+=st->rx_outofresource_errors; + st->rx_outofresource_errors=0; + dev->stats.rx_errors=rx_errors; + + /* Number of packets which saw one collision */ + dev->stats.collisions+=st->dataC[10]; + st->dataC[10]=0; + + /* Number of packets which saw 2--15 collisions */ + dev->stats.collisions+=st->dataC[11]; + st->dataC[11]=0; +} + + +/** + * mc32_rx_ring - process the receive ring + * @dev: 3c527 that needs its receive ring processing + * + * + * We have received one or more indications from the card that a + * receive has completed. The buffer ring thus contains dirty + * entries. We walk the ring by iterating over the circular rx_ring + * array, starting at the next dirty buffer (which happens to be the + * one we finished up at last time around). + * + * For each completed packet, we will either copy it and pass it up + * the stack or, if the packet is near MTU sized, we allocate + * another buffer and flip the old one up the stack. + * + * We must succeed in keeping a buffer on the ring. If necessary we + * will toss a received packet rather than lose a ring entry. Once + * the first uncompleted descriptor is found, we move the + * End-Of-List bit to include the buffers just processed. + * + */ + +static void mc32_rx_ring(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + volatile struct skb_header *p; + u16 rx_ring_tail; + u16 rx_old_tail; + int x=0; + + rx_old_tail = rx_ring_tail = lp->rx_ring_tail; + + do + { + p=lp->rx_ring[rx_ring_tail].p; + + if(!(p->status & (1<<7))) { /* Not COMPLETED */ + break; + } + if(p->status & (1<<6)) /* COMPLETED_OK */ + { + + u16 length=p->length; + struct sk_buff *skb; + struct sk_buff *newskb; + + /* Try to save time by avoiding a copy on big frames */ + + if ((length > RX_COPYBREAK) && + ((newskb=dev_alloc_skb(1532)) != NULL)) + { + skb=lp->rx_ring[rx_ring_tail].skb; + skb_put(skb, length); + + skb_reserve(newskb,18); + lp->rx_ring[rx_ring_tail].skb=newskb; + p->data=isa_virt_to_bus(newskb->data); + } + else + { + skb=dev_alloc_skb(length+2); + + if(skb==NULL) { + dev->stats.rx_dropped++; + goto dropped; + } + + skb_reserve(skb,2); + memcpy(skb_put(skb, length), + lp->rx_ring[rx_ring_tail].skb->data, length); + } + + skb->protocol=eth_type_trans(skb,dev); + dev->stats.rx_packets++; + dev->stats.rx_bytes += length; + netif_rx(skb); + } + + dropped: + p->length = 1532; + p->status = 0; + + rx_ring_tail=next_rx(rx_ring_tail); + } + while(x++<48); + + /* If there was actually a frame to be processed, place the EOL bit */ + /* at the descriptor prior to the one to be filled next */ + + if (rx_ring_tail != rx_old_tail) + { + lp->rx_ring[prev_rx(rx_ring_tail)].p->control |= CONTROL_EOL; + lp->rx_ring[prev_rx(rx_old_tail)].p->control &= ~CONTROL_EOL; + + lp->rx_ring_tail=rx_ring_tail; + } +} + + +/** + * mc32_tx_ring - process completed transmits + * @dev: 3c527 that needs its transmit ring processing + * + * + * This operates in a similar fashion to mc32_rx_ring. We iterate + * over the transmit ring. For each descriptor which has been + * processed by the card, we free its associated buffer and note + * any errors. This continues until the transmit ring is emptied + * or we reach a descriptor that hasn't yet been processed by the + * card. + * + */ + +static void mc32_tx_ring(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + volatile struct skb_header *np; + + /* + * We rely on head==tail to mean 'queue empty'. + * This is why lp->tx_count=TX_RING_LEN-1: in order to prevent + * tx_ring_head wrapping to tail and confusing a 'queue empty' + * condition with 'queue full' + */ + + while (lp->tx_ring_tail != atomic_read(&lp->tx_ring_head)) + { + u16 t; + + t=next_tx(lp->tx_ring_tail); + np=lp->tx_ring[t].p; + + if(!(np->status & (1<<7))) + { + /* Not COMPLETED */ + break; + } + dev->stats.tx_packets++; + if(!(np->status & (1<<6))) /* Not COMPLETED_OK */ + { + dev->stats.tx_errors++; + + switch(np->status&0x0F) + { + case 1: + dev->stats.tx_aborted_errors++; + break; /* Max collisions */ + case 2: + dev->stats.tx_fifo_errors++; + break; + case 3: + dev->stats.tx_carrier_errors++; + break; + case 4: + dev->stats.tx_window_errors++; + break; /* CTS Lost */ + case 5: + dev->stats.tx_aborted_errors++; + break; /* Transmit timeout */ + } + } + /* Packets are sent in order - this is + basically a FIFO queue of buffers matching + the card ring */ + dev->stats.tx_bytes+=lp->tx_ring[t].skb->len; + dev_kfree_skb_irq(lp->tx_ring[t].skb); + lp->tx_ring[t].skb=NULL; + atomic_inc(&lp->tx_count); + netif_wake_queue(dev); + + lp->tx_ring_tail=t; + } + +} + + +/** + * mc32_interrupt - handle an interrupt from a 3c527 + * @irq: Interrupt number + * @dev_id: 3c527 that requires servicing + * @regs: Registers (unused) + * + * + * An interrupt is raised whenever the 3c527 writes to the command + * register. This register contains the message it wishes to send us + * packed into a single byte field. We keep reading status entries + * until we have processed all the control items, but simply count + * transmit and receive reports. When all reports are in we empty the + * transceiver rings as appropriate. This saves the overhead of + * multiple command requests. + * + * Because MCA is level-triggered, we shouldn't miss indications. + * Therefore, we needn't ask the card to suspend interrupts within + * this handler. The card receives an implicit acknowledgment of the + * current interrupt when we read the command register. + * + */ + +static irqreturn_t mc32_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct mc32_local *lp; + int ioaddr, status, boguscount = 0; + int rx_event = 0; + int tx_event = 0; + + ioaddr = dev->base_addr; + lp = netdev_priv(dev); + + /* See whats cooking */ + + while((inb(ioaddr+HOST_STATUS)&HOST_STATUS_CWR) && boguscount++<2000) + { + status=inb(ioaddr+HOST_CMD); + + pr_debug("Status TX%d RX%d EX%d OV%d BC%d\n", + (status&7), (status>>3)&7, (status>>6)&1, + (status>>7)&1, boguscount); + + switch(status&7) + { + case 0: + break; + case 6: /* TX fail */ + case 2: /* TX ok */ + tx_event = 1; + break; + case 3: /* Halt */ + case 4: /* Abort */ + complete(&lp->xceiver_cmd); + break; + default: + pr_notice("%s: strange tx ack %d\n", dev->name, status&7); + } + status>>=3; + switch(status&7) + { + case 0: + break; + case 2: /* RX */ + rx_event=1; + break; + case 3: /* Halt */ + case 4: /* Abort */ + complete(&lp->xceiver_cmd); + break; + case 6: + /* Out of RX buffers stat */ + /* Must restart rx */ + dev->stats.rx_dropped++; + mc32_rx_ring(dev); + mc32_start_transceiver(dev); + break; + default: + pr_notice("%s: strange rx ack %d\n", + dev->name, status&7); + } + status>>=3; + if(status&1) + { + /* + * No thread is waiting: we need to tidy + * up ourself. + */ + + if (lp->cmd_nonblocking) { + up(&lp->cmd_mutex); + if (lp->mc_reload_wait) + mc32_reset_multicast_list(dev); + } + else complete(&lp->execution_cmd); + } + if(status&2) + { + /* + * We get interrupted once per + * counter that is about to overflow. + */ + + mc32_update_stats(dev); + } + } + + + /* + * Process the transmit and receive rings + */ + + if(tx_event) + mc32_tx_ring(dev); + + if(rx_event) + mc32_rx_ring(dev); + + return IRQ_HANDLED; +} + + +/** + * mc32_close - user configuring the 3c527 down + * @dev: 3c527 card to shut down + * + * The 3c527 is a bus mastering device. We must be careful how we + * shut it down. It may also be running shared interrupt so we have + * to be sure to silence it properly + * + * We indicate that the card is closing to the rest of the + * driver. Otherwise, it is possible that the card may run out + * of receive buffers and restart the transceiver while we're + * trying to close it. + * + * We abort any receive and transmits going on and then wait until + * any pending exec commands have completed in other code threads. + * In theory we can't get here while that is true, in practice I am + * paranoid + * + * We turn off the interrupt enable for the board to be sure it can't + * intefere with other devices. + */ + +static int mc32_close(struct net_device *dev) +{ + struct mc32_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + u8 regs; + u16 one=1; + + lp->xceiver_desired_state = HALTED; + netif_stop_queue(dev); + + /* + * Send the indications on command (handy debug check) + */ + + mc32_command(dev, 4, &one, 2); + + /* Shut down the transceiver */ + + mc32_halt_transceiver(dev); + + /* Ensure we issue no more commands beyond this point */ + + down(&lp->cmd_mutex); + + /* Ok the card is now stopping */ + + regs=inb(ioaddr+HOST_CTRL); + regs&=~HOST_CTRL_INTE; + outb(regs, ioaddr+HOST_CTRL); + + mc32_flush_rx_ring(dev); + mc32_flush_tx_ring(dev); + + mc32_update_stats(dev); + + return 0; +} + + +/** + * mc32_get_stats - hand back stats to network layer + * @dev: The 3c527 card to handle + * + * We've collected all the stats we can in software already. Now + * it's time to update those kept on-card and return the lot. + * + */ + +static struct net_device_stats *mc32_get_stats(struct net_device *dev) +{ + mc32_update_stats(dev); + return &dev->stats; +} + + +/** + * do_mc32_set_multicast_list - attempt to update multicasts + * @dev: 3c527 device to load the list on + * @retry: indicates this is not the first call. + * + * + * Actually set or clear the multicast filter for this adaptor. The + * locking issues are handled by this routine. We have to track + * state as it may take multiple calls to get the command sequence + * completed. We just keep trying to schedule the loads until we + * manage to process them all. + * + * num_addrs == -1 Promiscuous mode, receive all packets + * + * num_addrs == 0 Normal mode, clear multicast list + * + * num_addrs > 0 Multicast mode, receive normal and MC packets, + * and do best-effort filtering. + * + * See mc32_update_stats() regards setting the SAV BP bit. + * + */ + +static void do_mc32_set_multicast_list(struct net_device *dev, int retry) +{ + struct mc32_local *lp = netdev_priv(dev); + u16 filt = (1<<2); /* Save Bad Packets, for stats purposes */ + + if ((dev->flags&IFF_PROMISC) || + (dev->flags&IFF_ALLMULTI) || + netdev_mc_count(dev) > 10) + /* Enable promiscuous mode */ + filt |= 1; + else if (!netdev_mc_empty(dev)) + { + unsigned char block[62]; + unsigned char *bp; + struct netdev_hw_addr *ha; + + if(retry==0) + lp->mc_list_valid = 0; + if(!lp->mc_list_valid) + { + block[1]=0; + block[0]=netdev_mc_count(dev); + bp=block+2; + + netdev_for_each_mc_addr(ha, dev) { + memcpy(bp, ha->addr, 6); + bp+=6; + } + if(mc32_command_nowait(dev, 2, block, + 2+6*netdev_mc_count(dev))==-1) + { + lp->mc_reload_wait = 1; + return; + } + lp->mc_list_valid=1; + } + } + + if(mc32_command_nowait(dev, 0, &filt, 2)==-1) + { + lp->mc_reload_wait = 1; + } + else { + lp->mc_reload_wait = 0; + } +} + + +/** + * mc32_set_multicast_list - queue multicast list update + * @dev: The 3c527 to use + * + * Commence loading the multicast list. This is called when the kernel + * changes the lists. It will override any pending list we are trying to + * load. + */ + +static void mc32_set_multicast_list(struct net_device *dev) +{ + do_mc32_set_multicast_list(dev,0); +} + + +/** + * mc32_reset_multicast_list - reset multicast list + * @dev: The 3c527 to use + * + * Attempt the next step in loading the multicast lists. If this attempt + * fails to complete then it will be scheduled and this function called + * again later from elsewhere. + */ + +static void mc32_reset_multicast_list(struct net_device *dev) +{ + do_mc32_set_multicast_list(dev,1); +} + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "MCA 0x%lx", dev->base_addr); +} + +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return mc32_debug; +} + +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + mc32_debug = level; +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; + +#ifdef MODULE + +static struct net_device *this_device; + +/** + * init_module - entry point + * + * Probe and locate a 3c527 card. This really should probe and locate + * all the 3c527 cards in the machine not just one of them. Yes you can + * insmod multiple modules for now but it's a hack. + */ + +int __init init_module(void) +{ + this_device = mc32_probe(-1); + if (IS_ERR(this_device)) + return PTR_ERR(this_device); + return 0; +} + +/** + * cleanup_module - free resources for an unload + * + * Unloading time. We release the MCA bus resources and the interrupt + * at which point everything is ready to unload. The card must be stopped + * at this point or we would not have been called. When we unload we + * leave the card stopped but not totally shut down. When the card is + * initialized it must be rebooted or the rings reloaded before any + * transmit operations are allowed to start scribbling into memory. + */ + +void __exit cleanup_module(void) +{ + unregister_netdev(this_device); + cleanup_card(this_device); + free_netdev(this_device); +} + +#endif /* MODULE */ diff --git a/drivers/net/ethernet/i825xx/3c527.h b/drivers/net/ethernet/i825xx/3c527.h new file mode 100644 index 000000000000..d693b8d15cde --- /dev/null +++ b/drivers/net/ethernet/i825xx/3c527.h @@ -0,0 +1,81 @@ +/* + * 3COM "EtherLink MC/32" Descriptions + */ + +/* + * Registers + */ + +#define HOST_CMD 0 +#define HOST_CMD_START_RX (1<<3) +#define HOST_CMD_SUSPND_RX (3<<3) +#define HOST_CMD_RESTRT_RX (5<<3) + +#define HOST_CMD_SUSPND_TX 3 +#define HOST_CMD_RESTRT_TX 5 + + +#define HOST_STATUS 2 +#define HOST_STATUS_CRR (1<<6) +#define HOST_STATUS_CWR (1<<5) + + +#define HOST_CTRL 6 +#define HOST_CTRL_ATTN (1<<7) +#define HOST_CTRL_RESET (1<<6) +#define HOST_CTRL_INTE (1<<2) + +#define HOST_RAMPAGE 8 + +#define HALTED 0 +#define RUNNING 1 + +struct mc32_mailbox +{ + u16 mbox; + u16 data[1]; +} __packed; + +struct skb_header +{ + u8 status; + u8 control; + u16 next; /* Do not change! */ + u16 length; + u32 data; +} __packed; + +struct mc32_stats +{ + /* RX Errors */ + u32 rx_crc_errors; + u32 rx_alignment_errors; + u32 rx_overrun_errors; + u32 rx_tooshort_errors; + u32 rx_toolong_errors; + u32 rx_outofresource_errors; + + u32 rx_discarded; /* via card pattern match filter */ + + /* TX Errors */ + u32 tx_max_collisions; + u32 tx_carrier_errors; + u32 tx_underrun_errors; + u32 tx_cts_errors; + u32 tx_timeout_errors; + + /* various cruft */ + u32 dataA[6]; + u16 dataB[5]; + u32 dataC[14]; +} __packed; + +#define STATUS_MASK 0x0F +#define COMPLETED (1<<7) +#define COMPLETED_OK (1<<6) +#define BUFFER_BUSY (1<<5) + +#define CONTROL_EOP (1<<7) /* End Of Packet */ +#define CONTROL_EOL (1<<6) /* End of List */ + +#define MCA_MC32_ID 0x0041 /* Our MCA ident */ diff --git a/drivers/net/ethernet/i825xx/82596.c b/drivers/net/ethernet/i825xx/82596.c new file mode 100644 index 000000000000..be1f1970c842 --- /dev/null +++ b/drivers/net/ethernet/i825xx/82596.c @@ -0,0 +1,1632 @@ +/* 82596.c: A generic 82596 ethernet driver for linux. */ +/* + Based on Apricot.c + Written 1994 by Mark Evans. + This driver is for the Apricot 82596 bus-master interface + + Modularised 12/94 Mark Evans + + + Modified to support the 82596 ethernet chips on 680x0 VME boards. + by Richard Hirst <richard@sleepie.demon.co.uk> + Renamed to be 82596.c + + 980825: Changed to receive directly in to sk_buffs which are + allocated at open() time. Eliminates copy on incoming frames + (small ones are still copied). Shared data now held in a + non-cached page, so we can run on 68060 in copyback mode. + + TBD: + * look at deferring rx frames rather than discarding (as per tulip) + * handle tx ring full as per tulip + * performance test to tune rx_copybreak + + Most of my modifications relate to the braindead big-endian + implementation by Intel. When the i596 is operating in + 'big-endian' mode, it thinks a 32 bit value of 0x12345678 + should be stored as 0x56781234. This is a real pain, when + you have linked lists which are shared by the 680x0 and the + i596. + + Driver skeleton + Written 1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, + National Security Agency. This software may only be used and distributed + according to the terms of the GNU General Public License as modified by SRC, + incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 + + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/gfp.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/pgtable.h> +#include <asm/cacheflush.h> + +static char version[] __initdata = + "82596.c $Revision: 1.5 $\n"; + +#define DRV_NAME "82596" + +/* DEBUG flags + */ + +#define DEB_INIT 0x0001 +#define DEB_PROBE 0x0002 +#define DEB_SERIOUS 0x0004 +#define DEB_ERRORS 0x0008 +#define DEB_MULTI 0x0010 +#define DEB_TDR 0x0020 +#define DEB_OPEN 0x0040 +#define DEB_RESET 0x0080 +#define DEB_ADDCMD 0x0100 +#define DEB_STATUS 0x0200 +#define DEB_STARTTX 0x0400 +#define DEB_RXADDR 0x0800 +#define DEB_TXADDR 0x1000 +#define DEB_RXFRAME 0x2000 +#define DEB_INTS 0x4000 +#define DEB_STRUCT 0x8000 +#define DEB_ANY 0xffff + + +#define DEB(x,y) if (i596_debug & (x)) y + + +#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_MVME16x_NET_MODULE) +#define ENABLE_MVME16x_NET +#endif +#if defined(CONFIG_BVME6000_NET) || defined(CONFIG_BVME6000_NET_MODULE) +#define ENABLE_BVME6000_NET +#endif +#if defined(CONFIG_APRICOT) || defined(CONFIG_APRICOT_MODULE) +#define ENABLE_APRICOT +#endif + +#ifdef ENABLE_MVME16x_NET +#include <asm/mvme16xhw.h> +#endif +#ifdef ENABLE_BVME6000_NET +#include <asm/bvme6000hw.h> +#endif + +/* + * Define various macros for Channel Attention, word swapping etc., dependent + * on architecture. MVME and BVME are 680x0 based, otherwise it is Intel. + */ + +#ifdef __mc68000__ +#define WSWAPrfd(x) ((struct i596_rfd *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPrbd(x) ((struct i596_rbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPiscp(x) ((struct i596_iscp *)(((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPscb(x) ((struct i596_scb *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPcmd(x) ((struct i596_cmd *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPtbd(x) ((struct i596_tbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPchar(x) ((char *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define ISCP_BUSY 0x00010000 +#define MACH_IS_APRICOT 0 +#else +#define WSWAPrfd(x) ((struct i596_rfd *)((long)x)) +#define WSWAPrbd(x) ((struct i596_rbd *)((long)x)) +#define WSWAPiscp(x) ((struct i596_iscp *)((long)x)) +#define WSWAPscb(x) ((struct i596_scb *)((long)x)) +#define WSWAPcmd(x) ((struct i596_cmd *)((long)x)) +#define WSWAPtbd(x) ((struct i596_tbd *)((long)x)) +#define WSWAPchar(x) ((char *)((long)x)) +#define ISCP_BUSY 0x0001 +#define MACH_IS_APRICOT 1 +#endif + +/* + * The MPU_PORT command allows direct access to the 82596. With PORT access + * the following commands are available (p5-18). The 32-bit port command + * must be word-swapped with the most significant word written first. + * This only applies to VME boards. + */ +#define PORT_RESET 0x00 /* reset 82596 */ +#define PORT_SELFTEST 0x01 /* selftest */ +#define PORT_ALTSCP 0x02 /* alternate SCB address */ +#define PORT_ALTDUMP 0x03 /* Alternate DUMP address */ + +static int i596_debug = (DEB_SERIOUS|DEB_PROBE); + +MODULE_AUTHOR("Richard Hirst"); +MODULE_DESCRIPTION("i82596 driver"); +MODULE_LICENSE("GPL"); + +module_param(i596_debug, int, 0); +MODULE_PARM_DESC(i596_debug, "i82596 debug mask"); + + +/* Copy frames shorter than rx_copybreak, otherwise pass on up in + * a full sized sk_buff. Value of 100 stolen from tulip.c (!alpha). + */ +static int rx_copybreak = 100; + +#define PKT_BUF_SZ 1536 +#define MAX_MC_CNT 64 + +#define I596_TOTAL_SIZE 17 + +#define I596_NULL ((void *)0xffffffff) + +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +#define CMD_FLEX 0x0008 /* Enable flexible memory model */ + +enum commands { + CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7 +}; + +#define STAT_C 0x8000 /* Set to 0 after execution */ +#define STAT_B 0x4000 /* Command being executed */ +#define STAT_OK 0x2000 /* Command executed ok */ +#define STAT_A 0x1000 /* Command aborted */ + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define CUC_ABORT 0x0400 +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 +#define RX_ABORT 0x0040 + +#define TX_TIMEOUT (HZ/20) + + +struct i596_reg { + unsigned short porthi; + unsigned short portlo; + unsigned long ca; +}; + +#define EOF 0x8000 +#define SIZE_MASK 0x3fff + +struct i596_tbd { + unsigned short size; + unsigned short pad; + struct i596_tbd *next; + char *data; +}; + +/* The command structure has two 'next' pointers; v_next is the address of + * the next command as seen by the CPU, b_next is the address of the next + * command as seen by the 82596. The b_next pointer, as used by the 82596 + * always references the status field of the next command, rather than the + * v_next field, because the 82596 is unaware of v_next. It may seem more + * logical to put v_next at the end of the structure, but we cannot do that + * because the 82596 expects other fields to be there, depending on command + * type. + */ + +struct i596_cmd { + struct i596_cmd *v_next; /* Address from CPUs viewpoint */ + unsigned short status; + unsigned short command; + struct i596_cmd *b_next; /* Address from i596 viewpoint */ +}; + +struct tx_cmd { + struct i596_cmd cmd; + struct i596_tbd *tbd; + unsigned short size; + unsigned short pad; + struct sk_buff *skb; /* So we can free it after tx */ +}; + +struct tdr_cmd { + struct i596_cmd cmd; + unsigned short status; + unsigned short pad; +}; + +struct mc_cmd { + struct i596_cmd cmd; + short mc_cnt; + char mc_addrs[MAX_MC_CNT*6]; +}; + +struct sa_cmd { + struct i596_cmd cmd; + char eth_addr[8]; +}; + +struct cf_cmd { + struct i596_cmd cmd; + char i596_config[16]; +}; + +struct i596_rfd { + unsigned short stat; + unsigned short cmd; + struct i596_rfd *b_next; /* Address from i596 viewpoint */ + struct i596_rbd *rbd; + unsigned short count; + unsigned short size; + struct i596_rfd *v_next; /* Address from CPUs viewpoint */ + struct i596_rfd *v_prev; +}; + +struct i596_rbd { + unsigned short count; + unsigned short zero1; + struct i596_rbd *b_next; + unsigned char *b_data; /* Address from i596 viewpoint */ + unsigned short size; + unsigned short zero2; + struct sk_buff *skb; + struct i596_rbd *v_next; + struct i596_rbd *b_addr; /* This rbd addr from i596 view */ + unsigned char *v_data; /* Address from CPUs viewpoint */ +}; + +#define TX_RING_SIZE 64 +#define RX_RING_SIZE 16 + +struct i596_scb { + unsigned short status; + unsigned short command; + struct i596_cmd *cmd; + struct i596_rfd *rfd; + unsigned long crc_err; + unsigned long align_err; + unsigned long resource_err; + unsigned long over_err; + unsigned long rcvdt_err; + unsigned long short_err; + unsigned short t_on; + unsigned short t_off; +}; + +struct i596_iscp { + unsigned long stat; + struct i596_scb *scb; +}; + +struct i596_scp { + unsigned long sysbus; + unsigned long pad; + struct i596_iscp *iscp; +}; + +struct i596_private { + volatile struct i596_scp scp; + volatile struct i596_iscp iscp; + volatile struct i596_scb scb; + struct sa_cmd sa_cmd; + struct cf_cmd cf_cmd; + struct tdr_cmd tdr_cmd; + struct mc_cmd mc_cmd; + unsigned long stat; + int last_restart __attribute__((aligned(4))); + struct i596_rfd *rfd_head; + struct i596_rbd *rbd_head; + struct i596_cmd *cmd_tail; + struct i596_cmd *cmd_head; + int cmd_backlog; + unsigned long last_cmd; + struct i596_rfd rfds[RX_RING_SIZE]; + struct i596_rbd rbds[RX_RING_SIZE]; + struct tx_cmd tx_cmds[TX_RING_SIZE]; + struct i596_tbd tbds[TX_RING_SIZE]; + int next_tx_cmd; + spinlock_t lock; +}; + +static char init_setup[] = +{ + 0x8E, /* length, prefetch on */ + 0xC8, /* fifo to 8, monitor off */ +#ifdef CONFIG_VME + 0xc0, /* don't save bad frames */ +#else + 0x80, /* don't save bad frames */ +#endif + 0x2E, /* No source address insertion, 8 byte preamble */ + 0x00, /* priority and backoff defaults */ + 0x60, /* interframe spacing */ + 0x00, /* slot time LSB */ + 0xf2, /* slot time and retries */ + 0x00, /* promiscuous mode */ + 0x00, /* collision detect */ + 0x40, /* minimum frame length */ + 0xff, + 0x00, + 0x7f /* *multi IA */ }; + +static int i596_open(struct net_device *dev); +static netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t i596_interrupt(int irq, void *dev_id); +static int i596_close(struct net_device *dev); +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd); +static void i596_tx_timeout (struct net_device *dev); +static void print_eth(unsigned char *buf, char *str); +static void set_multicast_list(struct net_device *dev); + +static int rx_ring_size = RX_RING_SIZE; +static int ticks_limit = 25; +static int max_cmd_backlog = TX_RING_SIZE-1; + + +static inline void CA(struct net_device *dev) +{ +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + ((struct i596_reg *) dev->base_addr)->ca = 1; + } +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + volatile u32 i; + + i = *(volatile u32 *) (dev->base_addr); + } +#endif +#ifdef ENABLE_APRICOT + if (MACH_IS_APRICOT) { + outw(0, (short) (dev->base_addr) + 4); + } +#endif +} + + +static inline void MPU_PORT(struct net_device *dev, int c, volatile void *x) +{ +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + struct i596_reg *p = (struct i596_reg *) (dev->base_addr); + p->porthi = ((c) | (u32) (x)) & 0xffff; + p->portlo = ((c) | (u32) (x)) >> 16; + } +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + u32 v = (u32) (c) | (u32) (x); + v = ((u32) (v) << 16) | ((u32) (v) >> 16); + *(volatile u32 *) dev->base_addr = v; + udelay(1); + *(volatile u32 *) dev->base_addr = v; + } +#endif +} + + +static inline int wait_istat(struct net_device *dev, struct i596_private *lp, int delcnt, char *str) +{ + while (--delcnt && lp->iscp.stat) + udelay(10); + if (!delcnt) { + printk(KERN_ERR "%s: %s, status %4.4x, cmd %4.4x.\n", + dev->name, str, lp->scb.status, lp->scb.command); + return -1; + } + else + return 0; +} + + +static inline int wait_cmd(struct net_device *dev, struct i596_private *lp, int delcnt, char *str) +{ + while (--delcnt && lp->scb.command) + udelay(10); + if (!delcnt) { + printk(KERN_ERR "%s: %s, status %4.4x, cmd %4.4x.\n", + dev->name, str, lp->scb.status, lp->scb.command); + return -1; + } + else + return 0; +} + + +static inline int wait_cfg(struct net_device *dev, struct i596_cmd *cmd, int delcnt, char *str) +{ + volatile struct i596_cmd *c = cmd; + + while (--delcnt && c->command) + udelay(10); + if (!delcnt) { + printk(KERN_ERR "%s: %s.\n", dev->name, str); + return -1; + } + else + return 0; +} + + +static void i596_display_data(struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + struct i596_cmd *cmd; + struct i596_rfd *rfd; + struct i596_rbd *rbd; + + printk(KERN_ERR "lp and scp at %p, .sysbus = %08lx, .iscp = %p\n", + &lp->scp, lp->scp.sysbus, lp->scp.iscp); + printk(KERN_ERR "iscp at %p, iscp.stat = %08lx, .scb = %p\n", + &lp->iscp, lp->iscp.stat, lp->iscp.scb); + printk(KERN_ERR "scb at %p, scb.status = %04x, .command = %04x," + " .cmd = %p, .rfd = %p\n", + &lp->scb, lp->scb.status, lp->scb.command, + lp->scb.cmd, lp->scb.rfd); + printk(KERN_ERR " errors: crc %lx, align %lx, resource %lx," + " over %lx, rcvdt %lx, short %lx\n", + lp->scb.crc_err, lp->scb.align_err, lp->scb.resource_err, + lp->scb.over_err, lp->scb.rcvdt_err, lp->scb.short_err); + cmd = lp->cmd_head; + while (cmd != I596_NULL) { + printk(KERN_ERR "cmd at %p, .status = %04x, .command = %04x, .b_next = %p\n", + cmd, cmd->status, cmd->command, cmd->b_next); + cmd = cmd->v_next; + } + rfd = lp->rfd_head; + printk(KERN_ERR "rfd_head = %p\n", rfd); + do { + printk(KERN_ERR " %p .stat %04x, .cmd %04x, b_next %p, rbd %p," + " count %04x\n", + rfd, rfd->stat, rfd->cmd, rfd->b_next, rfd->rbd, + rfd->count); + rfd = rfd->v_next; + } while (rfd != lp->rfd_head); + rbd = lp->rbd_head; + printk(KERN_ERR "rbd_head = %p\n", rbd); + do { + printk(KERN_ERR " %p .count %04x, b_next %p, b_data %p, size %04x\n", + rbd, rbd->count, rbd->b_next, rbd->b_data, rbd->size); + rbd = rbd->v_next; + } while (rbd != lp->rbd_head); +} + + +#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET) +static irqreturn_t i596_error(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000; + + pcc2[0x28] = 1; + pcc2[0x2b] = 0x1d; + } +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG; + + *ethirq = 1; + *ethirq = 3; + } +#endif + printk(KERN_ERR "%s: Error interrupt\n", dev->name); + i596_display_data(dev); + return IRQ_HANDLED; +} +#endif + +static inline void remove_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + struct i596_rbd *rbd; + int i; + + for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) { + if (rbd->skb == NULL) + break; + dev_kfree_skb(rbd->skb); + rbd->skb = NULL; + } +} + +static inline int init_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + int i; + struct i596_rfd *rfd; + struct i596_rbd *rbd; + + /* First build the Receive Buffer Descriptor List */ + + for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) { + struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); + + if (skb == NULL) { + remove_rx_bufs(dev); + return -ENOMEM; + } + + skb->dev = dev; + rbd->v_next = rbd+1; + rbd->b_next = WSWAPrbd(virt_to_bus(rbd+1)); + rbd->b_addr = WSWAPrbd(virt_to_bus(rbd)); + rbd->skb = skb; + rbd->v_data = skb->data; + rbd->b_data = WSWAPchar(virt_to_bus(skb->data)); + rbd->size = PKT_BUF_SZ; +#ifdef __mc68000__ + cache_clear(virt_to_phys(skb->data), PKT_BUF_SZ); +#endif + } + lp->rbd_head = lp->rbds; + rbd = lp->rbds + rx_ring_size - 1; + rbd->v_next = lp->rbds; + rbd->b_next = WSWAPrbd(virt_to_bus(lp->rbds)); + + /* Now build the Receive Frame Descriptor List */ + + for (i = 0, rfd = lp->rfds; i < rx_ring_size; i++, rfd++) { + rfd->rbd = I596_NULL; + rfd->v_next = rfd+1; + rfd->v_prev = rfd-1; + rfd->b_next = WSWAPrfd(virt_to_bus(rfd+1)); + rfd->cmd = CMD_FLEX; + } + lp->rfd_head = lp->rfds; + lp->scb.rfd = WSWAPrfd(virt_to_bus(lp->rfds)); + rfd = lp->rfds; + rfd->rbd = lp->rbd_head; + rfd->v_prev = lp->rfds + rx_ring_size - 1; + rfd = lp->rfds + rx_ring_size - 1; + rfd->v_next = lp->rfds; + rfd->b_next = WSWAPrfd(virt_to_bus(lp->rfds)); + rfd->cmd = CMD_EOL|CMD_FLEX; + + return 0; +} + + +static void rebuild_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + int i; + + /* Ensure rx frame/buffer descriptors are tidy */ + + for (i = 0; i < rx_ring_size; i++) { + lp->rfds[i].rbd = I596_NULL; + lp->rfds[i].cmd = CMD_FLEX; + } + lp->rfds[rx_ring_size-1].cmd = CMD_EOL|CMD_FLEX; + lp->rfd_head = lp->rfds; + lp->scb.rfd = WSWAPrfd(virt_to_bus(lp->rfds)); + lp->rbd_head = lp->rbds; + lp->rfds[0].rbd = WSWAPrbd(virt_to_bus(lp->rbds)); +} + + +static int init_i596_mem(struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; +#if !defined(ENABLE_MVME16x_NET) && !defined(ENABLE_BVME6000_NET) || defined(ENABLE_APRICOT) + short ioaddr = dev->base_addr; +#endif + unsigned long flags; + + MPU_PORT(dev, PORT_RESET, NULL); + + udelay(100); /* Wait 100us - seems to help */ + +#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET) +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000; + + /* Disable all ints for now */ + pcc2[0x28] = 1; + pcc2[0x2a] = 0x48; + /* Following disables snooping. Snooping is not required + * as we make appropriate use of non-cached pages for + * shared data, and cache_push/cache_clear. + */ + pcc2[0x2b] = 0x08; + } +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG; + + *ethirq = 1; + } +#endif + + /* change the scp address */ + + MPU_PORT(dev, PORT_ALTSCP, (void *)virt_to_bus((void *)&lp->scp)); + +#elif defined(ENABLE_APRICOT) + + { + u32 scp = virt_to_bus(&lp->scp); + + /* change the scp address */ + outw(0, ioaddr); + outw(0, ioaddr); + outb(4, ioaddr + 0xf); + outw(scp | 2, ioaddr); + outw(scp >> 16, ioaddr); + } +#endif + + lp->last_cmd = jiffies; + +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) + lp->scp.sysbus = 0x00000054; +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) + lp->scp.sysbus = 0x0000004c; +#endif +#ifdef ENABLE_APRICOT + if (MACH_IS_APRICOT) + lp->scp.sysbus = 0x00440000; +#endif + + lp->scp.iscp = WSWAPiscp(virt_to_bus((void *)&lp->iscp)); + lp->iscp.scb = WSWAPscb(virt_to_bus((void *)&lp->scb)); + lp->iscp.stat = ISCP_BUSY; + lp->cmd_backlog = 0; + + lp->cmd_head = lp->scb.cmd = I596_NULL; + +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + lp->scb.t_on = 7 * 25; + lp->scb.t_off = 1 * 25; + } +#endif + + DEB(DEB_INIT,printk(KERN_DEBUG "%s: starting i82596.\n", dev->name)); + +#if defined(ENABLE_APRICOT) + (void) inb(ioaddr + 0x10); + outb(4, ioaddr + 0xf); +#endif + CA(dev); + + if (wait_istat(dev,lp,1000,"initialization timed out")) + goto failed; + DEB(DEB_INIT,printk(KERN_DEBUG "%s: i82596 initialization successful\n", dev->name)); + + /* Ensure rx frame/buffer descriptors are tidy */ + rebuild_rx_bufs(dev); + lp->scb.command = 0; + +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000; + + /* Enable ints, etc. now */ + pcc2[0x2a] = 0x55; /* Edge sensitive */ + pcc2[0x2b] = 0x15; + } +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG; + + *ethirq = 3; + } +#endif + + + DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdConfigure\n", dev->name)); + memcpy(lp->cf_cmd.i596_config, init_setup, 14); + lp->cf_cmd.cmd.command = CmdConfigure; + i596_add_cmd(dev, &lp->cf_cmd.cmd); + + DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name)); + memcpy(lp->sa_cmd.eth_addr, dev->dev_addr, 6); + lp->sa_cmd.cmd.command = CmdSASetup; + i596_add_cmd(dev, &lp->sa_cmd.cmd); + + DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdTDR\n", dev->name)); + lp->tdr_cmd.cmd.command = CmdTDR; + i596_add_cmd(dev, &lp->tdr_cmd.cmd); + + spin_lock_irqsave (&lp->lock, flags); + + if (wait_cmd(dev,lp,1000,"timed out waiting to issue RX_START")) { + spin_unlock_irqrestore (&lp->lock, flags); + goto failed; + } + DEB(DEB_INIT,printk(KERN_DEBUG "%s: Issuing RX_START\n", dev->name)); + lp->scb.command = RX_START; + CA(dev); + + spin_unlock_irqrestore (&lp->lock, flags); + + if (wait_cmd(dev,lp,1000,"RX_START not processed")) + goto failed; + DEB(DEB_INIT,printk(KERN_DEBUG "%s: Receive unit started OK\n", dev->name)); + return 0; + +failed: + printk(KERN_CRIT "%s: Failed to initialise 82596\n", dev->name); + MPU_PORT(dev, PORT_RESET, NULL); + return -1; +} + +static inline int i596_rx(struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + struct i596_rfd *rfd; + struct i596_rbd *rbd; + int frames = 0; + + DEB(DEB_RXFRAME,printk(KERN_DEBUG "i596_rx(), rfd_head %p, rbd_head %p\n", + lp->rfd_head, lp->rbd_head)); + + rfd = lp->rfd_head; /* Ref next frame to check */ + + while ((rfd->stat) & STAT_C) { /* Loop while complete frames */ + if (rfd->rbd == I596_NULL) + rbd = I596_NULL; + else if (rfd->rbd == lp->rbd_head->b_addr) + rbd = lp->rbd_head; + else { + printk(KERN_CRIT "%s: rbd chain broken!\n", dev->name); + /* XXX Now what? */ + rbd = I596_NULL; + } + DEB(DEB_RXFRAME, printk(KERN_DEBUG " rfd %p, rfd.rbd %p, rfd.stat %04x\n", + rfd, rfd->rbd, rfd->stat)); + + if (rbd != I596_NULL && ((rfd->stat) & STAT_OK)) { + /* a good frame */ + int pkt_len = rbd->count & 0x3fff; + struct sk_buff *skb = rbd->skb; + int rx_in_place = 0; + + DEB(DEB_RXADDR,print_eth(rbd->v_data, "received")); + frames++; + + /* Check if the packet is long enough to just accept + * without copying to a properly sized skbuff. + */ + + if (pkt_len > rx_copybreak) { + struct sk_buff *newskb; + + /* Get fresh skbuff to replace filled one. */ + newskb = dev_alloc_skb(PKT_BUF_SZ); + if (newskb == NULL) { + skb = NULL; /* drop pkt */ + goto memory_squeeze; + } + /* Pass up the skb already on the Rx ring. */ + skb_put(skb, pkt_len); + rx_in_place = 1; + rbd->skb = newskb; + newskb->dev = dev; + rbd->v_data = newskb->data; + rbd->b_data = WSWAPchar(virt_to_bus(newskb->data)); +#ifdef __mc68000__ + cache_clear(virt_to_phys(newskb->data), PKT_BUF_SZ); +#endif + } + else + skb = dev_alloc_skb(pkt_len + 2); +memory_squeeze: + if (skb == NULL) { + /* XXX tulip.c can defer packets here!! */ + printk(KERN_WARNING "%s: i596_rx Memory squeeze, dropping packet.\n", dev->name); + dev->stats.rx_dropped++; + } + else { + if (!rx_in_place) { + /* 16 byte align the data fields */ + skb_reserve(skb, 2); + memcpy(skb_put(skb,pkt_len), rbd->v_data, pkt_len); + } + skb->protocol=eth_type_trans(skb,dev); + skb->len = pkt_len; +#ifdef __mc68000__ + cache_clear(virt_to_phys(rbd->skb->data), + pkt_len); +#endif + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes+=pkt_len; + } + } + else { + DEB(DEB_ERRORS, printk(KERN_DEBUG "%s: Error, rfd.stat = 0x%04x\n", + dev->name, rfd->stat)); + dev->stats.rx_errors++; + if ((rfd->stat) & 0x0001) + dev->stats.collisions++; + if ((rfd->stat) & 0x0080) + dev->stats.rx_length_errors++; + if ((rfd->stat) & 0x0100) + dev->stats.rx_over_errors++; + if ((rfd->stat) & 0x0200) + dev->stats.rx_fifo_errors++; + if ((rfd->stat) & 0x0400) + dev->stats.rx_frame_errors++; + if ((rfd->stat) & 0x0800) + dev->stats.rx_crc_errors++; + if ((rfd->stat) & 0x1000) + dev->stats.rx_length_errors++; + } + + /* Clear the buffer descriptor count and EOF + F flags */ + + if (rbd != I596_NULL && (rbd->count & 0x4000)) { + rbd->count = 0; + lp->rbd_head = rbd->v_next; + } + + /* Tidy the frame descriptor, marking it as end of list */ + + rfd->rbd = I596_NULL; + rfd->stat = 0; + rfd->cmd = CMD_EOL|CMD_FLEX; + rfd->count = 0; + + /* Remove end-of-list from old end descriptor */ + + rfd->v_prev->cmd = CMD_FLEX; + + /* Update record of next frame descriptor to process */ + + lp->scb.rfd = rfd->b_next; + lp->rfd_head = rfd->v_next; + rfd = lp->rfd_head; + } + + DEB(DEB_RXFRAME,printk(KERN_DEBUG "frames %d\n", frames)); + + return 0; +} + + +static void i596_cleanup_cmd(struct net_device *dev, struct i596_private *lp) +{ + struct i596_cmd *ptr; + + while (lp->cmd_head != I596_NULL) { + ptr = lp->cmd_head; + lp->cmd_head = ptr->v_next; + lp->cmd_backlog--; + + switch ((ptr->command) & 0x7) { + case CmdTx: + { + struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; + struct sk_buff *skb = tx_cmd->skb; + + dev_kfree_skb(skb); + + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; + + ptr->v_next = ptr->b_next = I596_NULL; + tx_cmd->cmd.command = 0; /* Mark as free */ + break; + } + default: + ptr->v_next = ptr->b_next = I596_NULL; + } + } + + wait_cmd(dev,lp,100,"i596_cleanup_cmd timed out"); + lp->scb.cmd = I596_NULL; +} + +static void i596_reset(struct net_device *dev, struct i596_private *lp, + int ioaddr) +{ + unsigned long flags; + + DEB(DEB_RESET,printk(KERN_DEBUG "i596_reset\n")); + + spin_lock_irqsave (&lp->lock, flags); + + wait_cmd(dev,lp,100,"i596_reset timed out"); + + netif_stop_queue(dev); + + lp->scb.command = CUC_ABORT | RX_ABORT; + CA(dev); + + /* wait for shutdown */ + wait_cmd(dev,lp,1000,"i596_reset 2 timed out"); + spin_unlock_irqrestore (&lp->lock, flags); + + i596_cleanup_cmd(dev,lp); + i596_rx(dev); + + netif_start_queue(dev); + init_i596_mem(dev); +} + +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) +{ + struct i596_private *lp = dev->ml_priv; + int ioaddr = dev->base_addr; + unsigned long flags; + + DEB(DEB_ADDCMD,printk(KERN_DEBUG "i596_add_cmd\n")); + + cmd->status = 0; + cmd->command |= (CMD_EOL | CMD_INTR); + cmd->v_next = cmd->b_next = I596_NULL; + + spin_lock_irqsave (&lp->lock, flags); + + if (lp->cmd_head != I596_NULL) { + lp->cmd_tail->v_next = cmd; + lp->cmd_tail->b_next = WSWAPcmd(virt_to_bus(&cmd->status)); + } else { + lp->cmd_head = cmd; + wait_cmd(dev,lp,100,"i596_add_cmd timed out"); + lp->scb.cmd = WSWAPcmd(virt_to_bus(&cmd->status)); + lp->scb.command = CUC_START; + CA(dev); + } + lp->cmd_tail = cmd; + lp->cmd_backlog++; + + spin_unlock_irqrestore (&lp->lock, flags); + + if (lp->cmd_backlog > max_cmd_backlog) { + unsigned long tickssofar = jiffies - lp->last_cmd; + + if (tickssofar < ticks_limit) + return; + + printk(KERN_NOTICE "%s: command unit timed out, status resetting.\n", dev->name); + + i596_reset(dev, lp, ioaddr); + } +} + +static int i596_open(struct net_device *dev) +{ + int res = 0; + + DEB(DEB_OPEN,printk(KERN_DEBUG "%s: i596_open() irq %d.\n", dev->name, dev->irq)); + + if (request_irq(dev->irq, i596_interrupt, 0, "i82596", dev)) { + printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq); + return -EAGAIN; + } +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + if (request_irq(0x56, i596_error, 0, "i82596_error", dev)) { + res = -EAGAIN; + goto err_irq_dev; + } + } +#endif + res = init_rx_bufs(dev); + if (res) + goto err_irq_56; + + netif_start_queue(dev); + + if (init_i596_mem(dev)) { + res = -EAGAIN; + goto err_queue; + } + + return 0; + +err_queue: + netif_stop_queue(dev); + remove_rx_bufs(dev); +err_irq_56: +#ifdef ENABLE_MVME16x_NET + free_irq(0x56, dev); +err_irq_dev: +#endif + free_irq(dev->irq, dev); + + return res; +} + +static void i596_tx_timeout (struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + int ioaddr = dev->base_addr; + + /* Transmitter timeout, serious problems. */ + DEB(DEB_ERRORS,printk(KERN_ERR "%s: transmit timed out, status resetting.\n", + dev->name)); + + dev->stats.tx_errors++; + + /* Try to restart the adaptor */ + if (lp->last_restart == dev->stats.tx_packets) { + DEB(DEB_ERRORS,printk(KERN_ERR "Resetting board.\n")); + /* Shutdown and restart */ + i596_reset (dev, lp, ioaddr); + } else { + /* Issue a channel attention signal */ + DEB(DEB_ERRORS,printk(KERN_ERR "Kicking board.\n")); + lp->scb.command = CUC_START | RX_START; + CA (dev); + lp->last_restart = dev->stats.tx_packets; + } + + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue (dev); +} + +static netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + struct tx_cmd *tx_cmd; + struct i596_tbd *tbd; + short length = skb->len; + + DEB(DEB_STARTTX,printk(KERN_DEBUG "%s: i596_start_xmit(%x,%p) called\n", + dev->name, skb->len, skb->data)); + + if (skb->len < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + length = ETH_ZLEN; + } + netif_stop_queue(dev); + + tx_cmd = lp->tx_cmds + lp->next_tx_cmd; + tbd = lp->tbds + lp->next_tx_cmd; + + if (tx_cmd->cmd.command) { + printk(KERN_NOTICE "%s: xmit ring full, dropping packet.\n", + dev->name); + dev->stats.tx_dropped++; + + dev_kfree_skb(skb); + } else { + if (++lp->next_tx_cmd == TX_RING_SIZE) + lp->next_tx_cmd = 0; + tx_cmd->tbd = WSWAPtbd(virt_to_bus(tbd)); + tbd->next = I596_NULL; + + tx_cmd->cmd.command = CMD_FLEX | CmdTx; + tx_cmd->skb = skb; + + tx_cmd->pad = 0; + tx_cmd->size = 0; + tbd->pad = 0; + tbd->size = EOF | length; + + tbd->data = WSWAPchar(virt_to_bus(skb->data)); + +#ifdef __mc68000__ + cache_push(virt_to_phys(skb->data), length); +#endif + DEB(DEB_TXADDR,print_eth(skb->data, "tx-queued")); + i596_add_cmd(dev, &tx_cmd->cmd); + + dev->stats.tx_packets++; + dev->stats.tx_bytes += length; + } + + netif_start_queue(dev); + + return NETDEV_TX_OK; +} + +static void print_eth(unsigned char *add, char *str) +{ + printk(KERN_DEBUG "i596 0x%p, %pM --> %pM %02X%02X, %s\n", + add, add + 6, add, add[12], add[13], str); +} + +static int io = 0x300; +static int irq = 10; + +static const struct net_device_ops i596_netdev_ops = { + .ndo_open = i596_open, + .ndo_stop = i596_close, + .ndo_start_xmit = i596_start_xmit, + .ndo_set_multicast_list = set_multicast_list, + .ndo_tx_timeout = i596_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +struct net_device * __init i82596_probe(int unit) +{ + struct net_device *dev; + int i; + struct i596_private *lp; + char eth_addr[8]; + static int probed; + int err; + + if (probed) + return ERR_PTR(-ENODEV); + probed++; + + dev = alloc_etherdev(0); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + } else { + dev->base_addr = io; + dev->irq = irq; + } + +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + if (mvme16x_config & MVME16x_CONFIG_NO_ETHERNET) { + printk(KERN_NOTICE "Ethernet probe disabled - chip not present\n"); + err = -ENODEV; + goto out; + } + memcpy(eth_addr, (void *) 0xfffc1f2c, 6); /* YUCK! Get addr from NOVRAM */ + dev->base_addr = MVME_I596_BASE; + dev->irq = (unsigned) MVME16x_IRQ_I596; + goto found; + } +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + volatile unsigned char *rtc = (unsigned char *) BVME_RTC_BASE; + unsigned char msr = rtc[3]; + int i; + + rtc[3] |= 0x80; + for (i = 0; i < 6; i++) + eth_addr[i] = rtc[i * 4 + 7]; /* Stored in RTC RAM at offset 1 */ + rtc[3] = msr; + dev->base_addr = BVME_I596_BASE; + dev->irq = (unsigned) BVME_IRQ_I596; + goto found; + } +#endif +#ifdef ENABLE_APRICOT + { + int checksum = 0; + int ioaddr = 0x300; + + /* this is easy the ethernet interface can only be at 0x300 */ + /* first check nothing is already registered here */ + + if (!request_region(ioaddr, I596_TOTAL_SIZE, DRV_NAME)) { + printk(KERN_ERR "82596: IO address 0x%04x in use\n", ioaddr); + err = -EBUSY; + goto out; + } + + dev->base_addr = ioaddr; + + for (i = 0; i < 8; i++) { + eth_addr[i] = inb(ioaddr + 8 + i); + checksum += eth_addr[i]; + } + + /* checksum is a multiple of 0x100, got this wrong first time + some machines have 0x100, some 0x200. The DOS driver doesn't + even bother with the checksum. + Some other boards trip the checksum.. but then appear as + ether address 0. Trap these - AC */ + + if ((checksum % 0x100) || + (memcmp(eth_addr, "\x00\x00\x49", 3) != 0)) { + err = -ENODEV; + goto out1; + } + + dev->irq = 10; + goto found; + } +#endif + err = -ENODEV; + goto out; + +found: + dev->mem_start = (int)__get_free_pages(GFP_ATOMIC, 0); + if (!dev->mem_start) { + err = -ENOMEM; + goto out1; + } + + DEB(DEB_PROBE,printk(KERN_INFO "%s: 82596 at %#3lx,", dev->name, dev->base_addr)); + + for (i = 0; i < 6; i++) + DEB(DEB_PROBE,printk(" %2.2X", dev->dev_addr[i] = eth_addr[i])); + + DEB(DEB_PROBE,printk(" IRQ %d.\n", dev->irq)); + + DEB(DEB_PROBE,printk(KERN_INFO "%s", version)); + + /* The 82596-specific entries in the device structure. */ + dev->netdev_ops = &i596_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->ml_priv = (void *)(dev->mem_start); + + lp = dev->ml_priv; + DEB(DEB_INIT,printk(KERN_DEBUG "%s: lp at 0x%08lx (%zd bytes), " + "lp->scb at 0x%08lx\n", + dev->name, (unsigned long)lp, + sizeof(struct i596_private), (unsigned long)&lp->scb)); + memset((void *) lp, 0, sizeof(struct i596_private)); + +#ifdef __mc68000__ + cache_push(virt_to_phys((void *)(dev->mem_start)), 4096); + cache_clear(virt_to_phys((void *)(dev->mem_start)), 4096); + kernel_set_cachemode((void *)(dev->mem_start), 4096, IOMAP_NOCACHE_SER); +#endif + lp->scb.command = 0; + lp->scb.cmd = I596_NULL; + lp->scb.rfd = I596_NULL; + spin_lock_init(&lp->lock); + + err = register_netdev(dev); + if (err) + goto out2; + return dev; +out2: +#ifdef __mc68000__ + /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, + * XXX which may be invalid (CONFIG_060_WRITETHROUGH) + */ + kernel_set_cachemode((void *)(dev->mem_start), 4096, + IOMAP_FULL_CACHING); +#endif + free_page ((u32)(dev->mem_start)); +out1: +#ifdef ENABLE_APRICOT + release_region(dev->base_addr, I596_TOTAL_SIZE); +#endif +out: + free_netdev(dev); + return ERR_PTR(err); +} + +static irqreturn_t i596_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct i596_private *lp; + short ioaddr; + unsigned short status, ack_cmd = 0; + int handled = 0; + +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + if (*(char *) BVME_LOCAL_IRQ_STAT & BVME_ETHERR) { + i596_error(irq, dev_id); + return IRQ_HANDLED; + } + } +#endif + if (dev == NULL) { + printk(KERN_ERR "i596_interrupt(): irq %d for unknown device.\n", irq); + return IRQ_NONE; + } + + ioaddr = dev->base_addr; + lp = dev->ml_priv; + + spin_lock (&lp->lock); + + wait_cmd(dev,lp,100,"i596 interrupt, timeout"); + status = lp->scb.status; + + DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt, IRQ %d, status %4.4x.\n", + dev->name, irq, status)); + + ack_cmd = status & 0xf000; + + if ((status & 0x8000) || (status & 0x2000)) { + struct i596_cmd *ptr; + + handled = 1; + if ((status & 0x8000)) + DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt completed command.\n", dev->name)); + if ((status & 0x2000)) + DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700)); + + while ((lp->cmd_head != I596_NULL) && (lp->cmd_head->status & STAT_C)) { + ptr = lp->cmd_head; + + DEB(DEB_STATUS,printk(KERN_DEBUG "cmd_head->status = %04x, ->command = %04x\n", + lp->cmd_head->status, lp->cmd_head->command)); + lp->cmd_head = ptr->v_next; + lp->cmd_backlog--; + + switch ((ptr->command) & 0x7) { + case CmdTx: + { + struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; + struct sk_buff *skb = tx_cmd->skb; + + if ((ptr->status) & STAT_OK) { + DEB(DEB_TXADDR,print_eth(skb->data, "tx-done")); + } else { + dev->stats.tx_errors++; + if ((ptr->status) & 0x0020) + dev->stats.collisions++; + if (!((ptr->status) & 0x0040)) + dev->stats.tx_heartbeat_errors++; + if ((ptr->status) & 0x0400) + dev->stats.tx_carrier_errors++; + if ((ptr->status) & 0x0800) + dev->stats.collisions++; + if ((ptr->status) & 0x1000) + dev->stats.tx_aborted_errors++; + } + + dev_kfree_skb_irq(skb); + + tx_cmd->cmd.command = 0; /* Mark free */ + break; + } + case CmdTDR: + { + unsigned short status = ((struct tdr_cmd *)ptr)->status; + + if (status & 0x8000) { + DEB(DEB_TDR,printk(KERN_INFO "%s: link ok.\n", dev->name)); + } else { + if (status & 0x4000) + printk(KERN_ERR "%s: Transceiver problem.\n", dev->name); + if (status & 0x2000) + printk(KERN_ERR "%s: Termination problem.\n", dev->name); + if (status & 0x1000) + printk(KERN_ERR "%s: Short circuit.\n", dev->name); + + DEB(DEB_TDR,printk(KERN_INFO "%s: Time %d.\n", dev->name, status & 0x07ff)); + } + break; + } + case CmdConfigure: + case CmdMulticastList: + /* Zap command so set_multicast_list() knows it is free */ + ptr->command = 0; + break; + } + ptr->v_next = ptr->b_next = I596_NULL; + lp->last_cmd = jiffies; + } + + ptr = lp->cmd_head; + while ((ptr != I596_NULL) && (ptr != lp->cmd_tail)) { + ptr->command &= 0x1fff; + ptr = ptr->v_next; + } + + if ((lp->cmd_head != I596_NULL)) + ack_cmd |= CUC_START; + lp->scb.cmd = WSWAPcmd(virt_to_bus(&lp->cmd_head->status)); + } + if ((status & 0x1000) || (status & 0x4000)) { + if ((status & 0x4000)) + DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt received a frame.\n", dev->name)); + i596_rx(dev); + /* Only RX_START if stopped - RGH 07-07-96 */ + if (status & 0x1000) { + if (netif_running(dev)) { + DEB(DEB_ERRORS,printk(KERN_ERR "%s: i596 interrupt receive unit inactive, status 0x%x\n", dev->name, status)); + ack_cmd |= RX_START; + dev->stats.rx_errors++; + dev->stats.rx_fifo_errors++; + rebuild_rx_bufs(dev); + } + } + } + wait_cmd(dev,lp,100,"i596 interrupt, timeout"); + lp->scb.command = ack_cmd; + +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + /* Ack the interrupt */ + + volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000; + + pcc2[0x2a] |= 0x08; + } +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG; + + *ethirq = 1; + *ethirq = 3; + } +#endif +#ifdef ENABLE_APRICOT + (void) inb(ioaddr + 0x10); + outb(4, ioaddr + 0xf); +#endif + CA(dev); + + DEB(DEB_INTS,printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name)); + + spin_unlock (&lp->lock); + return IRQ_RETVAL(handled); +} + +static int i596_close(struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + unsigned long flags; + + netif_stop_queue(dev); + + DEB(DEB_INIT,printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, lp->scb.status)); + + spin_lock_irqsave(&lp->lock, flags); + + wait_cmd(dev,lp,100,"close1 timed out"); + lp->scb.command = CUC_ABORT | RX_ABORT; + CA(dev); + + wait_cmd(dev,lp,100,"close2 timed out"); + + spin_unlock_irqrestore(&lp->lock, flags); + DEB(DEB_STRUCT,i596_display_data(dev)); + i596_cleanup_cmd(dev,lp); + +#ifdef ENABLE_MVME16x_NET + if (MACH_IS_MVME16x) { + volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000; + + /* Disable all ints */ + pcc2[0x28] = 1; + pcc2[0x2a] = 0x40; + pcc2[0x2b] = 0x40; /* Set snooping bits now! */ + } +#endif +#ifdef ENABLE_BVME6000_NET + if (MACH_IS_BVME6000) { + volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG; + + *ethirq = 1; + } +#endif + +#ifdef ENABLE_MVME16x_NET + free_irq(0x56, dev); +#endif + free_irq(dev->irq, dev); + remove_rx_bufs(dev); + + return 0; +} + +/* + * Set or clear the multicast filter for this adaptor. + */ + +static void set_multicast_list(struct net_device *dev) +{ + struct i596_private *lp = dev->ml_priv; + int config = 0, cnt; + + DEB(DEB_MULTI,printk(KERN_DEBUG "%s: set multicast list, %d entries, promisc %s, allmulti %s\n", + dev->name, netdev_mc_count(dev), + dev->flags & IFF_PROMISC ? "ON" : "OFF", + dev->flags & IFF_ALLMULTI ? "ON" : "OFF")); + + if (wait_cfg(dev, &lp->cf_cmd.cmd, 1000, "config change request timed out")) + return; + + if ((dev->flags & IFF_PROMISC) && !(lp->cf_cmd.i596_config[8] & 0x01)) { + lp->cf_cmd.i596_config[8] |= 0x01; + config = 1; + } + if (!(dev->flags & IFF_PROMISC) && (lp->cf_cmd.i596_config[8] & 0x01)) { + lp->cf_cmd.i596_config[8] &= ~0x01; + config = 1; + } + if ((dev->flags & IFF_ALLMULTI) && (lp->cf_cmd.i596_config[11] & 0x20)) { + lp->cf_cmd.i596_config[11] &= ~0x20; + config = 1; + } + if (!(dev->flags & IFF_ALLMULTI) && !(lp->cf_cmd.i596_config[11] & 0x20)) { + lp->cf_cmd.i596_config[11] |= 0x20; + config = 1; + } + if (config) { + lp->cf_cmd.cmd.command = CmdConfigure; + i596_add_cmd(dev, &lp->cf_cmd.cmd); + } + + cnt = netdev_mc_count(dev); + if (cnt > MAX_MC_CNT) + { + cnt = MAX_MC_CNT; + printk(KERN_ERR "%s: Only %d multicast addresses supported", + dev->name, cnt); + } + + if (!netdev_mc_empty(dev)) { + struct netdev_hw_addr *ha; + unsigned char *cp; + struct mc_cmd *cmd; + + if (wait_cfg(dev, &lp->mc_cmd.cmd, 1000, "multicast list change request timed out")) + return; + cmd = &lp->mc_cmd; + cmd->cmd.command = CmdMulticastList; + cmd->mc_cnt = cnt * ETH_ALEN; + cp = cmd->mc_addrs; + netdev_for_each_mc_addr(ha, dev) { + if (!cnt--) + break; + memcpy(cp, ha->addr, ETH_ALEN); + if (i596_debug > 1) + DEB(DEB_MULTI,printk(KERN_INFO "%s: Adding address %pM\n", + dev->name, cp)); + cp += ETH_ALEN; + } + i596_add_cmd(dev, &cmd->cmd); + } +} + +#ifdef MODULE +static struct net_device *dev_82596; + +#ifdef ENABLE_APRICOT +module_param(irq, int, 0); +MODULE_PARM_DESC(irq, "Apricot IRQ number"); +#endif + +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "i82596 debug mask"); + +int __init init_module(void) +{ + if (debug >= 0) + i596_debug = debug; + dev_82596 = i82596_probe(-1); + if (IS_ERR(dev_82596)) + return PTR_ERR(dev_82596); + return 0; +} + +void __exit cleanup_module(void) +{ + unregister_netdev(dev_82596); +#ifdef __mc68000__ + /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, + * XXX which may be invalid (CONFIG_060_WRITETHROUGH) + */ + + kernel_set_cachemode((void *)(dev_82596->mem_start), 4096, + IOMAP_FULL_CACHING); +#endif + free_page ((u32)(dev_82596->mem_start)); +#ifdef ENABLE_APRICOT + /* If we don't do this, we can't re-insmod it later. */ + release_region(dev_82596->base_addr, I596_TOTAL_SIZE); +#endif + free_netdev(dev_82596); +} + +#endif /* MODULE */ diff --git a/drivers/net/ethernet/i825xx/Kconfig b/drivers/net/ethernet/i825xx/Kconfig new file mode 100644 index 000000000000..5c30a5b3cba9 --- /dev/null +++ b/drivers/net/ethernet/i825xx/Kconfig @@ -0,0 +1,182 @@ +# +# Intel 82596/82593/82596 network device configuration +# + +config NET_VENDOR_I825XX + bool "Intel (82586/82593/82596) devices" + depends on NET_VENDOR_INTEL && (ISA || ISA_DMA_API || ARM || \ + ARCH_ACORN || MCA || MCA_LEGACY || SNI_RM || SUN3 || \ + GSC || BVME6000 || MVME16x || EXPERIMENTAL) + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + Note that the answer to this question does not directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about these devices. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_I825XX + +config ELPLUS + tristate "3c505 \"EtherLink Plus\" support" + depends on ISA && ISA_DMA_API + ---help--- + Information about this network (Ethernet) card can be found in + <file:Documentation/networking/3c505.txt>. If you have a card of + this type, say Y and read the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To compile this driver as a module, choose M here. The module + will be called 3c505. + +config EL16 + tristate "3c507 \"EtherLink 16\" support (EXPERIMENTAL)" + depends on ISA && EXPERIMENTAL + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To compile this driver as a module, choose M here. The module + will be called 3c507. + +config ELMC + tristate "3c523 \"EtherLink/MC\" support" + depends on MCA_LEGACY + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To compile this driver as a module, choose M here. The module + will be called 3c523. + +config ELMC_II + tristate "3c527 \"EtherLink/MC 32\" support (EXPERIMENTAL)" + depends on MCA && MCA_LEGACY + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To compile this driver as a module, choose M here. The module + will be called 3c527. + +config ARM_ETHER1 + tristate "Acorn Ether1 support" + depends on ARM && ARCH_ACORN + ---help--- + If you have an Acorn system with one of these (AKA25) network cards, + you should say Y to this option if you wish to use it with Linux. + +config APRICOT + tristate "Apricot Xen-II on board Ethernet" + depends on ISA + ---help--- + If you have a network (Ethernet) controller of this type, say Y and + read the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To compile this driver as a module, choose M here. The module + will be called apricot. + +config BVME6000_NET + tristate "BVME6000 Ethernet support" + depends on BVME6000MVME16x + ---help--- + This is the driver for the Ethernet interface on BVME4000 and + BVME6000 VME boards. Say Y here to include the driver for this chip + in your kernel. + To compile this driver as a module, choose M here. + +config EEXPRESS + tristate "EtherExpress 16 support" + depends on ISA + ---help--- + If you have an EtherExpress16 network (Ethernet) card, say Y and + read the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. Note that the Intel + EtherExpress16 card used to be regarded as a very poor choice + because the driver was very unreliable. We now have a new driver + that should do better. + + To compile this driver as a module, choose M here. The module + will be called eexpress. + +config EEXPRESS_PRO + tristate "EtherExpressPro support/EtherExpress 10 (i82595) support" + depends on ISA + ---help--- + If you have a network (Ethernet) card of this type, say Y. This + driver supports Intel i82595{FX,TX} based boards. Note however + that the EtherExpress PRO/100 Ethernet card has its own separate + driver. Please read the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To compile this driver as a module, choose M here. The module + will be called eepro. + +config LASI_82596 + tristate "Lasi ethernet" + depends on GSC + ---help--- + Say Y here to support the builtin Intel 82596 ethernet controller + found in Hewlett-Packard PA-RISC machines with 10Mbit ethernet. + +config LP486E + tristate "LP486E on board Ethernet" + depends on ISA + ---help--- + Say Y here to support the 82596-based on-board Ethernet controller + for the Panther motherboard, which is one of the two shipped in the + Intel Professional Workstation. + +config MVME16x_NET + tristate "MVME16x Ethernet support" + depends on MVME16x + ---help--- + This is the driver for the Ethernet interface on the Motorola + MVME162, 166, 167, 172 and 177 boards. Say Y here to include the + driver for this chip in your kernel. + To compile this driver as a module, choose M here. + +config NI52 + tristate "NI5210 support" + depends on ISA + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To compile this driver as a module, choose M here. The module + will be called ni52. + +config SNI_82596 + tristate "SNI RM ethernet" + depends on SNI_RM + ---help--- + Say Y here to support the on-board Intel 82596 ethernet controller + built into SNI RM machines. + +config SUN3_82586 + bool "Sun3 on-board Intel 82586 support" + depends on SUN3 + ---help--- + This driver enables support for the on-board Intel 82586 based + Ethernet adapter found on Sun 3/1xx and 3/2xx motherboards. Note + that this driver does not support 82586-based adapters on additional + VME boards. + +config ZNET + tristate "Zenith Z-Note support (EXPERIMENTAL)" + depends on EXPERIMENTAL && ISA_DMA_API + ---help--- + The Zenith Z-Note notebook computer has a built-in network + (Ethernet) card, and this is the Linux driver for it. Note that the + IBM Thinkpad 300 is compatible with the Z-Note and is also supported + by this driver. Read the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + +endif # NET_VENDOR_I825XX diff --git a/drivers/net/ethernet/i825xx/Makefile b/drivers/net/ethernet/i825xx/Makefile new file mode 100644 index 000000000000..f68a3694968a --- /dev/null +++ b/drivers/net/ethernet/i825xx/Makefile @@ -0,0 +1,20 @@ +# +# Makefile for the Intel 82586/82593/82596 chipset device drivers. +# + +obj-$(CONFIG_ARM_ETHER1) += ether1.o +obj-$(CONFIG_EEXPRESS) += eexpress.o +obj-$(CONFIG_EEXPRESS_PRO) += eepro.o +obj-$(CONFIG_ELPLUS) += 3c505.o +obj-$(CONFIG_EL16) += 3c507.o +obj-$(CONFIG_ELMC) += 3c523.o +obj-$(CONFIG_ELMC_II) += 3c527.o +obj-$(CONFIG_LP486E) += lp486e.o +obj-$(CONFIG_NI52) += ni52.o +obj-$(CONFIG_SUN3_82586) += sun3_82586.o +obj-$(CONFIG_ZNET) += znet.o +obj-$(CONFIG_APRICOT) += 82596.o +obj-$(CONFIG_LASI_82596) += lasi_82596.o +obj-$(CONFIG_SNI_82596) += sni_82596.o +obj-$(CONFIG_MVME16x_NET) += 82596.o +obj-$(CONFIG_BVME6000_NET) += 82596.o diff --git a/drivers/net/ethernet/i825xx/eepro.c b/drivers/net/ethernet/i825xx/eepro.c new file mode 100644 index 000000000000..dfeb006035df --- /dev/null +++ b/drivers/net/ethernet/i825xx/eepro.c @@ -0,0 +1,1822 @@ +/* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */ +/* + Written 1994, 1995,1996 by Bao C. Ha. + + Copyright (C) 1994, 1995,1996 by Bao C. Ha. + + This software may be used and distributed + according to the terms of the GNU General Public License, + incorporated herein by reference. + + The author may be reached at bao.ha@srs.gov + or 418 Hastings Place, Martinez, GA 30907. + + Things remaining to do: + Better record keeping of errors. + Eliminate transmit interrupt to reduce overhead. + Implement "concurrent processing". I won't be doing it! + + Bugs: + + If you have a problem of not detecting the 82595 during a + reboot (warm reset), disable the FLASH memory should fix it. + This is a compatibility hardware problem. + + Versions: + 0.13b basic ethtool support (aris, 09/13/2004) + 0.13a in memory shortage, drop packets also in board + (Michael Westermann <mw@microdata-pos.de>, 07/30/2002) + 0.13 irq sharing, rewrote probe function, fixed a nasty bug in + hardware_send_packet and a major cleanup (aris, 11/08/2001) + 0.12d fixing a problem with single card detected as eight eth devices + fixing a problem with sudden drop in card performance + (chris (asdn@go2.pl), 10/29/2001) + 0.12c fixing some problems with old cards (aris, 01/08/2001) + 0.12b misc fixes (aris, 06/26/2000) + 0.12a port of version 0.12a of 2.2.x kernels to 2.3.x + (aris (aris@conectiva.com.br), 05/19/2000) + 0.11e some tweaks about multiple cards support (PdP, jul/aug 1999) + 0.11d added __initdata, __init stuff; call spin_lock_init + in eepro_probe1. Replaced "eepro" by dev->name. Augmented + the code protected by spin_lock in interrupt routine + (PdP, 12/12/1998) + 0.11c minor cleanup (PdP, RMC, 09/12/1998) + 0.11b Pascal Dupuis (dupuis@lei.ucl.ac.be): works as a module + under 2.1.xx. Debug messages are flagged as KERN_DEBUG to + avoid console flooding. Added locking at critical parts. Now + the dawn thing is SMP safe. + 0.11a Attempt to get 2.1.xx support up (RMC) + 0.11 Brian Candler added support for multiple cards. Tested as + a module, no idea if it works when compiled into kernel. + + 0.10e Rick Bressler notified me that ifconfig up;ifconfig down fails + because the irq is lost somewhere. Fixed that by moving + request_irq and free_irq to eepro_open and eepro_close respectively. + 0.10d Ugh! Now Wakeup works. Was seriously broken in my first attempt. + I'll need to find a way to specify an ioport other than + the default one in the PnP case. PnP definitively sucks. + And, yes, this is not the only reason. + 0.10c PnP Wakeup Test for 595FX. uncomment #define PnPWakeup; + to use. + 0.10b Should work now with (some) Pro/10+. At least for + me (and my two cards) it does. _No_ guarantee for + function with non-Pro/10+ cards! (don't have any) + (RMC, 9/11/96) + + 0.10 Added support for the Etherexpress Pro/10+. The + IRQ map was changed significantly from the old + pro/10. The new interrupt map was provided by + Rainer M. Canavan (Canavan@Zeus.cs.bonn.edu). + (BCH, 9/3/96) + + 0.09 Fixed a race condition in the transmit algorithm, + which causes crashes under heavy load with fast + pentium computers. The performance should also + improve a bit. The size of RX buffer, and hence + TX buffer, can also be changed via lilo or insmod. + (BCH, 7/31/96) + + 0.08 Implement 32-bit I/O for the 82595TX and 82595FX + based lan cards. Disable full-duplex mode if TPE + is not used. (BCH, 4/8/96) + + 0.07a Fix a stat report which counts every packet as a + heart-beat failure. (BCH, 6/3/95) + + 0.07 Modified to support all other 82595-based lan cards. + The IRQ vector of the EtherExpress Pro will be set + according to the value saved in the EEPROM. For other + cards, I will do autoirq_request() to grab the next + available interrupt vector. (BCH, 3/17/95) + + 0.06a,b Interim released. Minor changes in the comments and + print out format. (BCH, 3/9/95 and 3/14/95) + + 0.06 First stable release that I am comfortable with. (BCH, + 3/2/95) + + 0.05 Complete testing of multicast. (BCH, 2/23/95) + + 0.04 Adding multicast support. (BCH, 2/14/95) + + 0.03 First widely alpha release for public testing. + (BCH, 2/14/95) + +*/ + +static const char version[] = + "eepro.c: v0.13b 09/13/2004 aris@cathedrallabs.org\n"; + +#include <linux/module.h> + +/* + Sources: + + This driver wouldn't have been written without the availability + of the Crynwr's Lan595 driver source code. It helps me to + familiarize with the 82595 chipset while waiting for the Intel + documentation. I also learned how to detect the 82595 using + the packet driver's technique. + + This driver is written by cutting and pasting the skeleton.c driver + provided by Donald Becker. I also borrowed the EEPROM routine from + Donald Becker's 82586 driver. + + Datasheet for the Intel 82595 (including the TX and FX version). It + provides just enough info that the casual reader might think that it + documents the i82595. + + The User Manual for the 82595. It provides a lot of the missing + information. + +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/ethtool.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/dma.h> + +#define DRV_NAME "eepro" +#define DRV_VERSION "0.13c" + +#define compat_dev_kfree_skb( skb, mode ) dev_kfree_skb( (skb) ) +/* I had reports of looong delays with SLOW_DOWN defined as udelay(2) */ +#define SLOW_DOWN inb(0x80) +/* udelay(2) */ +#define compat_init_data __initdata +enum iftype { AUI=0, BNC=1, TPE=2 }; + +/* First, a few definitions that the brave might change. */ +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int eepro_portlist[] compat_init_data = + { 0x300, 0x210, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0}; +/* note: 0x300 is default, the 595FX supports ALL IO Ports + from 0x000 to 0x3F0, some of which are reserved in PCs */ + +/* To try the (not-really PnP Wakeup: */ +/* +#define PnPWakeup +*/ + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif +static unsigned int net_debug = NET_DEBUG; + +/* The number of low I/O ports used by the ethercard. */ +#define EEPRO_IO_EXTENT 16 + +/* Different 82595 chips */ +#define LAN595 0 +#define LAN595TX 1 +#define LAN595FX 2 +#define LAN595FX_10ISA 3 + +/* Information that need to be kept for each board. */ +struct eepro_local { + unsigned rx_start; + unsigned tx_start; /* start of the transmit chain */ + int tx_last; /* pointer to last packet in the transmit chain */ + unsigned tx_end; /* end of the transmit chain (plus 1) */ + int eepro; /* 1 for the EtherExpress Pro/10, + 2 for the EtherExpress Pro/10+, + 3 for the EtherExpress 10 (blue cards), + 0 for other 82595-based lan cards. */ + int version; /* a flag to indicate if this is a TX or FX + version of the 82595 chip. */ + int stepping; + + spinlock_t lock; /* Serializing lock */ + + unsigned rcv_ram; /* pre-calculated space for rx */ + unsigned xmt_ram; /* pre-calculated space for tx */ + unsigned char xmt_bar; + unsigned char xmt_lower_limit_reg; + unsigned char xmt_upper_limit_reg; + short xmt_lower_limit; + short xmt_upper_limit; + short rcv_lower_limit; + short rcv_upper_limit; + unsigned char eeprom_reg; + unsigned short word[8]; +}; + +/* The station (ethernet) address prefix, used for IDing the board. */ +#define SA_ADDR0 0x00 /* Etherexpress Pro/10 */ +#define SA_ADDR1 0xaa +#define SA_ADDR2 0x00 + +#define GetBit(x,y) ((x & (1<<y))>>y) + +/* EEPROM Word 0: */ +#define ee_PnP 0 /* Plug 'n Play enable bit */ +#define ee_Word1 1 /* Word 1? */ +#define ee_BusWidth 2 /* 8/16 bit */ +#define ee_FlashAddr 3 /* Flash Address */ +#define ee_FlashMask 0x7 /* Mask */ +#define ee_AutoIO 6 /* */ +#define ee_reserved0 7 /* =0! */ +#define ee_Flash 8 /* Flash there? */ +#define ee_AutoNeg 9 /* Auto Negotiation enabled? */ +#define ee_IO0 10 /* IO Address LSB */ +#define ee_IO0Mask 0x /*...*/ +#define ee_IO1 15 /* IO MSB */ + +/* EEPROM Word 1: */ +#define ee_IntSel 0 /* Interrupt */ +#define ee_IntMask 0x7 +#define ee_LI 3 /* Link Integrity 0= enabled */ +#define ee_PC 4 /* Polarity Correction 0= enabled */ +#define ee_TPE_AUI 5 /* PortSelection 1=TPE */ +#define ee_Jabber 6 /* Jabber prevention 0= enabled */ +#define ee_AutoPort 7 /* Auto Port Selection 1= Disabled */ +#define ee_SMOUT 8 /* SMout Pin Control 0= Input */ +#define ee_PROM 9 /* Flash EPROM / PROM 0=Flash */ +#define ee_reserved1 10 /* .. 12 =0! */ +#define ee_AltReady 13 /* Alternate Ready, 0=normal */ +#define ee_reserved2 14 /* =0! */ +#define ee_Duplex 15 + +/* Word2,3,4: */ +#define ee_IA5 0 /*bit start for individual Addr Byte 5 */ +#define ee_IA4 8 /*bit start for individual Addr Byte 5 */ +#define ee_IA3 0 /*bit start for individual Addr Byte 5 */ +#define ee_IA2 8 /*bit start for individual Addr Byte 5 */ +#define ee_IA1 0 /*bit start for individual Addr Byte 5 */ +#define ee_IA0 8 /*bit start for individual Addr Byte 5 */ + +/* Word 5: */ +#define ee_BNC_TPE 0 /* 0=TPE */ +#define ee_BootType 1 /* 00=None, 01=IPX, 10=ODI, 11=NDIS */ +#define ee_BootTypeMask 0x3 +#define ee_NumConn 3 /* Number of Connections 0= One or Two */ +#define ee_FlashSock 4 /* Presence of Flash Socket 0= Present */ +#define ee_PortTPE 5 +#define ee_PortBNC 6 +#define ee_PortAUI 7 +#define ee_PowerMgt 10 /* 0= disabled */ +#define ee_CP 13 /* Concurrent Processing */ +#define ee_CPMask 0x7 + +/* Word 6: */ +#define ee_Stepping 0 /* Stepping info */ +#define ee_StepMask 0x0F +#define ee_BoardID 4 /* Manucaturer Board ID, reserved */ +#define ee_BoardMask 0x0FFF + +/* Word 7: */ +#define ee_INT_TO_IRQ 0 /* int to IRQ Mapping = 0x1EB8 for Pro/10+ */ +#define ee_FX_INT2IRQ 0x1EB8 /* the _only_ mapping allowed for FX chips */ + +/*..*/ +#define ee_SIZE 0x40 /* total EEprom Size */ +#define ee_Checksum 0xBABA /* initial and final value for adding checksum */ + + +/* Card identification via EEprom: */ +#define ee_addr_vendor 0x10 /* Word offset for EISA Vendor ID */ +#define ee_addr_id 0x11 /* Word offset for Card ID */ +#define ee_addr_SN 0x12 /* Serial Number */ +#define ee_addr_CRC_8 0x14 /* CRC over last thee Bytes */ + + +#define ee_vendor_intel0 0x25 /* Vendor ID Intel */ +#define ee_vendor_intel1 0xD4 +#define ee_id_eepro10p0 0x10 /* ID for eepro/10+ */ +#define ee_id_eepro10p1 0x31 + +#define TX_TIMEOUT ((4*HZ)/10) + +/* Index to functions, as function prototypes. */ + +static int eepro_probe1(struct net_device *dev, int autoprobe); +static int eepro_open(struct net_device *dev); +static netdev_tx_t eepro_send_packet(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t eepro_interrupt(int irq, void *dev_id); +static void eepro_rx(struct net_device *dev); +static void eepro_transmit_interrupt(struct net_device *dev); +static int eepro_close(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void eepro_tx_timeout (struct net_device *dev); + +static int read_eeprom(int ioaddr, int location, struct net_device *dev); +static int hardware_send_packet(struct net_device *dev, void *buf, short length); +static int eepro_grab_irq(struct net_device *dev); + +/* + Details of the i82595. + +You will need either the datasheet or the user manual to understand what +is going on here. The 82595 is very different from the 82586, 82593. + +The receive algorithm in eepro_rx() is just an implementation of the +RCV ring structure that the Intel 82595 imposes at the hardware level. +The receive buffer is set at 24K, and the transmit buffer is 8K. I +am assuming that the total buffer memory is 32K, which is true for the +Intel EtherExpress Pro/10. If it is less than that on a generic card, +the driver will be broken. + +The transmit algorithm in the hardware_send_packet() is similar to the +one in the eepro_rx(). The transmit buffer is a ring linked list. +I just queue the next available packet to the end of the list. In my +system, the 82595 is so fast that the list seems to always contain a +single packet. In other systems with faster computers and more congested +network traffics, the ring linked list should improve performance by +allowing up to 8K worth of packets to be queued. + +The sizes of the receive and transmit buffers can now be changed via lilo +or insmod. Lilo uses the appended line "ether=io,irq,debug,rx-buffer,eth0" +where rx-buffer is in KB unit. Modules uses the parameter mem which is +also in KB unit, for example "insmod io=io-address irq=0 mem=rx-buffer." +The receive buffer has to be more than 3K or less than 29K. Otherwise, +it is reset to the default of 24K, and, hence, 8K for the trasnmit +buffer (transmit-buffer = 32K - receive-buffer). + +*/ +#define RAM_SIZE 0x8000 + +#define RCV_HEADER 8 +#define RCV_DEFAULT_RAM 0x6000 + +#define XMT_HEADER 8 +#define XMT_DEFAULT_RAM (RAM_SIZE - RCV_DEFAULT_RAM) + +#define XMT_START_PRO RCV_DEFAULT_RAM +#define XMT_START_10 0x0000 +#define RCV_START_PRO 0x0000 +#define RCV_START_10 XMT_DEFAULT_RAM + +#define RCV_DONE 0x0008 +#define RX_OK 0x2000 +#define RX_ERROR 0x0d81 + +#define TX_DONE_BIT 0x0080 +#define TX_OK 0x2000 +#define CHAIN_BIT 0x8000 +#define XMT_STATUS 0x02 +#define XMT_CHAIN 0x04 +#define XMT_COUNT 0x06 + +#define BANK0_SELECT 0x00 +#define BANK1_SELECT 0x40 +#define BANK2_SELECT 0x80 + +/* Bank 0 registers */ +#define COMMAND_REG 0x00 /* Register 0 */ +#define MC_SETUP 0x03 +#define XMT_CMD 0x04 +#define DIAGNOSE_CMD 0x07 +#define RCV_ENABLE_CMD 0x08 +#define RCV_DISABLE_CMD 0x0a +#define STOP_RCV_CMD 0x0b +#define RESET_CMD 0x0e +#define POWER_DOWN_CMD 0x18 +#define RESUME_XMT_CMD 0x1c +#define SEL_RESET_CMD 0x1e +#define STATUS_REG 0x01 /* Register 1 */ +#define RX_INT 0x02 +#define TX_INT 0x04 +#define EXEC_STATUS 0x30 +#define ID_REG 0x02 /* Register 2 */ +#define R_ROBIN_BITS 0xc0 /* round robin counter */ +#define ID_REG_MASK 0x2c +#define ID_REG_SIG 0x24 +#define AUTO_ENABLE 0x10 +#define INT_MASK_REG 0x03 /* Register 3 */ +#define RX_STOP_MASK 0x01 +#define RX_MASK 0x02 +#define TX_MASK 0x04 +#define EXEC_MASK 0x08 +#define ALL_MASK 0x0f +#define IO_32_BIT 0x10 +#define RCV_BAR 0x04 /* The following are word (16-bit) registers */ +#define RCV_STOP 0x06 + +#define XMT_BAR_PRO 0x0a +#define XMT_BAR_10 0x0b + +#define HOST_ADDRESS_REG 0x0c +#define IO_PORT 0x0e +#define IO_PORT_32_BIT 0x0c + +/* Bank 1 registers */ +#define REG1 0x01 +#define WORD_WIDTH 0x02 +#define INT_ENABLE 0x80 +#define INT_NO_REG 0x02 +#define RCV_LOWER_LIMIT_REG 0x08 +#define RCV_UPPER_LIMIT_REG 0x09 + +#define XMT_LOWER_LIMIT_REG_PRO 0x0a +#define XMT_UPPER_LIMIT_REG_PRO 0x0b +#define XMT_LOWER_LIMIT_REG_10 0x0b +#define XMT_UPPER_LIMIT_REG_10 0x0a + +/* Bank 2 registers */ +#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */ +#define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */ +#define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */ +#define REG2 0x02 +#define PRMSC_Mode 0x01 +#define Multi_IA 0x20 +#define REG3 0x03 +#define TPE_BIT 0x04 +#define BNC_BIT 0x20 +#define REG13 0x0d +#define FDX 0x00 +#define A_N_ENABLE 0x02 + +#define I_ADD_REG0 0x04 +#define I_ADD_REG1 0x05 +#define I_ADD_REG2 0x06 +#define I_ADD_REG3 0x07 +#define I_ADD_REG4 0x08 +#define I_ADD_REG5 0x09 + +#define EEPROM_REG_PRO 0x0a +#define EEPROM_REG_10 0x0b + +#define EESK 0x01 +#define EECS 0x02 +#define EEDI 0x04 +#define EEDO 0x08 + +/* do a full reset */ +#define eepro_reset(ioaddr) outb(RESET_CMD, ioaddr) + +/* do a nice reset */ +#define eepro_sel_reset(ioaddr) { \ + outb(SEL_RESET_CMD, ioaddr); \ + SLOW_DOWN; \ + SLOW_DOWN; \ + } + +/* disable all interrupts */ +#define eepro_dis_int(ioaddr) outb(ALL_MASK, ioaddr + INT_MASK_REG) + +/* clear all interrupts */ +#define eepro_clear_int(ioaddr) outb(ALL_MASK, ioaddr + STATUS_REG) + +/* enable tx/rx */ +#define eepro_en_int(ioaddr) outb(ALL_MASK & ~(RX_MASK | TX_MASK), \ + ioaddr + INT_MASK_REG) + +/* enable exec event interrupt */ +#define eepro_en_intexec(ioaddr) outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG) + +/* enable rx */ +#define eepro_en_rx(ioaddr) outb(RCV_ENABLE_CMD, ioaddr) + +/* disable rx */ +#define eepro_dis_rx(ioaddr) outb(RCV_DISABLE_CMD, ioaddr) + +/* switch bank */ +#define eepro_sw2bank0(ioaddr) outb(BANK0_SELECT, ioaddr) +#define eepro_sw2bank1(ioaddr) outb(BANK1_SELECT, ioaddr) +#define eepro_sw2bank2(ioaddr) outb(BANK2_SELECT, ioaddr) + +/* enable interrupt line */ +#define eepro_en_intline(ioaddr) outb(inb(ioaddr + REG1) | INT_ENABLE,\ + ioaddr + REG1) + +/* disable interrupt line */ +#define eepro_dis_intline(ioaddr) outb(inb(ioaddr + REG1) & 0x7f, \ + ioaddr + REG1); + +/* set diagnose flag */ +#define eepro_diag(ioaddr) outb(DIAGNOSE_CMD, ioaddr) + +/* ack for rx int */ +#define eepro_ack_rx(ioaddr) outb (RX_INT, ioaddr + STATUS_REG) + +/* ack for tx int */ +#define eepro_ack_tx(ioaddr) outb (TX_INT, ioaddr + STATUS_REG) + +/* a complete sel reset */ +#define eepro_complete_selreset(ioaddr) { \ + dev->stats.tx_errors++;\ + eepro_sel_reset(ioaddr);\ + lp->tx_end = \ + lp->xmt_lower_limit;\ + lp->tx_start = lp->tx_end;\ + lp->tx_last = 0;\ + dev->trans_start = jiffies;\ + netif_wake_queue(dev);\ + eepro_en_rx(ioaddr);\ + } + +/* Check for a network adaptor of this type, and return '0' if one exists. + If dev->base_addr == 0, probe all likely locations. + If dev->base_addr == 1, always return failure. + If dev->base_addr == 2, allocate space for the device and return success + (detachable devices only). + */ +static int __init do_eepro_probe(struct net_device *dev) +{ + int i; + int base_addr = dev->base_addr; + int irq = dev->irq; + +#ifdef PnPWakeup + /* XXXX for multiple cards should this only be run once? */ + + /* Wakeup: */ + #define WakeupPort 0x279 + #define WakeupSeq {0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,\ + 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,\ + 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,\ + 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x43} + + { + unsigned short int WS[32]=WakeupSeq; + + if (request_region(WakeupPort, 2, "eepro wakeup")) { + if (net_debug>5) + printk(KERN_DEBUG "Waking UP\n"); + + outb_p(0,WakeupPort); + outb_p(0,WakeupPort); + for (i=0; i<32; i++) { + outb_p(WS[i],WakeupPort); + if (net_debug>5) printk(KERN_DEBUG ": %#x ",WS[i]); + } + + release_region(WakeupPort, 2); + } else + printk(KERN_WARNING "PnP wakeup region busy!\n"); + } +#endif + + if (base_addr > 0x1ff) /* Check a single specified location. */ + return eepro_probe1(dev, 0); + + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + + for (i = 0; eepro_portlist[i]; i++) { + dev->base_addr = eepro_portlist[i]; + dev->irq = irq; + if (eepro_probe1(dev, 1) == 0) + return 0; + } + + return -ENODEV; +} + +#ifndef MODULE +struct net_device * __init eepro_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct eepro_local)); + int err; + + if (!dev) + return ERR_PTR(-ENODEV); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = do_eepro_probe(dev); + if (err) + goto out; + return dev; +out: + free_netdev(dev); + return ERR_PTR(err); +} +#endif + +static void __init printEEPROMInfo(struct net_device *dev) +{ + struct eepro_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + unsigned short Word; + int i,j; + + j = ee_Checksum; + for (i = 0; i < 8; i++) + j += lp->word[i]; + for ( ; i < ee_SIZE; i++) + j += read_eeprom(ioaddr, i, dev); + + printk(KERN_DEBUG "Checksum: %#x\n",j&0xffff); + + Word = lp->word[0]; + printk(KERN_DEBUG "Word0:\n"); + printk(KERN_DEBUG " Plug 'n Pray: %d\n",GetBit(Word,ee_PnP)); + printk(KERN_DEBUG " Buswidth: %d\n",(GetBit(Word,ee_BusWidth)+1)*8 ); + printk(KERN_DEBUG " AutoNegotiation: %d\n",GetBit(Word,ee_AutoNeg)); + printk(KERN_DEBUG " IO Address: %#x\n", (Word>>ee_IO0)<<4); + + if (net_debug>4) { + Word = lp->word[1]; + printk(KERN_DEBUG "Word1:\n"); + printk(KERN_DEBUG " INT: %d\n", Word & ee_IntMask); + printk(KERN_DEBUG " LI: %d\n", GetBit(Word,ee_LI)); + printk(KERN_DEBUG " PC: %d\n", GetBit(Word,ee_PC)); + printk(KERN_DEBUG " TPE/AUI: %d\n", GetBit(Word,ee_TPE_AUI)); + printk(KERN_DEBUG " Jabber: %d\n", GetBit(Word,ee_Jabber)); + printk(KERN_DEBUG " AutoPort: %d\n", !GetBit(Word,ee_AutoPort)); + printk(KERN_DEBUG " Duplex: %d\n", GetBit(Word,ee_Duplex)); + } + + Word = lp->word[5]; + printk(KERN_DEBUG "Word5:\n"); + printk(KERN_DEBUG " BNC: %d\n",GetBit(Word,ee_BNC_TPE)); + printk(KERN_DEBUG " NumConnectors: %d\n",GetBit(Word,ee_NumConn)); + printk(KERN_DEBUG " Has "); + if (GetBit(Word,ee_PortTPE)) printk(KERN_DEBUG "TPE "); + if (GetBit(Word,ee_PortBNC)) printk(KERN_DEBUG "BNC "); + if (GetBit(Word,ee_PortAUI)) printk(KERN_DEBUG "AUI "); + printk(KERN_DEBUG "port(s)\n"); + + Word = lp->word[6]; + printk(KERN_DEBUG "Word6:\n"); + printk(KERN_DEBUG " Stepping: %d\n",Word & ee_StepMask); + printk(KERN_DEBUG " BoardID: %d\n",Word>>ee_BoardID); + + Word = lp->word[7]; + printk(KERN_DEBUG "Word7:\n"); + printk(KERN_DEBUG " INT to IRQ:\n"); + + for (i=0, j=0; i<15; i++) + if (GetBit(Word,i)) printk(KERN_DEBUG " INT%d -> IRQ %d;",j++,i); + + printk(KERN_DEBUG "\n"); +} + +/* function to recalculate the limits of buffer based on rcv_ram */ +static void eepro_recalc (struct net_device *dev) +{ + struct eepro_local * lp; + + lp = netdev_priv(dev); + lp->xmt_ram = RAM_SIZE - lp->rcv_ram; + + if (lp->eepro == LAN595FX_10ISA) { + lp->xmt_lower_limit = XMT_START_10; + lp->xmt_upper_limit = (lp->xmt_ram - 2); + lp->rcv_lower_limit = lp->xmt_ram; + lp->rcv_upper_limit = (RAM_SIZE - 2); + } + else { + lp->rcv_lower_limit = RCV_START_PRO; + lp->rcv_upper_limit = (lp->rcv_ram - 2); + lp->xmt_lower_limit = lp->rcv_ram; + lp->xmt_upper_limit = (RAM_SIZE - 2); + } +} + +/* prints boot-time info */ +static void __init eepro_print_info (struct net_device *dev) +{ + struct eepro_local * lp = netdev_priv(dev); + int i; + const char * ifmap[] = {"AUI", "10Base2", "10BaseT"}; + + i = inb(dev->base_addr + ID_REG); + printk(KERN_DEBUG " id: %#x ",i); + printk(" io: %#x ", (unsigned)dev->base_addr); + + switch (lp->eepro) { + case LAN595FX_10ISA: + printk("%s: Intel EtherExpress 10 ISA\n at %#x,", + dev->name, (unsigned)dev->base_addr); + break; + case LAN595FX: + printk("%s: Intel EtherExpress Pro/10+ ISA\n at %#x,", + dev->name, (unsigned)dev->base_addr); + break; + case LAN595TX: + printk("%s: Intel EtherExpress Pro/10 ISA at %#x,", + dev->name, (unsigned)dev->base_addr); + break; + case LAN595: + printk("%s: Intel 82595-based lan card at %#x,", + dev->name, (unsigned)dev->base_addr); + break; + } + + printk(" %pM", dev->dev_addr); + + if (net_debug > 3) + printk(KERN_DEBUG ", %dK RCV buffer", + (int)(lp->rcv_ram)/1024); + + if (dev->irq > 2) + printk(", IRQ %d, %s.\n", dev->irq, ifmap[dev->if_port]); + else + printk(", %s.\n", ifmap[dev->if_port]); + + if (net_debug > 3) { + i = lp->word[5]; + if (i & 0x2000) /* bit 13 of EEPROM word 5 */ + printk(KERN_DEBUG "%s: Concurrent Processing is " + "enabled but not used!\n", dev->name); + } + + /* Check the station address for the manufacturer's code */ + if (net_debug>3) + printEEPROMInfo(dev); +} + +static const struct ethtool_ops eepro_ethtool_ops; + +static const struct net_device_ops eepro_netdev_ops = { + .ndo_open = eepro_open, + .ndo_stop = eepro_close, + .ndo_start_xmit = eepro_send_packet, + .ndo_set_multicast_list = set_multicast_list, + .ndo_tx_timeout = eepro_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +/* This is the real probe routine. Linux has a history of friendly device + probes on the ISA bus. A good device probe avoids doing writes, and + verifies that the correct device exists and functions. */ + +static int __init eepro_probe1(struct net_device *dev, int autoprobe) +{ + unsigned short station_addr[3], id, counter; + int i; + struct eepro_local *lp; + int ioaddr = dev->base_addr; + int err; + + /* Grab the region so we can find another board if autoIRQ fails. */ + if (!request_region(ioaddr, EEPRO_IO_EXTENT, DRV_NAME)) { + if (!autoprobe) + printk(KERN_WARNING "EEPRO: io-port 0x%04x in use\n", + ioaddr); + return -EBUSY; + } + + /* Now, we are going to check for the signature of the + ID_REG (register 2 of bank 0) */ + + id = inb(ioaddr + ID_REG); + + if ((id & ID_REG_MASK) != ID_REG_SIG) + goto exit; + + /* We seem to have the 82595 signature, let's + play with its counter (last 2 bits of + register 2 of bank 0) to be sure. */ + + counter = id & R_ROBIN_BITS; + + if ((inb(ioaddr + ID_REG) & R_ROBIN_BITS) != (counter + 0x40)) + goto exit; + + lp = netdev_priv(dev); + memset(lp, 0, sizeof(struct eepro_local)); + lp->xmt_bar = XMT_BAR_PRO; + lp->xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_PRO; + lp->xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_PRO; + lp->eeprom_reg = EEPROM_REG_PRO; + spin_lock_init(&lp->lock); + + /* Now, get the ethernet hardware address from + the EEPROM */ + station_addr[0] = read_eeprom(ioaddr, 2, dev); + + /* FIXME - find another way to know that we've found + * an Etherexpress 10 + */ + if (station_addr[0] == 0x0000 || station_addr[0] == 0xffff) { + lp->eepro = LAN595FX_10ISA; + lp->eeprom_reg = EEPROM_REG_10; + lp->xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10; + lp->xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10; + lp->xmt_bar = XMT_BAR_10; + station_addr[0] = read_eeprom(ioaddr, 2, dev); + } + + /* get all words at once. will be used here and for ethtool */ + for (i = 0; i < 8; i++) { + lp->word[i] = read_eeprom(ioaddr, i, dev); + } + station_addr[1] = lp->word[3]; + station_addr[2] = lp->word[4]; + + if (!lp->eepro) { + if (lp->word[7] == ee_FX_INT2IRQ) + lp->eepro = 2; + else if (station_addr[2] == SA_ADDR1) + lp->eepro = 1; + } + + /* Fill in the 'dev' fields. */ + for (i=0; i < 6; i++) + dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i]; + + /* RX buffer must be more than 3K and less than 29K */ + if (dev->mem_end < 3072 || dev->mem_end > 29696) + lp->rcv_ram = RCV_DEFAULT_RAM; + + /* calculate {xmt,rcv}_{lower,upper}_limit */ + eepro_recalc(dev); + + if (GetBit(lp->word[5], ee_BNC_TPE)) + dev->if_port = BNC; + else + dev->if_port = TPE; + + if (dev->irq < 2 && lp->eepro != 0) { + /* Mask off INT number */ + int count = lp->word[1] & 7; + unsigned irqMask = lp->word[7]; + + while (count--) + irqMask &= irqMask - 1; + + count = ffs(irqMask); + + if (count) + dev->irq = count - 1; + + if (dev->irq < 2) { + printk(KERN_ERR " Duh! illegal interrupt vector stored in EEPROM.\n"); + goto exit; + } else if (dev->irq == 2) { + dev->irq = 9; + } + } + + dev->netdev_ops = &eepro_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + dev->ethtool_ops = &eepro_ethtool_ops; + + /* print boot time info */ + eepro_print_info(dev); + + /* reset 82595 */ + eepro_reset(ioaddr); + + err = register_netdev(dev); + if (err) + goto err; + return 0; +exit: + err = -ENODEV; +err: + release_region(dev->base_addr, EEPRO_IO_EXTENT); + return err; +} + +/* Open/initialize the board. This is called (in the current kernel) + sometime after booting when the 'ifconfig' program is run. + + This routine should set everything up anew at each open, even + registers that "should" only need to be set once at boot, so that + there is non-reboot way to recover if something goes wrong. + */ + +static const char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1}; +static const char irqrmap2[] = {-1,-1,4,0,1,2,-1,3,-1,4,5,6,7,-1,-1,-1}; +static int eepro_grab_irq(struct net_device *dev) +{ + static const int irqlist[] = { 3, 4, 5, 7, 9, 10, 11, 12, 0 }; + const int *irqp = irqlist; + int temp_reg, ioaddr = dev->base_addr; + + eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */ + + /* Enable the interrupt line. */ + eepro_en_intline(ioaddr); + + /* be CAREFUL, BANK 0 now */ + eepro_sw2bank0(ioaddr); + + /* clear all interrupts */ + eepro_clear_int(ioaddr); + + /* Let EXEC event to interrupt */ + eepro_en_intexec(ioaddr); + + do { + eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */ + + temp_reg = inb(ioaddr + INT_NO_REG); + outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG); + + eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */ + + if (request_irq (*irqp, NULL, IRQF_SHARED, "bogus", dev) != EBUSY) { + unsigned long irq_mask; + /* Twinkle the interrupt, and check if it's seen */ + irq_mask = probe_irq_on(); + + eepro_diag(ioaddr); /* RESET the 82595 */ + mdelay(20); + + if (*irqp == probe_irq_off(irq_mask)) /* It's a good IRQ line */ + break; + + /* clear all interrupts */ + eepro_clear_int(ioaddr); + } + } while (*++irqp); + + eepro_sw2bank1(ioaddr); /* Switch back to Bank 1 */ + + /* Disable the physical interrupt line. */ + eepro_dis_intline(ioaddr); + + eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */ + + /* Mask all the interrupts. */ + eepro_dis_int(ioaddr); + + /* clear all interrupts */ + eepro_clear_int(ioaddr); + + return dev->irq; +} + +static int eepro_open(struct net_device *dev) +{ + unsigned short temp_reg, old8, old9; + int irqMask; + int i, ioaddr = dev->base_addr; + struct eepro_local *lp = netdev_priv(dev); + + if (net_debug > 3) + printk(KERN_DEBUG "%s: entering eepro_open routine.\n", dev->name); + + irqMask = lp->word[7]; + + if (lp->eepro == LAN595FX_10ISA) { + if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 3;\n"); + } + else if (irqMask == ee_FX_INT2IRQ) /* INT to IRQ Mask */ + { + lp->eepro = 2; /* Yes, an Intel EtherExpress Pro/10+ */ + if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 2;\n"); + } + + else if ((dev->dev_addr[0] == SA_ADDR0 && + dev->dev_addr[1] == SA_ADDR1 && + dev->dev_addr[2] == SA_ADDR2)) + { + lp->eepro = 1; + if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 1;\n"); + } /* Yes, an Intel EtherExpress Pro/10 */ + + else lp->eepro = 0; /* No, it is a generic 82585 lan card */ + + /* Get the interrupt vector for the 82595 */ + if (dev->irq < 2 && eepro_grab_irq(dev) == 0) { + printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq); + return -EAGAIN; + } + + if (request_irq(dev->irq , eepro_interrupt, 0, dev->name, dev)) { + printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq); + return -EAGAIN; + } + + /* Initialize the 82595. */ + + eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */ + temp_reg = inb(ioaddr + lp->eeprom_reg); + + lp->stepping = temp_reg >> 5; /* Get the stepping number of the 595 */ + + if (net_debug > 3) + printk(KERN_DEBUG "The stepping of the 82595 is %d\n", lp->stepping); + + if (temp_reg & 0x10) /* Check the TurnOff Enable bit */ + outb(temp_reg & 0xef, ioaddr + lp->eeprom_reg); + for (i=0; i < 6; i++) + outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i); + + temp_reg = inb(ioaddr + REG1); /* Setup Transmit Chaining */ + outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */ + | RCV_Discard_BadFrame, ioaddr + REG1); + + temp_reg = inb(ioaddr + REG2); /* Match broadcast */ + outb(temp_reg | 0x14, ioaddr + REG2); + + temp_reg = inb(ioaddr + REG3); + outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */ + + /* Set the receiving mode */ + eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */ + + /* Set the interrupt vector */ + temp_reg = inb(ioaddr + INT_NO_REG); + if (lp->eepro == LAN595FX || lp->eepro == LAN595FX_10ISA) + outb((temp_reg & 0xf8) | irqrmap2[dev->irq], ioaddr + INT_NO_REG); + else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); + + + temp_reg = inb(ioaddr + INT_NO_REG); + if (lp->eepro == LAN595FX || lp->eepro == LAN595FX_10ISA) + outb((temp_reg & 0xf0) | irqrmap2[dev->irq] | 0x08,ioaddr+INT_NO_REG); + else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); + + if (net_debug > 3) + printk(KERN_DEBUG "eepro_open: content of INT Reg is %x\n", temp_reg); + + + /* Initialize the RCV and XMT upper and lower limits */ + outb(lp->rcv_lower_limit >> 8, ioaddr + RCV_LOWER_LIMIT_REG); + outb(lp->rcv_upper_limit >> 8, ioaddr + RCV_UPPER_LIMIT_REG); + outb(lp->xmt_lower_limit >> 8, ioaddr + lp->xmt_lower_limit_reg); + outb(lp->xmt_upper_limit >> 8, ioaddr + lp->xmt_upper_limit_reg); + + /* Enable the interrupt line. */ + eepro_en_intline(ioaddr); + + /* Switch back to Bank 0 */ + eepro_sw2bank0(ioaddr); + + /* Let RX and TX events to interrupt */ + eepro_en_int(ioaddr); + + /* clear all interrupts */ + eepro_clear_int(ioaddr); + + /* Initialize RCV */ + outw(lp->rcv_lower_limit, ioaddr + RCV_BAR); + lp->rx_start = lp->rcv_lower_limit; + outw(lp->rcv_upper_limit | 0xfe, ioaddr + RCV_STOP); + + /* Initialize XMT */ + outw(lp->xmt_lower_limit, ioaddr + lp->xmt_bar); + lp->tx_start = lp->tx_end = lp->xmt_lower_limit; + lp->tx_last = 0; + + /* Check for the i82595TX and i82595FX */ + old8 = inb(ioaddr + 8); + outb(~old8, ioaddr + 8); + + if ((temp_reg = inb(ioaddr + 8)) == old8) { + if (net_debug > 3) + printk(KERN_DEBUG "i82595 detected!\n"); + lp->version = LAN595; + } + else { + lp->version = LAN595TX; + outb(old8, ioaddr + 8); + old9 = inb(ioaddr + 9); + + if (irqMask==ee_FX_INT2IRQ) { + if (net_debug > 3) { + printk(KERN_DEBUG "IrqMask: %#x\n",irqMask); + printk(KERN_DEBUG "i82595FX detected!\n"); + } + lp->version = LAN595FX; + outb(old9, ioaddr + 9); + if (dev->if_port != TPE) { /* Hopefully, this will fix the + problem of using Pentiums and + pro/10 w/ BNC. */ + eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */ + temp_reg = inb(ioaddr + REG13); + /* disable the full duplex mode since it is not + applicable with the 10Base2 cable. */ + outb(temp_reg & ~(FDX | A_N_ENABLE), REG13); + eepro_sw2bank0(ioaddr); /* be CAREFUL, BANK 0 now */ + } + } + else if (net_debug > 3) { + printk(KERN_DEBUG "temp_reg: %#x ~old9: %#x\n",temp_reg,((~old9)&0xff)); + printk(KERN_DEBUG "i82595TX detected!\n"); + } + } + + eepro_sel_reset(ioaddr); + + netif_start_queue(dev); + + if (net_debug > 3) + printk(KERN_DEBUG "%s: exiting eepro_open routine.\n", dev->name); + + /* enabling rx */ + eepro_en_rx(ioaddr); + + return 0; +} + +static void eepro_tx_timeout (struct net_device *dev) +{ + struct eepro_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + /* if (net_debug > 1) */ + printk (KERN_ERR "%s: transmit timed out, %s?\n", dev->name, + "network cable problem"); + /* This is not a duplicate. One message for the console, + one for the log file */ + printk (KERN_DEBUG "%s: transmit timed out, %s?\n", dev->name, + "network cable problem"); + eepro_complete_selreset(ioaddr); +} + + +static netdev_tx_t eepro_send_packet(struct sk_buff *skb, + struct net_device *dev) +{ + struct eepro_local *lp = netdev_priv(dev); + unsigned long flags; + int ioaddr = dev->base_addr; + short length = skb->len; + + if (net_debug > 5) + printk(KERN_DEBUG "%s: entering eepro_send_packet routine.\n", dev->name); + + if (length < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + length = ETH_ZLEN; + } + netif_stop_queue (dev); + + eepro_dis_int(ioaddr); + spin_lock_irqsave(&lp->lock, flags); + + { + unsigned char *buf = skb->data; + + if (hardware_send_packet(dev, buf, length)) + /* we won't wake queue here because we're out of space */ + dev->stats.tx_dropped++; + else { + dev->stats.tx_bytes+=skb->len; + netif_wake_queue(dev); + } + + } + + dev_kfree_skb (skb); + + /* You might need to clean up and record Tx statistics here. */ + /* dev->stats.tx_aborted_errors++; */ + + if (net_debug > 5) + printk(KERN_DEBUG "%s: exiting eepro_send_packet routine.\n", dev->name); + + eepro_en_int(ioaddr); + spin_unlock_irqrestore(&lp->lock, flags); + + return NETDEV_TX_OK; +} + + +/* The typical workload of the driver: + Handle the network interface interrupts. */ + +static irqreturn_t +eepro_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct eepro_local *lp; + int ioaddr, status, boguscount = 20; + int handled = 0; + + lp = netdev_priv(dev); + + spin_lock(&lp->lock); + + if (net_debug > 5) + printk(KERN_DEBUG "%s: entering eepro_interrupt routine.\n", dev->name); + + ioaddr = dev->base_addr; + + while (((status = inb(ioaddr + STATUS_REG)) & (RX_INT|TX_INT)) && (boguscount--)) + { + handled = 1; + if (status & RX_INT) { + if (net_debug > 4) + printk(KERN_DEBUG "%s: packet received interrupt.\n", dev->name); + + eepro_dis_int(ioaddr); + + /* Get the received packets */ + eepro_ack_rx(ioaddr); + eepro_rx(dev); + + eepro_en_int(ioaddr); + } + if (status & TX_INT) { + if (net_debug > 4) + printk(KERN_DEBUG "%s: packet transmit interrupt.\n", dev->name); + + + eepro_dis_int(ioaddr); + + /* Process the status of transmitted packets */ + eepro_ack_tx(ioaddr); + eepro_transmit_interrupt(dev); + + eepro_en_int(ioaddr); + } + } + + if (net_debug > 5) + printk(KERN_DEBUG "%s: exiting eepro_interrupt routine.\n", dev->name); + + spin_unlock(&lp->lock); + return IRQ_RETVAL(handled); +} + +static int eepro_close(struct net_device *dev) +{ + struct eepro_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + short temp_reg; + + netif_stop_queue(dev); + + eepro_sw2bank1(ioaddr); /* Switch back to Bank 1 */ + + /* Disable the physical interrupt line. */ + temp_reg = inb(ioaddr + REG1); + outb(temp_reg & 0x7f, ioaddr + REG1); + + eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */ + + /* Flush the Tx and disable Rx. */ + outb(STOP_RCV_CMD, ioaddr); + lp->tx_start = lp->tx_end = lp->xmt_lower_limit; + lp->tx_last = 0; + + /* Mask all the interrupts. */ + eepro_dis_int(ioaddr); + + /* clear all interrupts */ + eepro_clear_int(ioaddr); + + /* Reset the 82595 */ + eepro_reset(ioaddr); + + /* release the interrupt */ + free_irq(dev->irq, dev); + + /* Update the statistics here. What statistics? */ + + return 0; +} + +/* Set or clear the multicast filter for this adaptor. + */ +static void +set_multicast_list(struct net_device *dev) +{ + struct eepro_local *lp = netdev_priv(dev); + short ioaddr = dev->base_addr; + unsigned short mode; + struct netdev_hw_addr *ha; + int mc_count = netdev_mc_count(dev); + + if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || mc_count > 63) + { + eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */ + mode = inb(ioaddr + REG2); + outb(mode | PRMSC_Mode, ioaddr + REG2); + mode = inb(ioaddr + REG3); + outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ + eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */ + } + + else if (mc_count == 0) + { + eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */ + mode = inb(ioaddr + REG2); + outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */ + mode = inb(ioaddr + REG3); + outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ + eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */ + } + + else + { + unsigned short status, *eaddrs; + int i, boguscount = 0; + + /* Disable RX and TX interrupts. Necessary to avoid + corruption of the HOST_ADDRESS_REG by interrupt + service routines. */ + eepro_dis_int(ioaddr); + + eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */ + mode = inb(ioaddr + REG2); + outb(mode | Multi_IA, ioaddr + REG2); + mode = inb(ioaddr + REG3); + outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ + eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */ + outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG); + outw(MC_SETUP, ioaddr + IO_PORT); + outw(0, ioaddr + IO_PORT); + outw(0, ioaddr + IO_PORT); + outw(6 * (mc_count + 1), ioaddr + IO_PORT); + + netdev_for_each_mc_addr(ha, dev) { + eaddrs = (unsigned short *) ha->addr; + outw(*eaddrs++, ioaddr + IO_PORT); + outw(*eaddrs++, ioaddr + IO_PORT); + outw(*eaddrs++, ioaddr + IO_PORT); + } + + eaddrs = (unsigned short *) dev->dev_addr; + outw(eaddrs[0], ioaddr + IO_PORT); + outw(eaddrs[1], ioaddr + IO_PORT); + outw(eaddrs[2], ioaddr + IO_PORT); + outw(lp->tx_end, ioaddr + lp->xmt_bar); + outb(MC_SETUP, ioaddr); + + /* Update the transmit queue */ + i = lp->tx_end + XMT_HEADER + 6 * (mc_count + 1); + + if (lp->tx_start != lp->tx_end) + { + /* update the next address and the chain bit in the + last packet */ + outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG); + outw(i, ioaddr + IO_PORT); + outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG); + status = inw(ioaddr + IO_PORT); + outw(status | CHAIN_BIT, ioaddr + IO_PORT); + lp->tx_end = i ; + } + else { + lp->tx_start = lp->tx_end = i ; + } + + /* Acknowledge that the MC setup is done */ + do { /* We should be doing this in the eepro_interrupt()! */ + SLOW_DOWN; + SLOW_DOWN; + if (inb(ioaddr + STATUS_REG) & 0x08) + { + i = inb(ioaddr); + outb(0x08, ioaddr + STATUS_REG); + + if (i & 0x20) { /* command ABORTed */ + printk(KERN_NOTICE "%s: multicast setup failed.\n", + dev->name); + break; + } else if ((i & 0x0f) == 0x03) { /* MC-Done */ + printk(KERN_DEBUG "%s: set Rx mode to %d address%s.\n", + dev->name, mc_count, + mc_count > 1 ? "es":""); + break; + } + } + } while (++boguscount < 100); + + /* Re-enable RX and TX interrupts */ + eepro_en_int(ioaddr); + } + if (lp->eepro == LAN595FX_10ISA) { + eepro_complete_selreset(ioaddr); + } + else + eepro_en_rx(ioaddr); +} + +/* The horrible routine to read a word from the serial EEPROM. */ +/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */ + +/* The delay between EEPROM clock transitions. */ +#define eeprom_delay() { udelay(40); } +#define EE_READ_CMD (6 << 6) + +static int +read_eeprom(int ioaddr, int location, struct net_device *dev) +{ + int i; + unsigned short retval = 0; + struct eepro_local *lp = netdev_priv(dev); + short ee_addr = ioaddr + lp->eeprom_reg; + int read_cmd = location | EE_READ_CMD; + short ctrl_val = EECS ; + + /* XXXX - black magic */ + eepro_sw2bank1(ioaddr); + outb(0x00, ioaddr + STATUS_REG); + /* XXXX - black magic */ + + eepro_sw2bank2(ioaddr); + outb(ctrl_val, ee_addr); + + /* Shift the read command bits out. */ + for (i = 8; i >= 0; i--) { + short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI + : ctrl_val; + outb(outval, ee_addr); + outb(outval | EESK, ee_addr); /* EEPROM clock tick. */ + eeprom_delay(); + outb(outval, ee_addr); /* Finish EEPROM a clock tick. */ + eeprom_delay(); + } + outb(ctrl_val, ee_addr); + + for (i = 16; i > 0; i--) { + outb(ctrl_val | EESK, ee_addr); eeprom_delay(); + retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); + outb(ctrl_val, ee_addr); eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + ctrl_val &= ~EECS; + outb(ctrl_val | EESK, ee_addr); + eeprom_delay(); + outb(ctrl_val, ee_addr); + eeprom_delay(); + eepro_sw2bank0(ioaddr); + return retval; +} + +static int +hardware_send_packet(struct net_device *dev, void *buf, short length) +{ + struct eepro_local *lp = netdev_priv(dev); + short ioaddr = dev->base_addr; + unsigned status, tx_available, last, end; + + if (net_debug > 5) + printk(KERN_DEBUG "%s: entering hardware_send_packet routine.\n", dev->name); + + /* determine how much of the transmit buffer space is available */ + if (lp->tx_end > lp->tx_start) + tx_available = lp->xmt_ram - (lp->tx_end - lp->tx_start); + else if (lp->tx_end < lp->tx_start) + tx_available = lp->tx_start - lp->tx_end; + else tx_available = lp->xmt_ram; + + if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER) >= tx_available) { + /* No space available ??? */ + return 1; + } + + last = lp->tx_end; + end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; + + if (end >= lp->xmt_upper_limit + 2) { /* the transmit buffer is wrapped around */ + if ((lp->xmt_upper_limit + 2 - last) <= XMT_HEADER) { + /* Arrrr!!!, must keep the xmt header together, + several days were lost to chase this one down. */ + last = lp->xmt_lower_limit; + end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; + } + else end = lp->xmt_lower_limit + (end - + lp->xmt_upper_limit + 2); + } + + outw(last, ioaddr + HOST_ADDRESS_REG); + outw(XMT_CMD, ioaddr + IO_PORT); + outw(0, ioaddr + IO_PORT); + outw(end, ioaddr + IO_PORT); + outw(length, ioaddr + IO_PORT); + + if (lp->version == LAN595) + outsw(ioaddr + IO_PORT, buf, (length + 3) >> 1); + else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */ + unsigned short temp = inb(ioaddr + INT_MASK_REG); + outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG); + outsl(ioaddr + IO_PORT_32_BIT, buf, (length + 3) >> 2); + outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG); + } + + /* A dummy read to flush the DRAM write pipeline */ + status = inw(ioaddr + IO_PORT); + + if (lp->tx_start == lp->tx_end) { + outw(last, ioaddr + lp->xmt_bar); + outb(XMT_CMD, ioaddr); + lp->tx_start = last; /* I don't like to change tx_start here */ + } + else { + /* update the next address and the chain bit in the + last packet */ + + if (lp->tx_end != last) { + outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG); + outw(last, ioaddr + IO_PORT); + } + + outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG); + status = inw(ioaddr + IO_PORT); + outw(status | CHAIN_BIT, ioaddr + IO_PORT); + + /* Continue the transmit command */ + outb(RESUME_XMT_CMD, ioaddr); + } + + lp->tx_last = last; + lp->tx_end = end; + + if (net_debug > 5) + printk(KERN_DEBUG "%s: exiting hardware_send_packet routine.\n", dev->name); + + return 0; +} + +static void +eepro_rx(struct net_device *dev) +{ + struct eepro_local *lp = netdev_priv(dev); + short ioaddr = dev->base_addr; + short boguscount = 20; + short rcv_car = lp->rx_start; + unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size; + + if (net_debug > 5) + printk(KERN_DEBUG "%s: entering eepro_rx routine.\n", dev->name); + + /* Set the read pointer to the start of the RCV */ + outw(rcv_car, ioaddr + HOST_ADDRESS_REG); + + rcv_event = inw(ioaddr + IO_PORT); + + while (rcv_event == RCV_DONE) { + + rcv_status = inw(ioaddr + IO_PORT); + rcv_next_frame = inw(ioaddr + IO_PORT); + rcv_size = inw(ioaddr + IO_PORT); + + if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) { + + /* Malloc up new buffer. */ + struct sk_buff *skb; + + dev->stats.rx_bytes+=rcv_size; + rcv_size &= 0x3fff; + skb = dev_alloc_skb(rcv_size+5); + if (skb == NULL) { + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); + dev->stats.rx_dropped++; + rcv_car = lp->rx_start + RCV_HEADER + rcv_size; + lp->rx_start = rcv_next_frame; + outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG); + + break; + } + skb_reserve(skb,2); + + if (lp->version == LAN595) + insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1); + else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */ + unsigned short temp = inb(ioaddr + INT_MASK_REG); + outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG); + insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size), + (rcv_size + 3) >> 2); + outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG); + } + + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + } + + else { /* Not sure will ever reach here, + I set the 595 to discard bad received frames */ + dev->stats.rx_errors++; + + if (rcv_status & 0x0100) + dev->stats.rx_over_errors++; + + else if (rcv_status & 0x0400) + dev->stats.rx_frame_errors++; + + else if (rcv_status & 0x0800) + dev->stats.rx_crc_errors++; + + printk(KERN_DEBUG "%s: event = %#x, status = %#x, next = %#x, size = %#x\n", + dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size); + } + + if (rcv_status & 0x1000) + dev->stats.rx_length_errors++; + + rcv_car = lp->rx_start + RCV_HEADER + rcv_size; + lp->rx_start = rcv_next_frame; + + if (--boguscount == 0) + break; + + outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG); + rcv_event = inw(ioaddr + IO_PORT); + + } + if (rcv_car == 0) + rcv_car = lp->rcv_upper_limit | 0xff; + + outw(rcv_car - 1, ioaddr + RCV_STOP); + + if (net_debug > 5) + printk(KERN_DEBUG "%s: exiting eepro_rx routine.\n", dev->name); +} + +static void +eepro_transmit_interrupt(struct net_device *dev) +{ + struct eepro_local *lp = netdev_priv(dev); + short ioaddr = dev->base_addr; + short boguscount = 25; + short xmt_status; + + while ((lp->tx_start != lp->tx_end) && boguscount--) { + + outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG); + xmt_status = inw(ioaddr+IO_PORT); + + if (!(xmt_status & TX_DONE_BIT)) + break; + + xmt_status = inw(ioaddr+IO_PORT); + lp->tx_start = inw(ioaddr+IO_PORT); + + netif_wake_queue (dev); + + if (xmt_status & TX_OK) + dev->stats.tx_packets++; + else { + dev->stats.tx_errors++; + if (xmt_status & 0x0400) { + dev->stats.tx_carrier_errors++; + printk(KERN_DEBUG "%s: carrier error\n", + dev->name); + printk(KERN_DEBUG "%s: XMT status = %#x\n", + dev->name, xmt_status); + } + else { + printk(KERN_DEBUG "%s: XMT status = %#x\n", + dev->name, xmt_status); + printk(KERN_DEBUG "%s: XMT status = %#x\n", + dev->name, xmt_status); + } + } + if (xmt_status & 0x000f) { + dev->stats.collisions += (xmt_status & 0x000f); + } + + if ((xmt_status & 0x0040) == 0x0) { + dev->stats.tx_heartbeat_errors++; + } + } +} + +static int eepro_ethtool_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct eepro_local *lp = netdev_priv(dev); + + cmd->supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_Autoneg; + cmd->advertising = ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_Autoneg; + + if (GetBit(lp->word[5], ee_PortTPE)) { + cmd->supported |= SUPPORTED_TP; + cmd->advertising |= ADVERTISED_TP; + } + if (GetBit(lp->word[5], ee_PortBNC)) { + cmd->supported |= SUPPORTED_BNC; + cmd->advertising |= ADVERTISED_BNC; + } + if (GetBit(lp->word[5], ee_PortAUI)) { + cmd->supported |= SUPPORTED_AUI; + cmd->advertising |= ADVERTISED_AUI; + } + + ethtool_cmd_speed_set(cmd, SPEED_10); + + if (dev->if_port == TPE && lp->word[1] & ee_Duplex) { + cmd->duplex = DUPLEX_FULL; + } + else { + cmd->duplex = DUPLEX_HALF; + } + + cmd->port = dev->if_port; + cmd->phy_address = dev->base_addr; + cmd->transceiver = XCVR_INTERNAL; + + if (lp->word[0] & ee_AutoNeg) { + cmd->autoneg = 1; + } + + return 0; +} + +static void eepro_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + strcpy(drvinfo->driver, DRV_NAME); + strcpy(drvinfo->version, DRV_VERSION); + sprintf(drvinfo->bus_info, "ISA 0x%lx", dev->base_addr); +} + +static const struct ethtool_ops eepro_ethtool_ops = { + .get_settings = eepro_ethtool_get_settings, + .get_drvinfo = eepro_ethtool_get_drvinfo, +}; + +#ifdef MODULE + +#define MAX_EEPRO 8 +static struct net_device *dev_eepro[MAX_EEPRO]; + +static int io[MAX_EEPRO] = { + [0 ... MAX_EEPRO-1] = -1 +}; +static int irq[MAX_EEPRO]; +static int mem[MAX_EEPRO] = { /* Size of the rx buffer in KB */ + [0 ... MAX_EEPRO-1] = RCV_DEFAULT_RAM/1024 +}; +static int autodetect; + +static int n_eepro; +/* For linux 2.1.xx */ + +MODULE_AUTHOR("Pascal Dupuis and others"); +MODULE_DESCRIPTION("Intel i82595 ISA EtherExpressPro10/10+ driver"); +MODULE_LICENSE("GPL"); + +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param_array(mem, int, NULL, 0); +module_param(autodetect, int, 0); +MODULE_PARM_DESC(io, "EtherExpress Pro/10 I/O base address(es)"); +MODULE_PARM_DESC(irq, "EtherExpress Pro/10 IRQ number(s)"); +MODULE_PARM_DESC(mem, "EtherExpress Pro/10 Rx buffer size(es) in kB (3-29)"); +MODULE_PARM_DESC(autodetect, "EtherExpress Pro/10 force board(s) detection (0-1)"); + +int __init init_module(void) +{ + struct net_device *dev; + int i; + if (io[0] == -1 && autodetect == 0) { + printk(KERN_WARNING "eepro_init_module: Probe is very dangerous in ISA boards!\n"); + printk(KERN_WARNING "eepro_init_module: Please add \"autodetect=1\" to force probe\n"); + return -ENODEV; + } + else if (autodetect) { + /* if autodetect is set then we must force detection */ + for (i = 0; i < MAX_EEPRO; i++) { + io[i] = 0; + } + + printk(KERN_INFO "eepro_init_module: Auto-detecting boards (May God protect us...)\n"); + } + + for (i = 0; i < MAX_EEPRO && io[i] != -1; i++) { + dev = alloc_etherdev(sizeof(struct eepro_local)); + if (!dev) + break; + + dev->mem_end = mem[i]; + dev->base_addr = io[i]; + dev->irq = irq[i]; + + if (do_eepro_probe(dev) == 0) { + dev_eepro[n_eepro++] = dev; + continue; + } + free_netdev(dev); + break; + } + + if (n_eepro) + printk(KERN_INFO "%s", version); + + return n_eepro ? 0 : -ENODEV; +} + +void __exit +cleanup_module(void) +{ + int i; + + for (i=0; i<n_eepro; i++) { + struct net_device *dev = dev_eepro[i]; + unregister_netdev(dev); + release_region(dev->base_addr, EEPRO_IO_EXTENT); + free_netdev(dev); + } +} +#endif /* MODULE */ diff --git a/drivers/net/ethernet/i825xx/eexpress.c b/drivers/net/ethernet/i825xx/eexpress.c new file mode 100644 index 000000000000..a19228563efd --- /dev/null +++ b/drivers/net/ethernet/i825xx/eexpress.c @@ -0,0 +1,1720 @@ +/* Intel EtherExpress 16 device driver for Linux + * + * Written by John Sullivan, 1995 + * based on original code by Donald Becker, with changes by + * Alan Cox and Pauline Middelink. + * + * Support for 8-bit mode by Zoltan Szilagyi <zoltans@cs.arizona.edu> + * + * Many modifications, and currently maintained, by + * Philip Blundell <philb@gnu.org> + * Added the Compaq LTE Alan Cox <alan@lxorguk.ukuu.org.uk> + * Added MCA support Adam Fritzler + * + * Note - this driver is experimental still - it has problems on faster + * machines. Someone needs to sit down and go through it line by line with + * a databook... + */ + +/* The EtherExpress 16 is a fairly simple card, based on a shared-memory + * design using the i82586 Ethernet coprocessor. It bears no relationship, + * as far as I know, to the similarly-named "EtherExpress Pro" range. + * + * Historically, Linux support for these cards has been very bad. However, + * things seem to be getting better slowly. + */ + +/* If your card is confused about what sort of interface it has (eg it + * persistently reports "10baseT" when none is fitted), running 'SOFTSET /BART' + * or 'SOFTSET /LISA' from DOS seems to help. + */ + +/* Here's the scoop on memory mapping. + * + * There are three ways to access EtherExpress card memory: either using the + * shared-memory mapping, or using PIO through the dataport, or using PIO + * through the "shadow memory" ports. + * + * The shadow memory system works by having the card map some of its memory + * as follows: + * + * (the low five bits of the SMPTR are ignored) + * + * base+0x4000..400f memory at SMPTR+0..15 + * base+0x8000..800f memory at SMPTR+16..31 + * base+0xc000..c007 dubious stuff (memory at SMPTR+16..23 apparently) + * base+0xc008..c00f memory at 0x0008..0x000f + * + * This last set (the one at c008) is particularly handy because the SCB + * lives at 0x0008. So that set of ports gives us easy random access to data + * in the SCB without having to mess around setting up pointers and the like. + * We always use this method to access the SCB (via the scb_xx() functions). + * + * Dataport access works by aiming the appropriate (read or write) pointer + * at the first address you're interested in, and then reading or writing from + * the dataport. The pointers auto-increment after each transfer. We use + * this for data transfer. + * + * We don't use the shared-memory system because it allegedly doesn't work on + * all cards, and because it's a bit more prone to go wrong (it's one more + * thing to configure...). + */ + +/* Known bugs: + * + * - The card seems to want to give us two interrupts every time something + * happens, where just one would be better. + */ + +/* + * + * Note by Zoltan Szilagyi 10-12-96: + * + * I've succeeded in eliminating the "CU wedged" messages, and hence the + * lockups, which were only occurring with cards running in 8-bit mode ("force + * 8-bit operation" in Intel's SoftSet utility). This version of the driver + * sets the 82586 and the ASIC to 8-bit mode at startup; it also stops the + * CU before submitting a packet for transmission, and then restarts it as soon + * as the process of handing the packet is complete. This is definitely an + * unnecessary slowdown if the card is running in 16-bit mode; therefore one + * should detect 16-bit vs 8-bit mode from the EEPROM settings and act + * accordingly. In 8-bit mode with this bugfix I'm getting about 150 K/s for + * ftp's, which is significantly better than I get in DOS, so the overhead of + * stopping and restarting the CU with each transmit is not prohibitive in + * practice. + * + * Update by David Woodhouse 11/5/99: + * + * I've seen "CU wedged" messages in 16-bit mode, on the Alpha architecture. + * I assume that this is because 16-bit accesses are actually handled as two + * 8-bit accesses. + */ + +#ifdef __alpha__ +#define LOCKUP16 1 +#endif +#ifndef LOCKUP16 +#define LOCKUP16 0 +#endif + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/string.h> +#include <linux/in.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/mca-legacy.h> +#include <linux/spinlock.h> +#include <linux/bitops.h> +#include <linux/jiffies.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> + +#ifndef NET_DEBUG +#define NET_DEBUG 4 +#endif + +#include "eexpress.h" + +#define EEXP_IO_EXTENT 16 + +/* + * Private data declarations + */ + +struct net_local +{ + unsigned long last_tx; /* jiffies when last transmit started */ + unsigned long init_time; /* jiffies when eexp_hw_init586 called */ + unsigned short rx_first; /* first rx buf, same as RX_BUF_START */ + unsigned short rx_last; /* last rx buf */ + unsigned short rx_ptr; /* first rx buf to look at */ + unsigned short tx_head; /* next free tx buf */ + unsigned short tx_reap; /* first in-use tx buf */ + unsigned short tx_tail; /* previous tx buf to tx_head */ + unsigned short tx_link; /* last known-executing tx buf */ + unsigned short last_tx_restart; /* set to tx_link when we + restart the CU */ + unsigned char started; + unsigned short rx_buf_start; + unsigned short rx_buf_end; + unsigned short num_tx_bufs; + unsigned short num_rx_bufs; + unsigned char width; /* 0 for 16bit, 1 for 8bit */ + unsigned char was_promisc; + unsigned char old_mc_count; + spinlock_t lock; +}; + +/* This is the code and data that is downloaded to the EtherExpress card's + * memory at boot time. + */ + +static unsigned short start_code[] = { +/* 0x0000 */ + 0x0001, /* ISCP: busy - cleared after reset */ + 0x0008,0x0000,0x0000, /* offset,address (lo,hi) of SCB */ + + 0x0000,0x0000, /* SCB: status, commands */ + 0x0000,0x0000, /* links to first command block, + first receive descriptor */ + 0x0000,0x0000, /* CRC error, alignment error counts */ + 0x0000,0x0000, /* out of resources, overrun error counts */ + + 0x0000,0x0000, /* pad */ + 0x0000,0x0000, + +/* 0x20 -- start of 82586 CU program */ +#define CONF_LINK 0x20 + 0x0000,Cmd_Config, + 0x0032, /* link to next command */ + 0x080c, /* 12 bytes follow : fifo threshold=8 */ + 0x2e40, /* don't rx bad frames + * SRDY/ARDY => ext. sync. : preamble len=8 + * take addresses from data buffers + * 6 bytes/address + */ + 0x6000, /* default backoff method & priority + * interframe spacing = 0x60 */ + 0xf200, /* slot time=0x200 + * max collision retry = 0xf */ +#define CONF_PROMISC 0x2e + 0x0000, /* no HDLC : normal CRC : enable broadcast + * disable promiscuous/multicast modes */ + 0x003c, /* minimum frame length = 60 octets) */ + + 0x0000,Cmd_SetAddr, + 0x003e, /* link to next command */ +#define CONF_HWADDR 0x38 + 0x0000,0x0000,0x0000, /* hardware address placed here */ + + 0x0000,Cmd_MCast, + 0x0076, /* link to next command */ +#define CONF_NR_MULTICAST 0x44 + 0x0000, /* number of bytes in multicast address(es) */ +#define CONF_MULTICAST 0x46 + 0x0000, 0x0000, 0x0000, /* some addresses */ + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + +#define CONF_DIAG_RESULT 0x76 + 0x0000, Cmd_Diag, + 0x007c, /* link to next command */ + + 0x0000,Cmd_TDR|Cmd_INT, + 0x0084, +#define CONF_TDR_RESULT 0x82 + 0x0000, + + 0x0000,Cmd_END|Cmd_Nop, /* end of configure sequence */ + 0x0084 /* dummy link */ +}; + +/* maps irq number to EtherExpress magic value */ +static char irqrmap[] = { 0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0 }; + +#ifdef CONFIG_MCA_LEGACY +/* mapping of the first four bits of the second POS register */ +static unsigned short mca_iomap[] = { + 0x270, 0x260, 0x250, 0x240, 0x230, 0x220, 0x210, 0x200, + 0x370, 0x360, 0x350, 0x340, 0x330, 0x320, 0x310, 0x300 +}; +/* bits 5-7 of the second POS register */ +static char mca_irqmap[] = { 12, 9, 3, 4, 5, 10, 11, 15 }; +#endif + +/* + * Prototypes for Linux interface + */ + +static int eexp_open(struct net_device *dev); +static int eexp_close(struct net_device *dev); +static void eexp_timeout(struct net_device *dev); +static netdev_tx_t eexp_xmit(struct sk_buff *buf, + struct net_device *dev); + +static irqreturn_t eexp_irq(int irq, void *dev_addr); +static void eexp_set_multicast(struct net_device *dev); + +/* + * Prototypes for hardware access functions + */ + +static void eexp_hw_rx_pio(struct net_device *dev); +static void eexp_hw_tx_pio(struct net_device *dev, unsigned short *buf, + unsigned short len); +static int eexp_hw_probe(struct net_device *dev,unsigned short ioaddr); +static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, + unsigned char location); + +static unsigned short eexp_hw_lasttxstat(struct net_device *dev); +static void eexp_hw_txrestart(struct net_device *dev); + +static void eexp_hw_txinit (struct net_device *dev); +static void eexp_hw_rxinit (struct net_device *dev); + +static void eexp_hw_init586 (struct net_device *dev); +static void eexp_setup_filter (struct net_device *dev); + +static char *eexp_ifmap[]={"AUI", "BNC", "RJ45"}; +enum eexp_iftype {AUI=0, BNC=1, TPE=2}; + +#define STARTED_RU 2 +#define STARTED_CU 1 + +/* + * Primitive hardware access functions. + */ + +static inline unsigned short scb_status(struct net_device *dev) +{ + return inw(dev->base_addr + 0xc008); +} + +static inline unsigned short scb_rdcmd(struct net_device *dev) +{ + return inw(dev->base_addr + 0xc00a); +} + +static inline void scb_command(struct net_device *dev, unsigned short cmd) +{ + outw(cmd, dev->base_addr + 0xc00a); +} + +static inline void scb_wrcbl(struct net_device *dev, unsigned short val) +{ + outw(val, dev->base_addr + 0xc00c); +} + +static inline void scb_wrrfa(struct net_device *dev, unsigned short val) +{ + outw(val, dev->base_addr + 0xc00e); +} + +static inline void set_loopback(struct net_device *dev) +{ + outb(inb(dev->base_addr + Config) | 2, dev->base_addr + Config); +} + +static inline void clear_loopback(struct net_device *dev) +{ + outb(inb(dev->base_addr + Config) & ~2, dev->base_addr + Config); +} + +static inline unsigned short int SHADOW(short int addr) +{ + addr &= 0x1f; + if (addr > 0xf) addr += 0x3ff0; + return addr + 0x4000; +} + +/* + * Linux interface + */ + +/* + * checks for presence of EtherExpress card + */ + +static int __init do_express_probe(struct net_device *dev) +{ + unsigned short *port; + static unsigned short ports[] = { 0x240,0x300,0x310,0x270,0x320,0x340,0 }; + unsigned short ioaddr = dev->base_addr; + int dev_irq = dev->irq; + int err; + + dev->if_port = 0xff; /* not set */ + +#ifdef CONFIG_MCA_LEGACY + if (MCA_bus) { + int slot = 0; + + /* + * Only find one card at a time. Subsequent calls + * will find others, however, proper multicard MCA + * probing and setup can't be done with the + * old-style Space.c init routines. -- ASF + */ + while (slot != MCA_NOTFOUND) { + int pos0, pos1; + + slot = mca_find_unused_adapter(0x628B, slot); + if (slot == MCA_NOTFOUND) + break; + + pos0 = mca_read_stored_pos(slot, 2); + pos1 = mca_read_stored_pos(slot, 3); + ioaddr = mca_iomap[pos1&0xf]; + + dev->irq = mca_irqmap[(pos1>>4)&0x7]; + + /* + * XXX: Transceiver selection is done + * differently on the MCA version. + * How to get it to select something + * other than external/AUI is currently + * unknown. This code is just for looks. -- ASF + */ + if ((pos0 & 0x7) == 0x1) + dev->if_port = AUI; + else if ((pos0 & 0x7) == 0x5) { + if (pos1 & 0x80) + dev->if_port = BNC; + else + dev->if_port = TPE; + } + + mca_set_adapter_name(slot, "Intel EtherExpress 16 MCA"); + mca_set_adapter_procfn(slot, NULL, dev); + mca_mark_as_used(slot); + + break; + } + } +#endif + if (ioaddr&0xfe00) { + if (!request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress")) + return -EBUSY; + err = eexp_hw_probe(dev,ioaddr); + release_region(ioaddr, EEXP_IO_EXTENT); + return err; + } else if (ioaddr) + return -ENXIO; + + for (port=&ports[0] ; *port ; port++ ) + { + unsigned short sum = 0; + int i; + if (!request_region(*port, EEXP_IO_EXTENT, "EtherExpress")) + continue; + for ( i=0 ; i<4 ; i++ ) + { + unsigned short t; + t = inb(*port + ID_PORT); + sum |= (t>>4) << ((t & 0x03)<<2); + } + if (sum==0xbaba && !eexp_hw_probe(dev,*port)) { + release_region(*port, EEXP_IO_EXTENT); + return 0; + } + release_region(*port, EEXP_IO_EXTENT); + dev->irq = dev_irq; + } + return -ENODEV; +} + +#ifndef MODULE +struct net_device * __init express_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); + int err; + + if (!dev) + return ERR_PTR(-ENOMEM); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = do_express_probe(dev); + if (!err) + return dev; + free_netdev(dev); + return ERR_PTR(err); +} +#endif + +/* + * open and initialize the adapter, ready for use + */ + +static int eexp_open(struct net_device *dev) +{ + int ret; + unsigned short ioaddr = dev->base_addr; + struct net_local *lp = netdev_priv(dev); + +#if NET_DEBUG > 6 + printk(KERN_DEBUG "%s: eexp_open()\n", dev->name); +#endif + + if (!dev->irq || !irqrmap[dev->irq]) + return -ENXIO; + + ret = request_irq(dev->irq, eexp_irq, 0, dev->name, dev); + if (ret) + return ret; + + if (!request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress")) { + printk(KERN_WARNING "EtherExpress io port %x, is busy.\n" + , ioaddr); + goto err_out1; + } + if (!request_region(ioaddr+0x4000, EEXP_IO_EXTENT, "EtherExpress shadow")) { + printk(KERN_WARNING "EtherExpress io port %x, is busy.\n" + , ioaddr+0x4000); + goto err_out2; + } + if (!request_region(ioaddr+0x8000, EEXP_IO_EXTENT, "EtherExpress shadow")) { + printk(KERN_WARNING "EtherExpress io port %x, is busy.\n" + , ioaddr+0x8000); + goto err_out3; + } + if (!request_region(ioaddr+0xc000, EEXP_IO_EXTENT, "EtherExpress shadow")) { + printk(KERN_WARNING "EtherExpress io port %x, is busy.\n" + , ioaddr+0xc000); + goto err_out4; + } + + if (lp->width) { + printk("%s: forcing ASIC to 8-bit mode\n", dev->name); + outb(inb(dev->base_addr+Config)&~4, dev->base_addr+Config); + } + + eexp_hw_init586(dev); + netif_start_queue(dev); +#if NET_DEBUG > 6 + printk(KERN_DEBUG "%s: leaving eexp_open()\n", dev->name); +#endif + return 0; + + err_out4: + release_region(ioaddr+0x8000, EEXP_IO_EXTENT); + err_out3: + release_region(ioaddr+0x4000, EEXP_IO_EXTENT); + err_out2: + release_region(ioaddr, EEXP_IO_EXTENT); + err_out1: + free_irq(dev->irq, dev); + return -EBUSY; +} + +/* + * close and disable the interface, leaving the 586 in reset. + */ + +static int eexp_close(struct net_device *dev) +{ + unsigned short ioaddr = dev->base_addr; + struct net_local *lp = netdev_priv(dev); + + int irq = dev->irq; + + netif_stop_queue(dev); + + outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ); + lp->started = 0; + scb_command(dev, SCB_CUsuspend|SCB_RUsuspend); + outb(0,ioaddr+SIGNAL_CA); + free_irq(irq,dev); + outb(i586_RST,ioaddr+EEPROM_Ctrl); + release_region(ioaddr, EEXP_IO_EXTENT); + release_region(ioaddr+0x4000, 16); + release_region(ioaddr+0x8000, 16); + release_region(ioaddr+0xc000, 16); + + return 0; +} + +/* + * This gets called when a higher level thinks we are broken. Check that + * nothing has become jammed in the CU. + */ + +static void unstick_cu(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned short ioaddr = dev->base_addr; + + if (lp->started) + { + if (time_after(jiffies, dev_trans_start(dev) + HZ/2)) + { + if (lp->tx_link==lp->last_tx_restart) + { + unsigned short boguscount=200,rsst; + printk(KERN_WARNING "%s: Retransmit timed out, status %04x, resetting...\n", + dev->name, scb_status(dev)); + eexp_hw_txinit(dev); + lp->last_tx_restart = 0; + scb_wrcbl(dev, lp->tx_link); + scb_command(dev, SCB_CUstart); + outb(0,ioaddr+SIGNAL_CA); + while (!SCB_complete(rsst=scb_status(dev))) + { + if (!--boguscount) + { + boguscount=200; + printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n", + dev->name,rsst); + scb_wrcbl(dev, lp->tx_link); + scb_command(dev, SCB_CUstart); + outb(0,ioaddr+SIGNAL_CA); + } + } + netif_wake_queue(dev); + } + else + { + unsigned short status = scb_status(dev); + if (SCB_CUdead(status)) + { + unsigned short txstatus = eexp_hw_lasttxstat(dev); + printk(KERN_WARNING "%s: Transmit timed out, CU not active status %04x %04x, restarting...\n", + dev->name, status, txstatus); + eexp_hw_txrestart(dev); + } + else + { + unsigned short txstatus = eexp_hw_lasttxstat(dev); + if (netif_queue_stopped(dev) && !txstatus) + { + printk(KERN_WARNING "%s: CU wedged, status %04x %04x, resetting...\n", + dev->name,status,txstatus); + eexp_hw_init586(dev); + netif_wake_queue(dev); + } + else + { + printk(KERN_WARNING "%s: transmit timed out\n", dev->name); + } + } + } + } + } + else + { + if (time_after(jiffies, lp->init_time + 10)) + { + unsigned short status = scb_status(dev); + printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n", + dev->name, status); + eexp_hw_init586(dev); + netif_wake_queue(dev); + } + } +} + +static void eexp_timeout(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); +#ifdef CONFIG_SMP + unsigned long flags; +#endif + int status; + + disable_irq(dev->irq); + + /* + * Best would be to use synchronize_irq(); spin_lock() here + * lets make it work first.. + */ + +#ifdef CONFIG_SMP + spin_lock_irqsave(&lp->lock, flags); +#endif + + status = scb_status(dev); + unstick_cu(dev); + printk(KERN_INFO "%s: transmit timed out, %s?\n", dev->name, + (SCB_complete(status)?"lost interrupt": + "board on fire")); + dev->stats.tx_errors++; + lp->last_tx = jiffies; + if (!SCB_complete(status)) { + scb_command(dev, SCB_CUabort); + outb(0,dev->base_addr+SIGNAL_CA); + } + netif_wake_queue(dev); +#ifdef CONFIG_SMP + spin_unlock_irqrestore(&lp->lock, flags); +#endif +} + +/* + * Called to transmit a packet, or to allow us to right ourselves + * if the kernel thinks we've died. + */ +static netdev_tx_t eexp_xmit(struct sk_buff *buf, struct net_device *dev) +{ + short length = buf->len; +#ifdef CONFIG_SMP + struct net_local *lp = netdev_priv(dev); + unsigned long flags; +#endif + +#if NET_DEBUG > 6 + printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name); +#endif + + if (buf->len < ETH_ZLEN) { + if (skb_padto(buf, ETH_ZLEN)) + return NETDEV_TX_OK; + length = ETH_ZLEN; + } + + disable_irq(dev->irq); + + /* + * Best would be to use synchronize_irq(); spin_lock() here + * lets make it work first.. + */ + +#ifdef CONFIG_SMP + spin_lock_irqsave(&lp->lock, flags); +#endif + + { + unsigned short *data = (unsigned short *)buf->data; + + dev->stats.tx_bytes += length; + + eexp_hw_tx_pio(dev,data,length); + } + dev_kfree_skb(buf); +#ifdef CONFIG_SMP + spin_unlock_irqrestore(&lp->lock, flags); +#endif + enable_irq(dev->irq); + return NETDEV_TX_OK; +} + +/* + * Handle an EtherExpress interrupt + * If we've finished initializing, start the RU and CU up. + * If we've already started, reap tx buffers, handle any received packets, + * check to make sure we've not become wedged. + */ + +static unsigned short eexp_start_irq(struct net_device *dev, + unsigned short status) +{ + unsigned short ack_cmd = SCB_ack(status); + struct net_local *lp = netdev_priv(dev); + unsigned short ioaddr = dev->base_addr; + if ((dev->flags & IFF_UP) && !(lp->started & STARTED_CU)) { + short diag_status, tdr_status; + while (SCB_CUstat(status)==2) + status = scb_status(dev); +#if NET_DEBUG > 4 + printk("%s: CU went non-active (status %04x)\n", + dev->name, status); +#endif + + outw(CONF_DIAG_RESULT & ~31, ioaddr + SM_PTR); + diag_status = inw(ioaddr + SHADOW(CONF_DIAG_RESULT)); + if (diag_status & 1<<11) { + printk(KERN_WARNING "%s: 82586 failed self-test\n", + dev->name); + } else if (!(diag_status & 1<<13)) { + printk(KERN_WARNING "%s: 82586 self-test failed to complete\n", dev->name); + } + + outw(CONF_TDR_RESULT & ~31, ioaddr + SM_PTR); + tdr_status = inw(ioaddr + SHADOW(CONF_TDR_RESULT)); + if (tdr_status & (TDR_SHORT|TDR_OPEN)) { + printk(KERN_WARNING "%s: TDR reports cable %s at %d tick%s\n", dev->name, (tdr_status & TDR_SHORT)?"short":"broken", tdr_status & TDR_TIME, ((tdr_status & TDR_TIME) != 1) ? "s" : ""); + } + else if (tdr_status & TDR_XCVRPROBLEM) { + printk(KERN_WARNING "%s: TDR reports transceiver problem\n", dev->name); + } + else if (tdr_status & TDR_LINKOK) { +#if NET_DEBUG > 4 + printk(KERN_DEBUG "%s: TDR reports link OK\n", dev->name); +#endif + } else { + printk("%s: TDR is ga-ga (status %04x)\n", dev->name, + tdr_status); + } + + lp->started |= STARTED_CU; + scb_wrcbl(dev, lp->tx_link); + /* if the RU isn't running, start it now */ + if (!(lp->started & STARTED_RU)) { + ack_cmd |= SCB_RUstart; + scb_wrrfa(dev, lp->rx_buf_start); + lp->rx_ptr = lp->rx_buf_start; + lp->started |= STARTED_RU; + } + ack_cmd |= SCB_CUstart | 0x2000; + } + + if ((dev->flags & IFF_UP) && !(lp->started & STARTED_RU) && SCB_RUstat(status)==4) + lp->started|=STARTED_RU; + + return ack_cmd; +} + +static void eexp_cmd_clear(struct net_device *dev) +{ + unsigned long int oldtime = jiffies; + while (scb_rdcmd(dev) && (time_before(jiffies, oldtime + 10))); + if (scb_rdcmd(dev)) { + printk("%s: command didn't clear\n", dev->name); + } +} + +static irqreturn_t eexp_irq(int dummy, void *dev_info) +{ + struct net_device *dev = dev_info; + struct net_local *lp; + unsigned short ioaddr,status,ack_cmd; + unsigned short old_read_ptr, old_write_ptr; + + lp = netdev_priv(dev); + ioaddr = dev->base_addr; + + spin_lock(&lp->lock); + + old_read_ptr = inw(ioaddr+READ_PTR); + old_write_ptr = inw(ioaddr+WRITE_PTR); + + outb(SIRQ_dis|irqrmap[dev->irq], ioaddr+SET_IRQ); + + status = scb_status(dev); + +#if NET_DEBUG > 4 + printk(KERN_DEBUG "%s: interrupt (status %x)\n", dev->name, status); +#endif + + if (lp->started == (STARTED_CU | STARTED_RU)) { + + do { + eexp_cmd_clear(dev); + + ack_cmd = SCB_ack(status); + scb_command(dev, ack_cmd); + outb(0,ioaddr+SIGNAL_CA); + + eexp_cmd_clear(dev); + + if (SCB_complete(status)) { + if (!eexp_hw_lasttxstat(dev)) { + printk("%s: tx interrupt but no status\n", dev->name); + } + } + + if (SCB_rxdframe(status)) + eexp_hw_rx_pio(dev); + + status = scb_status(dev); + } while (status & 0xc000); + + if (SCB_RUdead(status)) + { + printk(KERN_WARNING "%s: RU stopped: status %04x\n", + dev->name,status); +#if 0 + printk(KERN_WARNING "%s: cur_rfd=%04x, cur_rbd=%04x\n", dev->name, lp->cur_rfd, lp->cur_rbd); + outw(lp->cur_rfd, ioaddr+READ_PTR); + printk(KERN_WARNING "%s: [%04x]\n", dev->name, inw(ioaddr+DATAPORT)); + outw(lp->cur_rfd+6, ioaddr+READ_PTR); + printk(KERN_WARNING "%s: rbd is %04x\n", dev->name, rbd= inw(ioaddr+DATAPORT)); + outw(rbd, ioaddr+READ_PTR); + printk(KERN_WARNING "%s: [%04x %04x] ", dev->name, inw(ioaddr+DATAPORT), inw(ioaddr+DATAPORT)); + outw(rbd+8, ioaddr+READ_PTR); + printk("[%04x]\n", inw(ioaddr+DATAPORT)); +#endif + dev->stats.rx_errors++; +#if 1 + eexp_hw_rxinit(dev); +#else + lp->cur_rfd = lp->first_rfd; +#endif + scb_wrrfa(dev, lp->rx_buf_start); + scb_command(dev, SCB_RUstart); + outb(0,ioaddr+SIGNAL_CA); + } + } else { + if (status & 0x8000) + ack_cmd = eexp_start_irq(dev, status); + else + ack_cmd = SCB_ack(status); + scb_command(dev, ack_cmd); + outb(0,ioaddr+SIGNAL_CA); + } + + eexp_cmd_clear(dev); + + outb(SIRQ_en|irqrmap[dev->irq], ioaddr+SET_IRQ); + +#if NET_DEBUG > 6 + printk("%s: leaving eexp_irq()\n", dev->name); +#endif + outw(old_read_ptr, ioaddr+READ_PTR); + outw(old_write_ptr, ioaddr+WRITE_PTR); + + spin_unlock(&lp->lock); + return IRQ_HANDLED; +} + +/* + * Hardware access functions + */ + +/* + * Set the cable type to use. + */ + +static void eexp_hw_set_interface(struct net_device *dev) +{ + unsigned char oldval = inb(dev->base_addr + 0x300e); + oldval &= ~0x82; + switch (dev->if_port) { + case TPE: + oldval |= 0x2; + case BNC: + oldval |= 0x80; + break; + } + outb(oldval, dev->base_addr+0x300e); + mdelay(20); +} + +/* + * Check all the receive buffers, and hand any received packets + * to the upper levels. Basic sanity check on each frame + * descriptor, though we don't bother trying to fix broken ones. + */ + +static void eexp_hw_rx_pio(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned short rx_block = lp->rx_ptr; + unsigned short boguscount = lp->num_rx_bufs; + unsigned short ioaddr = dev->base_addr; + unsigned short status; + +#if NET_DEBUG > 6 + printk(KERN_DEBUG "%s: eexp_hw_rx()\n", dev->name); +#endif + + do { + unsigned short rfd_cmd, rx_next, pbuf, pkt_len; + + outw(rx_block, ioaddr + READ_PTR); + status = inw(ioaddr + DATAPORT); + + if (FD_Done(status)) + { + rfd_cmd = inw(ioaddr + DATAPORT); + rx_next = inw(ioaddr + DATAPORT); + pbuf = inw(ioaddr + DATAPORT); + + outw(pbuf, ioaddr + READ_PTR); + pkt_len = inw(ioaddr + DATAPORT); + + if (rfd_cmd!=0x0000) + { + printk(KERN_WARNING "%s: rfd_cmd not zero:0x%04x\n", + dev->name, rfd_cmd); + continue; + } + else if (pbuf!=rx_block+0x16) + { + printk(KERN_WARNING "%s: rfd and rbd out of sync 0x%04x 0x%04x\n", + dev->name, rx_block+0x16, pbuf); + continue; + } + else if ((pkt_len & 0xc000)!=0xc000) + { + printk(KERN_WARNING "%s: EOF or F not set on received buffer (%04x)\n", + dev->name, pkt_len & 0xc000); + continue; + } + else if (!FD_OK(status)) + { + dev->stats.rx_errors++; + if (FD_CRC(status)) + dev->stats.rx_crc_errors++; + if (FD_Align(status)) + dev->stats.rx_frame_errors++; + if (FD_Resrc(status)) + dev->stats.rx_fifo_errors++; + if (FD_DMA(status)) + dev->stats.rx_over_errors++; + if (FD_Short(status)) + dev->stats.rx_length_errors++; + } + else + { + struct sk_buff *skb; + pkt_len &= 0x3fff; + skb = dev_alloc_skb(pkt_len+16); + if (skb == NULL) + { + printk(KERN_WARNING "%s: Memory squeeze, dropping packet\n",dev->name); + dev->stats.rx_dropped++; + break; + } + skb_reserve(skb, 2); + outw(pbuf+10, ioaddr+READ_PTR); + insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1); + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + outw(rx_block, ioaddr+WRITE_PTR); + outw(0, ioaddr+DATAPORT); + outw(0, ioaddr+DATAPORT); + rx_block = rx_next; + } + } while (FD_Done(status) && boguscount--); + lp->rx_ptr = rx_block; +} + +/* + * Hand a packet to the card for transmission + * If we get here, we MUST have already checked + * to make sure there is room in the transmit + * buffer region. + */ + +static void eexp_hw_tx_pio(struct net_device *dev, unsigned short *buf, + unsigned short len) +{ + struct net_local *lp = netdev_priv(dev); + unsigned short ioaddr = dev->base_addr; + + if (LOCKUP16 || lp->width) { + /* Stop the CU so that there is no chance that it + jumps off to a bogus address while we are writing the + pointer to the next transmit packet in 8-bit mode -- + this eliminates the "CU wedged" errors in 8-bit mode. + (Zoltan Szilagyi 10-12-96) */ + scb_command(dev, SCB_CUsuspend); + outw(0xFFFF, ioaddr+SIGNAL_CA); + } + + outw(lp->tx_head, ioaddr + WRITE_PTR); + + outw(0x0000, ioaddr + DATAPORT); + outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT); + outw(lp->tx_head+0x08, ioaddr + DATAPORT); + outw(lp->tx_head+0x0e, ioaddr + DATAPORT); + + outw(0x0000, ioaddr + DATAPORT); + outw(0x0000, ioaddr + DATAPORT); + outw(lp->tx_head+0x08, ioaddr + DATAPORT); + + outw(0x8000|len, ioaddr + DATAPORT); + outw(-1, ioaddr + DATAPORT); + outw(lp->tx_head+0x16, ioaddr + DATAPORT); + outw(0, ioaddr + DATAPORT); + + outsw(ioaddr + DATAPORT, buf, (len+1)>>1); + + outw(lp->tx_tail+0xc, ioaddr + WRITE_PTR); + outw(lp->tx_head, ioaddr + DATAPORT); + + dev->trans_start = jiffies; + lp->tx_tail = lp->tx_head; + if (lp->tx_head==TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE)) + lp->tx_head = TX_BUF_START; + else + lp->tx_head += TX_BUF_SIZE; + if (lp->tx_head != lp->tx_reap) + netif_wake_queue(dev); + + if (LOCKUP16 || lp->width) { + /* Restart the CU so that the packet can actually + be transmitted. (Zoltan Szilagyi 10-12-96) */ + scb_command(dev, SCB_CUresume); + outw(0xFFFF, ioaddr+SIGNAL_CA); + } + + dev->stats.tx_packets++; + lp->last_tx = jiffies; +} + +static const struct net_device_ops eexp_netdev_ops = { + .ndo_open = eexp_open, + .ndo_stop = eexp_close, + .ndo_start_xmit = eexp_xmit, + .ndo_set_multicast_list = eexp_set_multicast, + .ndo_tx_timeout = eexp_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +/* + * Sanity check the suspected EtherExpress card + * Read hardware address, reset card, size memory and initialize buffer + * memory pointers. These are held in netdev_priv(), in case someone has more + * than one card in a machine. + */ + +static int __init eexp_hw_probe(struct net_device *dev, unsigned short ioaddr) +{ + unsigned short hw_addr[3]; + unsigned char buswidth; + unsigned int memory_size; + int i; + unsigned short xsum = 0; + struct net_local *lp = netdev_priv(dev); + + printk("%s: EtherExpress 16 at %#x ",dev->name,ioaddr); + + outb(ASIC_RST, ioaddr+EEPROM_Ctrl); + outb(0, ioaddr+EEPROM_Ctrl); + udelay(500); + outb(i586_RST, ioaddr+EEPROM_Ctrl); + + hw_addr[0] = eexp_hw_readeeprom(ioaddr,2); + hw_addr[1] = eexp_hw_readeeprom(ioaddr,3); + hw_addr[2] = eexp_hw_readeeprom(ioaddr,4); + + /* Standard Address or Compaq LTE Address */ + if (!((hw_addr[2]==0x00aa && ((hw_addr[1] & 0xff00)==0x0000)) || + (hw_addr[2]==0x0080 && ((hw_addr[1] & 0xff00)==0x5F00)))) + { + printk(" rejected: invalid address %04x%04x%04x\n", + hw_addr[2],hw_addr[1],hw_addr[0]); + return -ENODEV; + } + + /* Calculate the EEPROM checksum. Carry on anyway if it's bad, + * though. + */ + for (i = 0; i < 64; i++) + xsum += eexp_hw_readeeprom(ioaddr, i); + if (xsum != 0xbaba) + printk(" (bad EEPROM xsum 0x%02x)", xsum); + + dev->base_addr = ioaddr; + for ( i=0 ; i<6 ; i++ ) + dev->dev_addr[i] = ((unsigned char *)hw_addr)[5-i]; + + { + static const char irqmap[] = { 0, 9, 3, 4, 5, 10, 11, 0 }; + unsigned short setupval = eexp_hw_readeeprom(ioaddr,0); + + /* Use the IRQ from EEPROM if none was given */ + if (!dev->irq) + dev->irq = irqmap[setupval>>13]; + + if (dev->if_port == 0xff) { + dev->if_port = !(setupval & 0x1000) ? AUI : + eexp_hw_readeeprom(ioaddr,5) & 0x1 ? TPE : BNC; + } + + buswidth = !((setupval & 0x400) >> 10); + } + + memset(lp, 0, sizeof(struct net_local)); + spin_lock_init(&lp->lock); + + printk("(IRQ %d, %s connector, %d-bit bus", dev->irq, + eexp_ifmap[dev->if_port], buswidth?8:16); + + if (!request_region(dev->base_addr + 0x300e, 1, "EtherExpress")) + return -EBUSY; + + eexp_hw_set_interface(dev); + + release_region(dev->base_addr + 0x300e, 1); + + /* Find out how much RAM we have on the card */ + outw(0, dev->base_addr + WRITE_PTR); + for (i = 0; i < 32768; i++) + outw(0, dev->base_addr + DATAPORT); + + for (memory_size = 0; memory_size < 64; memory_size++) + { + outw(memory_size<<10, dev->base_addr + READ_PTR); + if (inw(dev->base_addr+DATAPORT)) + break; + outw(memory_size<<10, dev->base_addr + WRITE_PTR); + outw(memory_size | 0x5000, dev->base_addr+DATAPORT); + outw(memory_size<<10, dev->base_addr + READ_PTR); + if (inw(dev->base_addr+DATAPORT) != (memory_size | 0x5000)) + break; + } + + /* Sort out the number of buffers. We may have 16, 32, 48 or 64k + * of RAM to play with. + */ + lp->num_tx_bufs = 4; + lp->rx_buf_end = 0x3ff6; + switch (memory_size) + { + case 64: + lp->rx_buf_end += 0x4000; + case 48: + lp->num_tx_bufs += 4; + lp->rx_buf_end += 0x4000; + case 32: + lp->rx_buf_end += 0x4000; + case 16: + printk(", %dk RAM)\n", memory_size); + break; + default: + printk(") bad memory size (%dk).\n", memory_size); + return -ENODEV; + break; + } + + lp->rx_buf_start = TX_BUF_START + (lp->num_tx_bufs*TX_BUF_SIZE); + lp->width = buswidth; + + dev->netdev_ops = &eexp_netdev_ops; + dev->watchdog_timeo = 2*HZ; + + return register_netdev(dev); +} + +/* + * Read a word from the EtherExpress on-board serial EEPROM. + * The EEPROM contains 64 words of 16 bits. + */ +static unsigned short __init eexp_hw_readeeprom(unsigned short ioaddr, + unsigned char location) +{ + unsigned short cmd = 0x180|(location&0x7f); + unsigned short rval = 0,wval = EC_CS|i586_RST; + int i; + + outb(EC_CS|i586_RST,ioaddr+EEPROM_Ctrl); + for (i=0x100 ; i ; i>>=1 ) + { + if (cmd&i) + wval |= EC_Wr; + else + wval &= ~EC_Wr; + + outb(wval,ioaddr+EEPROM_Ctrl); + outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl); + eeprom_delay(); + outb(wval,ioaddr+EEPROM_Ctrl); + eeprom_delay(); + } + wval &= ~EC_Wr; + outb(wval,ioaddr+EEPROM_Ctrl); + for (i=0x8000 ; i ; i>>=1 ) + { + outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl); + eeprom_delay(); + if (inb(ioaddr+EEPROM_Ctrl)&EC_Rd) + rval |= i; + outb(wval,ioaddr+EEPROM_Ctrl); + eeprom_delay(); + } + wval &= ~EC_CS; + outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl); + eeprom_delay(); + outb(wval,ioaddr+EEPROM_Ctrl); + eeprom_delay(); + return rval; +} + +/* + * Reap tx buffers and return last transmit status. + * if ==0 then either: + * a) we're not transmitting anything, so why are we here? + * b) we've died. + * otherwise, Stat_Busy(return) means we've still got some packets + * to transmit, Stat_Done(return) means our buffers should be empty + * again + */ + +static unsigned short eexp_hw_lasttxstat(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned short tx_block = lp->tx_reap; + unsigned short status; + + if (!netif_queue_stopped(dev) && lp->tx_head==lp->tx_reap) + return 0x0000; + + do + { + outw(tx_block & ~31, dev->base_addr + SM_PTR); + status = inw(dev->base_addr + SHADOW(tx_block)); + if (!Stat_Done(status)) + { + lp->tx_link = tx_block; + return status; + } + else + { + lp->last_tx_restart = 0; + dev->stats.collisions += Stat_NoColl(status); + if (!Stat_OK(status)) + { + char *whatsup = NULL; + dev->stats.tx_errors++; + if (Stat_Abort(status)) + dev->stats.tx_aborted_errors++; + if (Stat_TNoCar(status)) { + whatsup = "aborted, no carrier"; + dev->stats.tx_carrier_errors++; + } + if (Stat_TNoCTS(status)) { + whatsup = "aborted, lost CTS"; + dev->stats.tx_carrier_errors++; + } + if (Stat_TNoDMA(status)) { + whatsup = "FIFO underran"; + dev->stats.tx_fifo_errors++; + } + if (Stat_TXColl(status)) { + whatsup = "aborted, too many collisions"; + dev->stats.tx_aborted_errors++; + } + if (whatsup) + printk(KERN_INFO "%s: transmit %s\n", + dev->name, whatsup); + } + else + dev->stats.tx_packets++; + } + if (tx_block == TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE)) + lp->tx_reap = tx_block = TX_BUF_START; + else + lp->tx_reap = tx_block += TX_BUF_SIZE; + netif_wake_queue(dev); + } + while (lp->tx_reap != lp->tx_head); + + lp->tx_link = lp->tx_tail + 0x08; + + return status; +} + +/* + * This should never happen. It is called when some higher routine detects + * that the CU has stopped, to try to restart it from the last packet we knew + * we were working on, or the idle loop if we had finished for the time. + */ + +static void eexp_hw_txrestart(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned short ioaddr = dev->base_addr; + + lp->last_tx_restart = lp->tx_link; + scb_wrcbl(dev, lp->tx_link); + scb_command(dev, SCB_CUstart); + outb(0,ioaddr+SIGNAL_CA); + + { + unsigned short boguscount=50,failcount=5; + while (!scb_status(dev)) + { + if (!--boguscount) + { + if (--failcount) + { + printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n", dev->name, scb_status(dev), scb_rdcmd(dev)); + scb_wrcbl(dev, lp->tx_link); + scb_command(dev, SCB_CUstart); + outb(0,ioaddr+SIGNAL_CA); + boguscount = 100; + } + else + { + printk(KERN_WARNING "%s: Failed to restart CU, resetting board...\n",dev->name); + eexp_hw_init586(dev); + netif_wake_queue(dev); + return; + } + } + } + } +} + +/* + * Writes down the list of transmit buffers into card memory. Each + * entry consists of an 82586 transmit command, followed by a jump + * pointing to itself. When we want to transmit a packet, we write + * the data into the appropriate transmit buffer and then modify the + * preceding jump to point at the new transmit command. This means that + * the 586 command unit is continuously active. + */ + +static void eexp_hw_txinit(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned short tx_block = TX_BUF_START; + unsigned short curtbuf; + unsigned short ioaddr = dev->base_addr; + + for ( curtbuf=0 ; curtbuf<lp->num_tx_bufs ; curtbuf++ ) + { + outw(tx_block, ioaddr + WRITE_PTR); + + outw(0x0000, ioaddr + DATAPORT); + outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT); + outw(tx_block+0x08, ioaddr + DATAPORT); + outw(tx_block+0x0e, ioaddr + DATAPORT); + + outw(0x0000, ioaddr + DATAPORT); + outw(0x0000, ioaddr + DATAPORT); + outw(tx_block+0x08, ioaddr + DATAPORT); + + outw(0x8000, ioaddr + DATAPORT); + outw(-1, ioaddr + DATAPORT); + outw(tx_block+0x16, ioaddr + DATAPORT); + outw(0x0000, ioaddr + DATAPORT); + + tx_block += TX_BUF_SIZE; + } + lp->tx_head = TX_BUF_START; + lp->tx_reap = TX_BUF_START; + lp->tx_tail = tx_block - TX_BUF_SIZE; + lp->tx_link = lp->tx_tail + 0x08; + lp->rx_buf_start = tx_block; + +} + +/* + * Write the circular list of receive buffer descriptors to card memory. + * The end of the list isn't marked, which means that the 82586 receive + * unit will loop until buffers become available (this avoids it giving us + * "out of resources" messages). + */ + +static void eexp_hw_rxinit(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned short rx_block = lp->rx_buf_start; + unsigned short ioaddr = dev->base_addr; + + lp->num_rx_bufs = 0; + lp->rx_first = lp->rx_ptr = rx_block; + do + { + lp->num_rx_bufs++; + + outw(rx_block, ioaddr + WRITE_PTR); + + outw(0, ioaddr + DATAPORT); outw(0, ioaddr+DATAPORT); + outw(rx_block + RX_BUF_SIZE, ioaddr+DATAPORT); + outw(0xffff, ioaddr+DATAPORT); + + outw(0x0000, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + + outw(0x0000, ioaddr+DATAPORT); + outw(rx_block + RX_BUF_SIZE + 0x16, ioaddr+DATAPORT); + outw(rx_block + 0x20, ioaddr+DATAPORT); + outw(0, ioaddr+DATAPORT); + outw(RX_BUF_SIZE-0x20, ioaddr+DATAPORT); + + lp->rx_last = rx_block; + rx_block += RX_BUF_SIZE; + } while (rx_block <= lp->rx_buf_end-RX_BUF_SIZE); + + + /* Make first Rx frame descriptor point to first Rx buffer + descriptor */ + outw(lp->rx_first + 6, ioaddr+WRITE_PTR); + outw(lp->rx_first + 0x16, ioaddr+DATAPORT); + + /* Close Rx frame descriptor ring */ + outw(lp->rx_last + 4, ioaddr+WRITE_PTR); + outw(lp->rx_first, ioaddr+DATAPORT); + + /* Close Rx buffer descriptor ring */ + outw(lp->rx_last + 0x16 + 2, ioaddr+WRITE_PTR); + outw(lp->rx_first + 0x16, ioaddr+DATAPORT); + +} + +/* + * Un-reset the 586, and start the configuration sequence. We don't wait for + * this to finish, but allow the interrupt handler to start the CU and RU for + * us. We can't start the receive/transmission system up before we know that + * the hardware is configured correctly. + */ + +static void eexp_hw_init586(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned short ioaddr = dev->base_addr; + int i; + +#if NET_DEBUG > 6 + printk("%s: eexp_hw_init586()\n", dev->name); +#endif + + lp->started = 0; + + set_loopback(dev); + + outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); + + /* Download the startup code */ + outw(lp->rx_buf_end & ~31, ioaddr + SM_PTR); + outw(lp->width?0x0001:0x0000, ioaddr + 0x8006); + outw(0x0000, ioaddr + 0x8008); + outw(0x0000, ioaddr + 0x800a); + outw(0x0000, ioaddr + 0x800c); + outw(0x0000, ioaddr + 0x800e); + + for (i = 0; i < ARRAY_SIZE(start_code) * 2; i+=32) { + int j; + outw(i, ioaddr + SM_PTR); + for (j = 0; j < 16 && (i+j)/2 < ARRAY_SIZE(start_code); j+=2) + outw(start_code[(i+j)/2], + ioaddr+0x4000+j); + for (j = 0; j < 16 && (i+j+16)/2 < ARRAY_SIZE(start_code); j+=2) + outw(start_code[(i+j+16)/2], + ioaddr+0x8000+j); + } + + /* Do we want promiscuous mode or multicast? */ + outw(CONF_PROMISC & ~31, ioaddr+SM_PTR); + i = inw(ioaddr+SHADOW(CONF_PROMISC)); + outw((dev->flags & IFF_PROMISC)?(i|1):(i & ~1), + ioaddr+SHADOW(CONF_PROMISC)); + lp->was_promisc = dev->flags & IFF_PROMISC; +#if 0 + eexp_setup_filter(dev); +#endif + + /* Write our hardware address */ + outw(CONF_HWADDR & ~31, ioaddr+SM_PTR); + outw(((unsigned short *)dev->dev_addr)[0], ioaddr+SHADOW(CONF_HWADDR)); + outw(((unsigned short *)dev->dev_addr)[1], + ioaddr+SHADOW(CONF_HWADDR+2)); + outw(((unsigned short *)dev->dev_addr)[2], + ioaddr+SHADOW(CONF_HWADDR+4)); + + eexp_hw_txinit(dev); + eexp_hw_rxinit(dev); + + outb(0,ioaddr+EEPROM_Ctrl); + mdelay(5); + + scb_command(dev, 0xf000); + outb(0,ioaddr+SIGNAL_CA); + + outw(0, ioaddr+SM_PTR); + + { + unsigned short rboguscount=50,rfailcount=5; + while (inw(ioaddr+0x4000)) + { + if (!--rboguscount) + { + printk(KERN_WARNING "%s: i82586 reset timed out, kicking...\n", + dev->name); + scb_command(dev, 0); + outb(0,ioaddr+SIGNAL_CA); + rboguscount = 100; + if (!--rfailcount) + { + printk(KERN_WARNING "%s: i82586 not responding, giving up.\n", + dev->name); + return; + } + } + } + } + + scb_wrcbl(dev, CONF_LINK); + scb_command(dev, 0xf000|SCB_CUstart); + outb(0,ioaddr+SIGNAL_CA); + + { + unsigned short iboguscount=50,ifailcount=5; + while (!scb_status(dev)) + { + if (!--iboguscount) + { + if (--ifailcount) + { + printk(KERN_WARNING "%s: i82586 initialization timed out, status %04x, cmd %04x\n", + dev->name, scb_status(dev), scb_rdcmd(dev)); + scb_wrcbl(dev, CONF_LINK); + scb_command(dev, 0xf000|SCB_CUstart); + outb(0,ioaddr+SIGNAL_CA); + iboguscount = 100; + } + else + { + printk(KERN_WARNING "%s: Failed to initialize i82586, giving up.\n",dev->name); + return; + } + } + } + } + + clear_loopback(dev); + outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); + + lp->init_time = jiffies; +#if NET_DEBUG > 6 + printk("%s: leaving eexp_hw_init586()\n", dev->name); +#endif +} + +static void eexp_setup_filter(struct net_device *dev) +{ + struct netdev_hw_addr *ha; + unsigned short ioaddr = dev->base_addr; + int count = netdev_mc_count(dev); + int i; + if (count > 8) { + printk(KERN_INFO "%s: too many multicast addresses (%d)\n", + dev->name, count); + count = 8; + } + + outw(CONF_NR_MULTICAST & ~31, ioaddr+SM_PTR); + outw(6*count, ioaddr+SHADOW(CONF_NR_MULTICAST)); + i = 0; + netdev_for_each_mc_addr(ha, dev) { + unsigned short *data = (unsigned short *) ha->addr; + + if (i == count) + break; + outw((CONF_MULTICAST+(6*i)) & ~31, ioaddr+SM_PTR); + outw(data[0], ioaddr+SHADOW(CONF_MULTICAST+(6*i))); + outw((CONF_MULTICAST+(6*i)+2) & ~31, ioaddr+SM_PTR); + outw(data[1], ioaddr+SHADOW(CONF_MULTICAST+(6*i)+2)); + outw((CONF_MULTICAST+(6*i)+4) & ~31, ioaddr+SM_PTR); + outw(data[2], ioaddr+SHADOW(CONF_MULTICAST+(6*i)+4)); + i++; + } +} + +/* + * Set or clear the multicast filter for this adaptor. + */ +static void +eexp_set_multicast(struct net_device *dev) +{ + unsigned short ioaddr = dev->base_addr; + struct net_local *lp = netdev_priv(dev); + int kick = 0, i; + if ((dev->flags & IFF_PROMISC) != lp->was_promisc) { + outw(CONF_PROMISC & ~31, ioaddr+SM_PTR); + i = inw(ioaddr+SHADOW(CONF_PROMISC)); + outw((dev->flags & IFF_PROMISC)?(i|1):(i & ~1), + ioaddr+SHADOW(CONF_PROMISC)); + lp->was_promisc = dev->flags & IFF_PROMISC; + kick = 1; + } + if (!(dev->flags & IFF_PROMISC)) { + eexp_setup_filter(dev); + if (lp->old_mc_count != netdev_mc_count(dev)) { + kick = 1; + lp->old_mc_count = netdev_mc_count(dev); + } + } + if (kick) { + unsigned long oj; + scb_command(dev, SCB_CUsuspend); + outb(0, ioaddr+SIGNAL_CA); + outb(0, ioaddr+SIGNAL_CA); +#if 0 + printk("%s: waiting for CU to go suspended\n", dev->name); +#endif + oj = jiffies; + while ((SCB_CUstat(scb_status(dev)) == 2) && + (time_before(jiffies, oj + 2000))); + if (SCB_CUstat(scb_status(dev)) == 2) + printk("%s: warning, CU didn't stop\n", dev->name); + lp->started &= ~(STARTED_CU); + scb_wrcbl(dev, CONF_LINK); + scb_command(dev, SCB_CUstart); + outb(0, ioaddr+SIGNAL_CA); + } +} + + +/* + * MODULE stuff + */ + +#ifdef MODULE + +#define EEXP_MAX_CARDS 4 /* max number of cards to support */ + +static struct net_device *dev_eexp[EEXP_MAX_CARDS]; +static int irq[EEXP_MAX_CARDS]; +static int io[EEXP_MAX_CARDS]; + +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(io, "EtherExpress 16 I/O base address(es)"); +MODULE_PARM_DESC(irq, "EtherExpress 16 IRQ number(s)"); +MODULE_LICENSE("GPL"); + + +/* Ideally the user would give us io=, irq= for every card. If any parameters + * are specified, we verify and then use them. If no parameters are given, we + * autoprobe for one card only. + */ +int __init init_module(void) +{ + struct net_device *dev; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < EEXP_MAX_CARDS; this_dev++) { + dev = alloc_etherdev(sizeof(struct net_local)); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + if (io[this_dev] == 0) { + if (this_dev) + break; + printk(KERN_NOTICE "eexpress.c: Module autoprobe not recommended, give io=xx.\n"); + } + if (do_express_probe(dev) == 0) { + dev_eexp[this_dev] = dev; + found++; + continue; + } + printk(KERN_WARNING "eexpress.c: Failed to register card at 0x%x.\n", io[this_dev]); + free_netdev(dev); + break; + } + if (found) + return 0; + return -ENXIO; +} + +void __exit cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < EEXP_MAX_CARDS; this_dev++) { + struct net_device *dev = dev_eexp[this_dev]; + if (dev) { + unregister_netdev(dev); + free_netdev(dev); + } + } +} +#endif + +/* + * Local Variables: + * c-file-style: "linux" + * tab-width: 8 + * End: + */ diff --git a/drivers/net/ethernet/i825xx/eexpress.h b/drivers/net/ethernet/i825xx/eexpress.h new file mode 100644 index 000000000000..dc9c6ea289e9 --- /dev/null +++ b/drivers/net/ethernet/i825xx/eexpress.h @@ -0,0 +1,179 @@ +/* + * eexpress.h: Intel EtherExpress16 defines + */ + +/* + * EtherExpress card register addresses + * as offsets from the base IO region (dev->base_addr) + */ + +#define DATAPORT 0x0000 +#define WRITE_PTR 0x0002 +#define READ_PTR 0x0004 +#define SIGNAL_CA 0x0006 +#define SET_IRQ 0x0007 +#define SM_PTR 0x0008 +#define MEM_Dec 0x000a +#define MEM_Ctrl 0x000b +#define MEM_Page_Ctrl 0x000c +#define Config 0x000d +#define EEPROM_Ctrl 0x000e +#define ID_PORT 0x000f +#define MEM_ECtrl 0x000f + +/* + * card register defines + */ + +/* SET_IRQ */ +#define SIRQ_en 0x08 +#define SIRQ_dis 0x00 + +/* EEPROM_Ctrl */ +#define EC_Clk 0x01 +#define EC_CS 0x02 +#define EC_Wr 0x04 +#define EC_Rd 0x08 +#define ASIC_RST 0x40 +#define i586_RST 0x80 + +#define eeprom_delay() { udelay(40); } + +/* + * i82586 Memory Configuration + */ + +/* (System Configuration Pointer) System start up block, read after 586_RST */ +#define SCP_START 0xfff6 + +/* Intermediate System Configuration Pointer */ +#define ISCP_START 0x0000 + +/* System Command Block */ +#define SCB_START 0x0008 + +/* Start of buffer region. Everything before this is used for control + * structures and the CU configuration program. The memory layout is + * determined in eexp_hw_probe(), once we know how much memory is + * available on the card. + */ + +#define TX_BUF_START 0x0100 + +#define TX_BUF_SIZE ((24+ETH_FRAME_LEN+31)&~0x1f) +#define RX_BUF_SIZE ((32+ETH_FRAME_LEN+31)&~0x1f) + +/* + * SCB defines + */ + +/* these functions take the SCB status word and test the relevant status bit */ +#define SCB_complete(s) (((s) & 0x8000) != 0) +#define SCB_rxdframe(s) (((s) & 0x4000) != 0) +#define SCB_CUdead(s) (((s) & 0x2000) != 0) +#define SCB_RUdead(s) (((s) & 0x1000) != 0) +#define SCB_ack(s) ((s) & 0xf000) + +/* Command unit status: 0=idle, 1=suspended, 2=active */ +#define SCB_CUstat(s) (((s)&0x0300)>>8) + +/* Receive unit status: 0=idle, 1=suspended, 2=out of resources, 4=ready */ +#define SCB_RUstat(s) (((s)&0x0070)>>4) + +/* SCB commands */ +#define SCB_CUnop 0x0000 +#define SCB_CUstart 0x0100 +#define SCB_CUresume 0x0200 +#define SCB_CUsuspend 0x0300 +#define SCB_CUabort 0x0400 +#define SCB_resetchip 0x0080 + +#define SCB_RUnop 0x0000 +#define SCB_RUstart 0x0010 +#define SCB_RUresume 0x0020 +#define SCB_RUsuspend 0x0030 +#define SCB_RUabort 0x0040 + +/* + * Command block defines + */ + +#define Stat_Done(s) (((s) & 0x8000) != 0) +#define Stat_Busy(s) (((s) & 0x4000) != 0) +#define Stat_OK(s) (((s) & 0x2000) != 0) +#define Stat_Abort(s) (((s) & 0x1000) != 0) +#define Stat_STFail (((s) & 0x0800) != 0) +#define Stat_TNoCar(s) (((s) & 0x0400) != 0) +#define Stat_TNoCTS(s) (((s) & 0x0200) != 0) +#define Stat_TNoDMA(s) (((s) & 0x0100) != 0) +#define Stat_TDefer(s) (((s) & 0x0080) != 0) +#define Stat_TColl(s) (((s) & 0x0040) != 0) +#define Stat_TXColl(s) (((s) & 0x0020) != 0) +#define Stat_NoColl(s) ((s) & 0x000f) + +/* Cmd_END will end AFTER the command if this is the first + * command block after an SCB_CUstart, but BEFORE the command + * for all subsequent commands. Best strategy is to place + * Cmd_INT on the last command in the sequence, followed by a + * dummy Cmd_Nop with Cmd_END after this. + */ + +#define Cmd_END 0x8000 +#define Cmd_SUS 0x4000 +#define Cmd_INT 0x2000 + +#define Cmd_Nop 0x0000 +#define Cmd_SetAddr 0x0001 +#define Cmd_Config 0x0002 +#define Cmd_MCast 0x0003 +#define Cmd_Xmit 0x0004 +#define Cmd_TDR 0x0005 +#define Cmd_Dump 0x0006 +#define Cmd_Diag 0x0007 + + +/* + * Frame Descriptor (Receive block) defines + */ + +#define FD_Done(s) (((s) & 0x8000) != 0) +#define FD_Busy(s) (((s) & 0x4000) != 0) +#define FD_OK(s) (((s) & 0x2000) != 0) + +#define FD_CRC(s) (((s) & 0x0800) != 0) +#define FD_Align(s) (((s) & 0x0400) != 0) +#define FD_Resrc(s) (((s) & 0x0200) != 0) +#define FD_DMA(s) (((s) & 0x0100) != 0) +#define FD_Short(s) (((s) & 0x0080) != 0) +#define FD_NoEOF(s) (((s) & 0x0040) != 0) + +struct rfd_header { + volatile unsigned long flags; + volatile unsigned short link; + volatile unsigned short rbd_offset; + volatile unsigned short dstaddr1; + volatile unsigned short dstaddr2; + volatile unsigned short dstaddr3; + volatile unsigned short srcaddr1; + volatile unsigned short srcaddr2; + volatile unsigned short srcaddr3; + volatile unsigned short length; + + /* This is actually a Receive Buffer Descriptor. The way we + * arrange memory means that an RBD always follows the RFD that + * points to it, so they might as well be in the same structure. + */ + volatile unsigned short actual_count; + volatile unsigned short next_rbd; + volatile unsigned short buf_addr1; + volatile unsigned short buf_addr2; + volatile unsigned short size; +}; + +/* Returned data from the Time Domain Reflectometer */ + +#define TDR_LINKOK (1<<15) +#define TDR_XCVRPROBLEM (1<<14) +#define TDR_OPEN (1<<13) +#define TDR_SHORT (1<<12) +#define TDR_TIME 0x7ff diff --git a/drivers/net/ethernet/i825xx/ether1.c b/drivers/net/ethernet/i825xx/ether1.c new file mode 100644 index 000000000000..b00781c02d5d --- /dev/null +++ b/drivers/net/ethernet/i825xx/ether1.c @@ -0,0 +1,1094 @@ +/* + * linux/drivers/acorn/net/ether1.c + * + * Copyright (C) 1996-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Acorn ether1 driver (82586 chip) for Acorn machines + * + * We basically keep two queues in the cards memory - one for transmit + * and one for receive. Each has a head and a tail. The head is where + * we/the chip adds packets to be transmitted/received, and the tail + * is where the transmitter has got to/where the receiver will stop. + * Both of these queues are circular, and since the chip is running + * all the time, we have to be careful when we modify the pointers etc + * so that the buffer memory contents is valid all the time. + * + * Change log: + * 1.00 RMK Released + * 1.01 RMK 19/03/1996 Transfers the last odd byte onto/off of the card now. + * 1.02 RMK 25/05/1997 Added code to restart RU if it goes not ready + * 1.03 RMK 14/09/1997 Cleaned up the handling of a reset during the TX interrupt. + * Should prevent lockup. + * 1.04 RMK 17/09/1997 Added more info when initialsation of chip goes wrong. + * TDR now only reports failure when chip reports non-zero + * TDR time-distance. + * 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1 + * 1.06 RMK 10/02/2000 Updated for 2.3.43 + * 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/bitops.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/ecard.h> + +#define __ETHER1_C +#include "ether1.h" + +static unsigned int net_debug = NET_DEBUG; + +#define BUFFER_SIZE 0x10000 +#define TX_AREA_START 0x00100 +#define TX_AREA_END 0x05000 +#define RX_AREA_START 0x05000 +#define RX_AREA_END 0x0fc00 + +static int ether1_open(struct net_device *dev); +static int ether1_sendpacket(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t ether1_interrupt(int irq, void *dev_id); +static int ether1_close(struct net_device *dev); +static void ether1_setmulticastlist(struct net_device *dev); +static void ether1_timeout(struct net_device *dev); + +/* ------------------------------------------------------------------------- */ + +static char version[] __devinitdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; + +#define BUS_16 16 +#define BUS_8 8 + +/* ------------------------------------------------------------------------- */ + +#define DISABLEIRQS 1 +#define NORMALIRQS 0 + +#define ether1_readw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs) +#define ether1_writew(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs) + +static inline unsigned short +ether1_inw_p (struct net_device *dev, int addr, int svflgs) +{ + unsigned long flags; + unsigned short ret; + + if (svflgs) + local_irq_save (flags); + + writeb(addr >> 12, REG_PAGE); + ret = readw(ETHER1_RAM + ((addr & 4095) << 1)); + if (svflgs) + local_irq_restore (flags); + return ret; +} + +static inline void +ether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs) +{ + unsigned long flags; + + if (svflgs) + local_irq_save (flags); + + writeb(addr >> 12, REG_PAGE); + writew(val, ETHER1_RAM + ((addr & 4095) << 1)); + if (svflgs) + local_irq_restore (flags); +} + +/* + * Some inline assembler to allow fast transfers on to/off of the card. + * Since this driver depends on some features presented by the ARM + * specific architecture, and that you can't configure this driver + * without specifiing ARM mode, this is not a problem. + * + * This routine is essentially an optimised memcpy from the card's + * onboard RAM to kernel memory. + */ +static void +ether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) +{ + unsigned int page, thislen, offset; + void __iomem *addr; + + offset = start & 4095; + page = start >> 12; + addr = ETHER1_RAM + (offset << 1); + + if (offset + length > 4096) + thislen = 4096 - offset; + else + thislen = length; + + do { + int used; + + writeb(page, REG_PAGE); + length -= thislen; + + __asm__ __volatile__( + "subs %3, %3, #2\n\ + bmi 2f\n\ +1: ldr %0, [%1], #2\n\ + mov %0, %0, lsl #16\n\ + orr %0, %0, %0, lsr #16\n\ + str %0, [%2], #4\n\ + subs %3, %3, #2\n\ + bmi 2f\n\ + ldr %0, [%1], #2\n\ + mov %0, %0, lsl #16\n\ + orr %0, %0, %0, lsr #16\n\ + str %0, [%2], #4\n\ + subs %3, %3, #2\n\ + bmi 2f\n\ + ldr %0, [%1], #2\n\ + mov %0, %0, lsl #16\n\ + orr %0, %0, %0, lsr #16\n\ + str %0, [%2], #4\n\ + subs %3, %3, #2\n\ + bmi 2f\n\ + ldr %0, [%1], #2\n\ + mov %0, %0, lsl #16\n\ + orr %0, %0, %0, lsr #16\n\ + str %0, [%2], #4\n\ + subs %3, %3, #2\n\ + bpl 1b\n\ +2: adds %3, %3, #1\n\ + ldreqb %0, [%1]\n\ + streqb %0, [%2]" + : "=&r" (used), "=&r" (data) + : "r" (addr), "r" (thislen), "1" (data)); + + addr = ETHER1_RAM; + + thislen = length; + if (thislen > 4096) + thislen = 4096; + page++; + } while (thislen); +} + +static void +ether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) +{ + unsigned int page, thislen, offset; + void __iomem *addr; + + offset = start & 4095; + page = start >> 12; + addr = ETHER1_RAM + (offset << 1); + + if (offset + length > 4096) + thislen = 4096 - offset; + else + thislen = length; + + do { + int used; + + writeb(page, REG_PAGE); + length -= thislen; + + __asm__ __volatile__( + "subs %3, %3, #2\n\ + bmi 2f\n\ +1: ldr %0, [%2], #4\n\ + strb %0, [%1], #1\n\ + mov %0, %0, lsr #8\n\ + strb %0, [%1], #1\n\ + subs %3, %3, #2\n\ + bmi 2f\n\ + ldr %0, [%2], #4\n\ + strb %0, [%1], #1\n\ + mov %0, %0, lsr #8\n\ + strb %0, [%1], #1\n\ + subs %3, %3, #2\n\ + bmi 2f\n\ + ldr %0, [%2], #4\n\ + strb %0, [%1], #1\n\ + mov %0, %0, lsr #8\n\ + strb %0, [%1], #1\n\ + subs %3, %3, #2\n\ + bmi 2f\n\ + ldr %0, [%2], #4\n\ + strb %0, [%1], #1\n\ + mov %0, %0, lsr #8\n\ + strb %0, [%1], #1\n\ + subs %3, %3, #2\n\ + bpl 1b\n\ +2: adds %3, %3, #1\n\ + ldreqb %0, [%2]\n\ + streqb %0, [%1]" + : "=&r" (used), "=&r" (data) + : "r" (addr), "r" (thislen), "1" (data)); + + addr = ETHER1_RAM; + + thislen = length; + if (thislen > 4096) + thislen = 4096; + page++; + } while (thislen); +} + +static int __devinit +ether1_ramtest(struct net_device *dev, unsigned char byte) +{ + unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL); + int i, ret = BUFFER_SIZE; + int max_errors = 15; + int bad = -1; + int bad_start = 0; + + if (!buffer) + return 1; + + memset (buffer, byte, BUFFER_SIZE); + ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE); + memset (buffer, byte ^ 0xff, BUFFER_SIZE); + ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE); + + for (i = 0; i < BUFFER_SIZE; i++) { + if (buffer[i] != byte) { + if (max_errors >= 0 && bad != buffer[i]) { + if (bad != -1) + printk ("\n"); + printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X", + dev->name, buffer[i], byte, i); + ret = -ENODEV; + max_errors --; + bad = buffer[i]; + bad_start = i; + } + } else { + if (bad != -1) { + if (bad_start == i - 1) + printk ("\n"); + else + printk (" - 0x%04X\n", i - 1); + bad = -1; + } + } + } + + if (bad != -1) + printk (" - 0x%04X\n", BUFFER_SIZE); + kfree (buffer); + + return ret; +} + +static int +ether1_reset (struct net_device *dev) +{ + writeb(CTRL_RST|CTRL_ACK, REG_CONTROL); + return BUS_16; +} + +static int __devinit +ether1_init_2(struct net_device *dev) +{ + int i; + dev->mem_start = 0; + + i = ether1_ramtest (dev, 0x5a); + + if (i > 0) + i = ether1_ramtest (dev, 0x1e); + + if (i <= 0) + return -ENODEV; + + dev->mem_end = i; + return 0; +} + +/* + * These are the structures that are loaded into the ether RAM card to + * initialise the 82586 + */ + +/* at 0x0100 */ +#define NOP_ADDR (TX_AREA_START) +#define NOP_SIZE (0x06) +static nop_t init_nop = { + 0, + CMD_NOP, + NOP_ADDR +}; + +/* at 0x003a */ +#define TDR_ADDR (0x003a) +#define TDR_SIZE (0x08) +static tdr_t init_tdr = { + 0, + CMD_TDR | CMD_INTR, + NOP_ADDR, + 0 +}; + +/* at 0x002e */ +#define MC_ADDR (0x002e) +#define MC_SIZE (0x0c) +static mc_t init_mc = { + 0, + CMD_SETMULTICAST, + TDR_ADDR, + 0, + { { 0, } } +}; + +/* at 0x0022 */ +#define SA_ADDR (0x0022) +#define SA_SIZE (0x0c) +static sa_t init_sa = { + 0, + CMD_SETADDRESS, + MC_ADDR, + { 0, } +}; + +/* at 0x0010 */ +#define CFG_ADDR (0x0010) +#define CFG_SIZE (0x12) +static cfg_t init_cfg = { + 0, + CMD_CONFIG, + SA_ADDR, + 8, + 8, + CFG8_SRDY, + CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6), + 0, + 0x60, + 0, + CFG13_RETRY(15) | CFG13_SLOTH(2), + 0, +}; + +/* at 0x0000 */ +#define SCB_ADDR (0x0000) +#define SCB_SIZE (0x10) +static scb_t init_scb = { + 0, + SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX, + CFG_ADDR, + RX_AREA_START, + 0, + 0, + 0, + 0 +}; + +/* at 0xffee */ +#define ISCP_ADDR (0xffee) +#define ISCP_SIZE (0x08) +static iscp_t init_iscp = { + 1, + SCB_ADDR, + 0x0000, + 0x0000 +}; + +/* at 0xfff6 */ +#define SCP_ADDR (0xfff6) +#define SCP_SIZE (0x0a) +static scp_t init_scp = { + SCP_SY_16BBUS, + { 0, 0 }, + ISCP_ADDR, + 0 +}; + +#define RFD_SIZE (0x16) +static rfd_t init_rfd = { + 0, + 0, + 0, + 0, + { 0, }, + { 0, }, + 0 +}; + +#define RBD_SIZE (0x0a) +static rbd_t init_rbd = { + 0, + 0, + 0, + 0, + ETH_FRAME_LEN + 8 +}; + +#define TX_SIZE (0x08) +#define TBD_SIZE (0x08) + +static int +ether1_init_for_open (struct net_device *dev) +{ + int i, status, addr, next, next2; + int failures = 0; + unsigned long timeout; + + writeb(CTRL_RST|CTRL_ACK, REG_CONTROL); + + for (i = 0; i < 6; i++) + init_sa.sa_addr[i] = dev->dev_addr[i]; + + /* load data structures into ether1 RAM */ + ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE); + ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE); + ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE); + ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE); + ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE); + ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE); + ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE); + ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE); + + if (ether1_readw(dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) { + printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n", + dev->name); + return 1; + } + + /* + * setup circularly linked list of { rfd, rbd, buffer }, with + * all rfds circularly linked, rbds circularly linked. + * First rfd is linked to scp, first rbd is linked to first + * rfd. Last rbd has a suspend command. + */ + addr = RX_AREA_START; + do { + next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; + next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; + + if (next2 >= RX_AREA_END) { + next = RX_AREA_START; + init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND; + priv(dev)->rx_tail = addr; + } else + init_rfd.rfd_command = 0; + if (addr == RX_AREA_START) + init_rfd.rfd_rbdoffset = addr + RFD_SIZE; + else + init_rfd.rfd_rbdoffset = 0; + init_rfd.rfd_link = next; + init_rbd.rbd_link = next + RFD_SIZE; + init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE; + + ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE); + ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE); + addr = next; + } while (next2 < RX_AREA_END); + + priv(dev)->tx_link = NOP_ADDR; + priv(dev)->tx_head = NOP_ADDR + NOP_SIZE; + priv(dev)->tx_tail = TDR_ADDR; + priv(dev)->rx_head = RX_AREA_START; + + /* release reset & give 586 a prod */ + priv(dev)->resetting = 1; + priv(dev)->initialising = 1; + writeb(CTRL_RST, REG_CONTROL); + writeb(0, REG_CONTROL); + writeb(CTRL_CA, REG_CONTROL); + + /* 586 should now unset iscp.busy */ + timeout = jiffies + HZ/2; + while (ether1_readw(dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) { + if (time_after(jiffies, timeout)) { + printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name); + return 1; + } + } + + /* check status of commands that we issued */ + timeout += HZ/10; + while (((status = ether1_readw(dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, timeout)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + timeout += HZ/10; + while (((status = ether1_readw(dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, timeout)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + timeout += HZ/10; + while (((status = ether1_readw(dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, timeout)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + timeout += HZ; + while (((status = ether1_readw(dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, timeout)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + } else { + status = ether1_readw(dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS); + if (status & TDR_XCVRPROB) + printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name); + else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) { +#ifdef FANCY + printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name, + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10, + (status & TDR_TIME) % 10); +#else + printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name, + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME)); +#endif + } + } + + if (failures) + ether1_reset (dev); + return failures ? 1 : 0; +} + +/* ------------------------------------------------------------------------- */ + +static int +ether1_txalloc (struct net_device *dev, int size) +{ + int start, tail; + + size = (size + 1) & ~1; + tail = priv(dev)->tx_tail; + + if (priv(dev)->tx_head + size > TX_AREA_END) { + if (tail > priv(dev)->tx_head) + return -1; + start = TX_AREA_START; + if (start + size > tail) + return -1; + priv(dev)->tx_head = start + size; + } else { + if (priv(dev)->tx_head < tail && (priv(dev)->tx_head + size) > tail) + return -1; + start = priv(dev)->tx_head; + priv(dev)->tx_head += size; + } + + return start; +} + +static int +ether1_open (struct net_device *dev) +{ + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_WARNING "%s: invalid ethernet MAC address\n", + dev->name); + return -EINVAL; + } + + if (request_irq(dev->irq, ether1_interrupt, 0, "ether1", dev)) + return -EAGAIN; + + if (ether1_init_for_open (dev)) { + free_irq (dev->irq, dev); + return -EAGAIN; + } + + netif_start_queue(dev); + + return 0; +} + +static void +ether1_timeout(struct net_device *dev) +{ + printk(KERN_WARNING "%s: transmit timeout, network cable problem?\n", + dev->name); + printk(KERN_WARNING "%s: resetting device\n", dev->name); + + ether1_reset (dev); + + if (ether1_init_for_open (dev)) + printk (KERN_ERR "%s: unable to restart interface\n", dev->name); + + dev->stats.tx_errors++; + netif_wake_queue(dev); +} + +static int +ether1_sendpacket (struct sk_buff *skb, struct net_device *dev) +{ + int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr; + unsigned long flags; + tx_t tx; + tbd_t tbd; + nop_t nop; + + if (priv(dev)->restart) { + printk(KERN_WARNING "%s: resetting device\n", dev->name); + + ether1_reset(dev); + + if (ether1_init_for_open(dev)) + printk(KERN_ERR "%s: unable to restart interface\n", dev->name); + else + priv(dev)->restart = 0; + } + + if (skb->len < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) + goto out; + } + + /* + * insert packet followed by a nop + */ + txaddr = ether1_txalloc (dev, TX_SIZE); + tbdaddr = ether1_txalloc (dev, TBD_SIZE); + dataddr = ether1_txalloc (dev, skb->len); + nopaddr = ether1_txalloc (dev, NOP_SIZE); + + tx.tx_status = 0; + tx.tx_command = CMD_TX | CMD_INTR; + tx.tx_link = nopaddr; + tx.tx_tbdoffset = tbdaddr; + tbd.tbd_opts = TBD_EOL | skb->len; + tbd.tbd_link = I82586_NULL; + tbd.tbd_bufl = dataddr; + tbd.tbd_bufh = 0; + nop.nop_status = 0; + nop.nop_command = CMD_NOP; + nop.nop_link = nopaddr; + + local_irq_save(flags); + ether1_writebuffer (dev, &tx, txaddr, TX_SIZE); + ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE); + ether1_writebuffer (dev, skb->data, dataddr, skb->len); + ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE); + tmp = priv(dev)->tx_link; + priv(dev)->tx_link = nopaddr; + + /* now reset the previous nop pointer */ + ether1_writew(dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS); + + local_irq_restore(flags); + + /* handle transmit */ + + /* check to see if we have room for a full sized ether frame */ + tmp = priv(dev)->tx_head; + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); + priv(dev)->tx_head = tmp; + dev_kfree_skb (skb); + + if (tst == -1) + netif_stop_queue(dev); + + out: + return NETDEV_TX_OK; +} + +static void +ether1_xmit_done (struct net_device *dev) +{ + nop_t nop; + int caddr, tst; + + caddr = priv(dev)->tx_tail; + +again: + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + + switch (nop.nop_command & CMD_MASK) { + case CMD_TDR: + /* special case */ + if (ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) + != (unsigned short)I82586_NULL) { + ether1_writew(dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t, + scb_command, NORMALIRQS); + writeb(CTRL_CA, REG_CONTROL); + } + priv(dev)->tx_tail = NOP_ADDR; + return; + + case CMD_NOP: + if (nop.nop_link == caddr) { + if (priv(dev)->initialising == 0) + printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name); + else + priv(dev)->initialising = 0; + return; + } + if (caddr == nop.nop_link) + return; + caddr = nop.nop_link; + goto again; + + case CMD_TX: + if (nop.nop_status & STAT_COMPLETE) + break; + printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name); + priv(dev)->restart = 1; + return; + + default: + printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name, + nop.nop_command & CMD_MASK, caddr); + priv(dev)->restart = 1; + return; + } + + while (nop.nop_status & STAT_COMPLETE) { + if (nop.nop_status & STAT_OK) { + dev->stats.tx_packets++; + dev->stats.collisions += (nop.nop_status & STAT_COLLISIONS); + } else { + dev->stats.tx_errors++; + + if (nop.nop_status & STAT_COLLAFTERTX) + dev->stats.collisions++; + if (nop.nop_status & STAT_NOCARRIER) + dev->stats.tx_carrier_errors++; + if (nop.nop_status & STAT_TXLOSTCTS) + printk (KERN_WARNING "%s: cts lost\n", dev->name); + if (nop.nop_status & STAT_TXSLOWDMA) + dev->stats.tx_fifo_errors++; + if (nop.nop_status & STAT_COLLEXCESSIVE) + dev->stats.collisions += 16; + } + + if (nop.nop_link == caddr) { + printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name); + break; + } + + caddr = nop.nop_link; + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + if ((nop.nop_command & CMD_MASK) != CMD_NOP) { + printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name); + break; + } + + if (caddr == nop.nop_link) + break; + + caddr = nop.nop_link; + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + if ((nop.nop_command & CMD_MASK) != CMD_TX) { + printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name); + break; + } + } + priv(dev)->tx_tail = caddr; + + caddr = priv(dev)->tx_head; + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); + priv(dev)->tx_head = caddr; + if (tst != -1) + netif_wake_queue(dev); +} + +static void +ether1_recv_done (struct net_device *dev) +{ + int status; + int nexttail, rbdaddr; + rbd_t rbd; + + do { + status = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_status, NORMALIRQS); + if ((status & RFD_COMPLETE) == 0) + break; + + rbdaddr = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS); + ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE); + + if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) { + int length = rbd.rbd_status & RBD_ACNT; + struct sk_buff *skb; + + length = (length + 1) & ~1; + skb = dev_alloc_skb (length + 2); + + if (skb) { + skb_reserve (skb, 2); + + ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length); + + skb->protocol = eth_type_trans (skb, dev); + netif_rx (skb); + dev->stats.rx_packets++; + } else + dev->stats.rx_dropped++; + } else { + printk(KERN_WARNING "%s: %s\n", dev->name, + (rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid"); + dev->stats.rx_dropped++; + } + + nexttail = ether1_readw(dev, priv(dev)->rx_tail, rfd_t, rfd_link, NORMALIRQS); + /* nexttail should be rx_head */ + if (nexttail != priv(dev)->rx_head) + printk(KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n", + dev->name, nexttail, priv(dev)->rx_head); + ether1_writew(dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS); + ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_command, NORMALIRQS); + ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_status, NORMALIRQS); + ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS); + + priv(dev)->rx_tail = nexttail; + priv(dev)->rx_head = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_link, NORMALIRQS); + } while (1); +} + +static irqreturn_t +ether1_interrupt (int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + int status; + + status = ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS); + + if (status) { + ether1_writew(dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX), + SCB_ADDR, scb_t, scb_command, NORMALIRQS); + writeb(CTRL_CA | CTRL_ACK, REG_CONTROL); + if (status & SCB_STCX) { + ether1_xmit_done (dev); + } + if (status & SCB_STCNA) { + if (priv(dev)->resetting == 0) + printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name); + else + priv(dev)->resetting += 1; + if (ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) + != (unsigned short)I82586_NULL) { + ether1_writew(dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS); + writeb(CTRL_CA, REG_CONTROL); + } + if (priv(dev)->resetting == 2) + priv(dev)->resetting = 0; + } + if (status & SCB_STFR) { + ether1_recv_done (dev); + } + if (status & SCB_STRNR) { + if (ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) { + printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name); + ether1_writew(dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS); + writeb(CTRL_CA, REG_CONTROL); + dev->stats.rx_dropped++; /* we suspended due to lack of buffer space */ + } else + printk(KERN_WARNING "%s: RU went not ready: %04X\n", dev->name, + ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS)); + printk (KERN_WARNING "RU ptr = %04X\n", ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, + NORMALIRQS)); + } + } else + writeb(CTRL_ACK, REG_CONTROL); + + return IRQ_HANDLED; +} + +static int +ether1_close (struct net_device *dev) +{ + ether1_reset (dev); + + free_irq(dev->irq, dev); + + return 0; +} + +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets. + * num_addrs == 0 Normal mode, clear multicast list. + * num_addrs > 0 Multicast mode, receive normal and MC packets, and do + * best-effort filtering. + */ +static void +ether1_setmulticastlist (struct net_device *dev) +{ +} + +/* ------------------------------------------------------------------------- */ + +static void __devinit ether1_banner(void) +{ + static unsigned int version_printed = 0; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +static const struct net_device_ops ether1_netdev_ops = { + .ndo_open = ether1_open, + .ndo_stop = ether1_close, + .ndo_start_xmit = ether1_sendpacket, + .ndo_set_multicast_list = ether1_setmulticastlist, + .ndo_tx_timeout = ether1_timeout, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, +}; + +static int __devinit +ether1_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct net_device *dev; + int i, ret = 0; + + ether1_banner(); + + ret = ecard_request_resources(ec); + if (ret) + goto out; + + dev = alloc_etherdev(sizeof(struct ether1_priv)); + if (!dev) { + ret = -ENOMEM; + goto release; + } + + SET_NETDEV_DEV(dev, &ec->dev); + + dev->irq = ec->irq; + priv(dev)->base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); + if (!priv(dev)->base) { + ret = -ENOMEM; + goto free; + } + + if ((priv(dev)->bus_type = ether1_reset(dev)) == 0) { + ret = -ENODEV; + goto free; + } + + for (i = 0; i < 6; i++) + dev->dev_addr[i] = readb(IDPROM_ADDRESS + (i << 2)); + + if (ether1_init_2(dev)) { + ret = -ENODEV; + goto free; + } + + dev->netdev_ops = ðer1_netdev_ops; + dev->watchdog_timeo = 5 * HZ / 100; + + ret = register_netdev(dev); + if (ret) + goto free; + + printk(KERN_INFO "%s: ether1 in slot %d, %pM\n", + dev->name, ec->slot_no, dev->dev_addr); + + ecard_set_drvdata(ec, dev); + return 0; + + free: + free_netdev(dev); + release: + ecard_release_resources(ec); + out: + return ret; +} + +static void __devexit ether1_remove(struct expansion_card *ec) +{ + struct net_device *dev = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + unregister_netdev(dev); + free_netdev(dev); + ecard_release_resources(ec); +} + +static const struct ecard_id ether1_ids[] = { + { MANU_ACORN, PROD_ACORN_ETHER1 }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver ether1_driver = { + .probe = ether1_probe, + .remove = __devexit_p(ether1_remove), + .id_table = ether1_ids, + .drv = { + .name = "ether1", + }, +}; + +static int __init ether1_init(void) +{ + return ecard_register_driver(ðer1_driver); +} + +static void __exit ether1_exit(void) +{ + ecard_remove_driver(ðer1_driver); +} + +module_init(ether1_init); +module_exit(ether1_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/i825xx/ether1.h b/drivers/net/ethernet/i825xx/ether1.h new file mode 100644 index 000000000000..3a5830ab3dc7 --- /dev/null +++ b/drivers/net/ethernet/i825xx/ether1.h @@ -0,0 +1,280 @@ +/* + * linux/drivers/acorn/net/ether1.h + * + * Copyright (C) 1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Network driver for Acorn Ether1 cards. + */ + +#ifndef _LINUX_ether1_H +#define _LINUX_ether1_H + +#ifdef __ETHER1_C +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +#define priv(dev) ((struct ether1_priv *)netdev_priv(dev)) + +/* Page register */ +#define REG_PAGE (priv(dev)->base + 0x0000) + +/* Control register */ +#define REG_CONTROL (priv(dev)->base + 0x0004) +#define CTRL_RST 0x01 +#define CTRL_LOOPBACK 0x02 +#define CTRL_CA 0x04 +#define CTRL_ACK 0x08 + +#define ETHER1_RAM (priv(dev)->base + 0x2000) + +/* HW address */ +#define IDPROM_ADDRESS (priv(dev)->base + 0x0024) + +struct ether1_priv { + void __iomem *base; + unsigned int tx_link; + unsigned int tx_head; + volatile unsigned int tx_tail; + volatile unsigned int rx_head; + volatile unsigned int rx_tail; + unsigned char bus_type; + unsigned char resetting; + unsigned char initialising : 1; + unsigned char restart : 1; +}; + +#define I82586_NULL (-1) + +typedef struct { /* tdr */ + unsigned short tdr_status; + unsigned short tdr_command; + unsigned short tdr_link; + unsigned short tdr_result; +#define TDR_TIME (0x7ff) +#define TDR_SHORT (1 << 12) +#define TDR_OPEN (1 << 13) +#define TDR_XCVRPROB (1 << 14) +#define TDR_LNKOK (1 << 15) +} tdr_t; + +typedef struct { /* transmit */ + unsigned short tx_status; + unsigned short tx_command; + unsigned short tx_link; + unsigned short tx_tbdoffset; +} tx_t; + +typedef struct { /* tbd */ + unsigned short tbd_opts; +#define TBD_CNT (0x3fff) +#define TBD_EOL (1 << 15) + unsigned short tbd_link; + unsigned short tbd_bufl; + unsigned short tbd_bufh; +} tbd_t; + +typedef struct { /* rfd */ + unsigned short rfd_status; +#define RFD_NOEOF (1 << 6) +#define RFD_FRAMESHORT (1 << 7) +#define RFD_DMAOVRN (1 << 8) +#define RFD_NORESOURCES (1 << 9) +#define RFD_ALIGNERROR (1 << 10) +#define RFD_CRCERROR (1 << 11) +#define RFD_OK (1 << 13) +#define RFD_FDCONSUMED (1 << 14) +#define RFD_COMPLETE (1 << 15) + unsigned short rfd_command; +#define RFD_CMDSUSPEND (1 << 14) +#define RFD_CMDEL (1 << 15) + unsigned short rfd_link; + unsigned short rfd_rbdoffset; + unsigned char rfd_dest[6]; + unsigned char rfd_src[6]; + unsigned short rfd_len; +} rfd_t; + +typedef struct { /* rbd */ + unsigned short rbd_status; +#define RBD_ACNT (0x3fff) +#define RBD_ACNTVALID (1 << 14) +#define RBD_EOF (1 << 15) + unsigned short rbd_link; + unsigned short rbd_bufl; + unsigned short rbd_bufh; + unsigned short rbd_len; +} rbd_t; + +typedef struct { /* nop */ + unsigned short nop_status; + unsigned short nop_command; + unsigned short nop_link; +} nop_t; + +typedef struct { /* set multicast */ + unsigned short mc_status; + unsigned short mc_command; + unsigned short mc_link; + unsigned short mc_cnt; + unsigned char mc_addrs[1][6]; +} mc_t; + +typedef struct { /* set address */ + unsigned short sa_status; + unsigned short sa_command; + unsigned short sa_link; + unsigned char sa_addr[6]; +} sa_t; + +typedef struct { /* config command */ + unsigned short cfg_status; + unsigned short cfg_command; + unsigned short cfg_link; + unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */ + unsigned char cfg_fifolim; /* FIFO threshold */ + unsigned char cfg_byte8; +#define CFG8_SRDY (1 << 6) +#define CFG8_SAVEBADF (1 << 7) + unsigned char cfg_byte9; +#define CFG9_ADDRLEN(x) (x) +#define CFG9_ADDRLENBUF (1 << 3) +#define CFG9_PREAMB2 (0 << 4) +#define CFG9_PREAMB4 (1 << 4) +#define CFG9_PREAMB8 (2 << 4) +#define CFG9_PREAMB16 (3 << 4) +#define CFG9_ILOOPBACK (1 << 6) +#define CFG9_ELOOPBACK (1 << 7) + unsigned char cfg_byte10; +#define CFG10_LINPRI(x) (x) +#define CFG10_ACR(x) (x << 4) +#define CFG10_BOFMET (1 << 7) + unsigned char cfg_ifs; + unsigned char cfg_slotl; + unsigned char cfg_byte13; +#define CFG13_SLOTH(x) (x) +#define CFG13_RETRY(x) (x << 4) + unsigned char cfg_byte14; +#define CFG14_PROMISC (1 << 0) +#define CFG14_DISBRD (1 << 1) +#define CFG14_MANCH (1 << 2) +#define CFG14_TNCRS (1 << 3) +#define CFG14_NOCRC (1 << 4) +#define CFG14_CRC16 (1 << 5) +#define CFG14_BTSTF (1 << 6) +#define CFG14_FLGPAD (1 << 7) + unsigned char cfg_byte15; +#define CFG15_CSTF(x) (x) +#define CFG15_ICSS (1 << 3) +#define CFG15_CDTF(x) (x << 4) +#define CFG15_ICDS (1 << 7) + unsigned short cfg_minfrmlen; +} cfg_t; + +typedef struct { /* scb */ + unsigned short scb_status; /* status of 82586 */ +#define SCB_STRXMASK (7 << 4) /* Receive unit status */ +#define SCB_STRXIDLE (0 << 4) /* Idle */ +#define SCB_STRXSUSP (1 << 4) /* Suspended */ +#define SCB_STRXNRES (2 << 4) /* No resources */ +#define SCB_STRXRDY (4 << 4) /* Ready */ +#define SCB_STCUMASK (7 << 8) /* Command unit status */ +#define SCB_STCUIDLE (0 << 8) /* Idle */ +#define SCB_STCUSUSP (1 << 8) /* Suspended */ +#define SCB_STCUACTV (2 << 8) /* Active */ +#define SCB_STRNR (1 << 12) /* Receive unit not ready */ +#define SCB_STCNA (1 << 13) /* Command unit not ready */ +#define SCB_STFR (1 << 14) /* Frame received */ +#define SCB_STCX (1 << 15) /* Command completed */ + unsigned short scb_command; /* Next command */ +#define SCB_CMDRXSTART (1 << 4) /* Start (at rfa_offset) */ +#define SCB_CMDRXRESUME (2 << 4) /* Resume reception */ +#define SCB_CMDRXSUSPEND (3 << 4) /* Suspend reception */ +#define SCB_CMDRXABORT (4 << 4) /* Abort reception */ +#define SCB_CMDCUCSTART (1 << 8) /* Start (at cbl_offset) */ +#define SCB_CMDCUCRESUME (2 << 8) /* Resume execution */ +#define SCB_CMDCUCSUSPEND (3 << 8) /* Suspend execution */ +#define SCB_CMDCUCABORT (4 << 8) /* Abort execution */ +#define SCB_CMDACKRNR (1 << 12) /* Ack RU not ready */ +#define SCB_CMDACKCNA (1 << 13) /* Ack CU not ready */ +#define SCB_CMDACKFR (1 << 14) /* Ack Frame received */ +#define SCB_CMDACKCX (1 << 15) /* Ack Command complete */ + unsigned short scb_cbl_offset; /* Offset of first command unit */ + unsigned short scb_rfa_offset; /* Offset of first receive frame area */ + unsigned short scb_crc_errors; /* Properly aligned frame with CRC error*/ + unsigned short scb_aln_errors; /* Misaligned frames */ + unsigned short scb_rsc_errors; /* Frames lost due to no space */ + unsigned short scb_ovn_errors; /* Frames lost due to slow bus */ +} scb_t; + +typedef struct { /* iscp */ + unsigned short iscp_busy; /* set by CPU before CA */ + unsigned short iscp_offset; /* offset of SCB */ + unsigned short iscp_basel; /* base of SCB */ + unsigned short iscp_baseh; +} iscp_t; + + /* this address must be 0xfff6 */ +typedef struct { /* scp */ + unsigned short scp_sysbus; /* bus size */ +#define SCP_SY_16BBUS 0x00 +#define SCP_SY_8BBUS 0x01 + unsigned short scp_junk[2]; /* junk */ + unsigned short scp_iscpl; /* lower 16 bits of iscp */ + unsigned short scp_iscph; /* upper 16 bits of iscp */ +} scp_t; + +/* commands */ +#define CMD_NOP 0 +#define CMD_SETADDRESS 1 +#define CMD_CONFIG 2 +#define CMD_SETMULTICAST 3 +#define CMD_TX 4 +#define CMD_TDR 5 +#define CMD_DUMP 6 +#define CMD_DIAGNOSE 7 + +#define CMD_MASK 7 + +#define CMD_INTR (1 << 13) +#define CMD_SUSP (1 << 14) +#define CMD_EOL (1 << 15) + +#define STAT_COLLISIONS (15) +#define STAT_COLLEXCESSIVE (1 << 5) +#define STAT_COLLAFTERTX (1 << 6) +#define STAT_TXDEFERRED (1 << 7) +#define STAT_TXSLOWDMA (1 << 8) +#define STAT_TXLOSTCTS (1 << 9) +#define STAT_NOCARRIER (1 << 10) +#define STAT_FAIL (1 << 11) +#define STAT_ABORTED (1 << 12) +#define STAT_OK (1 << 13) +#define STAT_BUSY (1 << 14) +#define STAT_COMPLETE (1 << 15) +#endif +#endif + +/* + * Ether1 card definitions: + * + * FAST accesses: + * +0 Page register + * 16 pages + * +4 Control + * '1' = reset + * '2' = loopback + * '4' = CA + * '8' = int ack + * + * RAM at address + 0x2000 + * Pod. Prod id = 3 + * Words after ID block [base + 8 words] + * +0 pcb issue (0x0c and 0xf3 invalid) + * +1 - +6 eth hw address + */ diff --git a/drivers/net/ethernet/i825xx/lasi_82596.c b/drivers/net/ethernet/i825xx/lasi_82596.c new file mode 100644 index 000000000000..6eba352c52e0 --- /dev/null +++ b/drivers/net/ethernet/i825xx/lasi_82596.c @@ -0,0 +1,238 @@ +/* lasi_82596.c -- driver for the intel 82596 ethernet controller, as + munged into HPPA boxen . + + This driver is based upon 82596.c, original credits are below... + but there were too many hoops which HP wants jumped through to + keep this code in there in a sane manner. + + 3 primary sources of the mess -- + 1) hppa needs *lots* of cacheline flushing to keep this kind of + MMIO running. + + 2) The 82596 needs to see all of its pointers as their physical + address. Thus virt_to_bus/bus_to_virt are *everywhere*. + + 3) The implementation HP is using seems to be significantly pickier + about when and how the command and RX units are started. some + command ordering was changed. + + Examination of the mach driver leads one to believe that there + might be a saner way to pull this off... anyone who feels like a + full rewrite can be my guest. + + Split 02/13/2000 Sam Creasey (sammy@oh.verio.com) + + 02/01/2000 Initial modifications for parisc by Helge Deller (deller@gmx.de) + 03/02/2000 changes for better/correct(?) cache-flushing (deller) +*/ + +/* 82596.c: A generic 82596 ethernet driver for linux. */ +/* + Based on Apricot.c + Written 1994 by Mark Evans. + This driver is for the Apricot 82596 bus-master interface + + Modularised 12/94 Mark Evans + + + Modified to support the 82596 ethernet chips on 680x0 VME boards. + by Richard Hirst <richard@sleepie.demon.co.uk> + Renamed to be 82596.c + + 980825: Changed to receive directly in to sk_buffs which are + allocated at open() time. Eliminates copy on incoming frames + (small ones are still copied). Shared data now held in a + non-cached page, so we can run on 68060 in copyback mode. + + TBD: + * look at deferring rx frames rather than discarding (as per tulip) + * handle tx ring full as per tulip + * performance test to tune rx_copybreak + + Most of my modifications relate to the braindead big-endian + implementation by Intel. When the i596 is operating in + 'big-endian' mode, it thinks a 32 bit value of 0x12345678 + should be stored as 0x56781234. This is a real pain, when + you have linked lists which are shared by the 680x0 and the + i596. + + Driver skeleton + Written 1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, + National Security Agency. This software may only be used and distributed + according to the terms of the GNU General Public License as modified by SRC, + incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 + + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/pdc.h> +#include <asm/parisc-device.h> + +#define LASI_82596_DRIVER_VERSION "LASI 82596 driver - Revision: 1.30" + +#define PA_I82596_RESET 0 /* Offsets relative to LASI-LAN-Addr.*/ +#define PA_CPU_PORT_L_ACCESS 4 +#define PA_CHANNEL_ATTENTION 8 + +#define OPT_SWAP_PORT 0x0001 /* Need to wordswp on the MPU port */ + +#define DMA_ALLOC dma_alloc_noncoherent +#define DMA_FREE dma_free_noncoherent +#define DMA_WBACK(ndev, addr, len) \ + do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_TO_DEVICE); } while (0) + +#define DMA_INV(ndev, addr, len) \ + do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_FROM_DEVICE); } while (0) + +#define DMA_WBACK_INV(ndev, addr, len) \ + do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_BIDIRECTIONAL); } while (0) + +#define SYSBUS 0x0000006c; + +/* big endian CPU, 82596 "big" endian mode */ +#define SWAP32(x) (((u32)(x)<<16) | ((((u32)(x)))>>16)) +#define SWAP16(x) (x) + +#include "lib82596.c" + +MODULE_AUTHOR("Richard Hirst"); +MODULE_DESCRIPTION("i82596 driver"); +MODULE_LICENSE("GPL"); +module_param(i596_debug, int, 0); +MODULE_PARM_DESC(i596_debug, "lasi_82596 debug mask"); + +static inline void ca(struct net_device *dev) +{ + gsc_writel(0, dev->base_addr + PA_CHANNEL_ATTENTION); +} + + +static void mpu_port(struct net_device *dev, int c, dma_addr_t x) +{ + struct i596_private *lp = netdev_priv(dev); + + u32 v = (u32) (c) | (u32) (x); + u16 a, b; + + if (lp->options & OPT_SWAP_PORT) { + a = v >> 16; + b = v & 0xffff; + } else { + a = v & 0xffff; + b = v >> 16; + } + + gsc_writel(a, dev->base_addr + PA_CPU_PORT_L_ACCESS); + udelay(1); + gsc_writel(b, dev->base_addr + PA_CPU_PORT_L_ACCESS); +} + +#define LAN_PROM_ADDR 0xF0810000 + +static int __devinit +lan_init_chip(struct parisc_device *dev) +{ + struct net_device *netdevice; + struct i596_private *lp; + int retval; + int i; + + if (!dev->irq) { + printk(KERN_ERR "%s: IRQ not found for i82596 at 0x%lx\n", + __FILE__, (unsigned long)dev->hpa.start); + return -ENODEV; + } + + printk(KERN_INFO "Found i82596 at 0x%lx, IRQ %d\n", + (unsigned long)dev->hpa.start, dev->irq); + + netdevice = alloc_etherdev(sizeof(struct i596_private)); + if (!netdevice) + return -ENOMEM; + SET_NETDEV_DEV(netdevice, &dev->dev); + parisc_set_drvdata (dev, netdevice); + + netdevice->base_addr = dev->hpa.start; + netdevice->irq = dev->irq; + + if (pdc_lan_station_id(netdevice->dev_addr, netdevice->base_addr)) { + for (i = 0; i < 6; i++) { + netdevice->dev_addr[i] = gsc_readb(LAN_PROM_ADDR + i); + } + printk(KERN_INFO + "%s: MAC of HP700 LAN read from EEPROM\n", __FILE__); + } + + lp = netdev_priv(netdevice); + lp->options = dev->id.sversion == 0x72 ? OPT_SWAP_PORT : 0; + + retval = i82596_probe(netdevice); + if (retval) { + free_netdev(netdevice); + return -ENODEV; + } + return retval; +} + +static int __devexit lan_remove_chip (struct parisc_device *pdev) +{ + struct net_device *dev = parisc_get_drvdata(pdev); + struct i596_private *lp = netdev_priv(dev); + + unregister_netdev (dev); + DMA_FREE(&pdev->dev, sizeof(struct i596_private), + (void *)lp->dma, lp->dma_addr); + free_netdev (dev); + return 0; +} + +static struct parisc_device_id lan_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008a }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00072 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(parisc, lan_tbl); + +static struct parisc_driver lan_driver = { + .name = "lasi_82596", + .id_table = lan_tbl, + .probe = lan_init_chip, + .remove = __devexit_p(lan_remove_chip), +}; + +static int __devinit lasi_82596_init(void) +{ + printk(KERN_INFO LASI_82596_DRIVER_VERSION "\n"); + return register_parisc_driver(&lan_driver); +} + +module_init(lasi_82596_init); + +static void __exit lasi_82596_exit(void) +{ + unregister_parisc_driver(&lan_driver); +} + +module_exit(lasi_82596_exit); diff --git a/drivers/net/ethernet/i825xx/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c new file mode 100644 index 000000000000..9e042894479b --- /dev/null +++ b/drivers/net/ethernet/i825xx/lib82596.c @@ -0,0 +1,1412 @@ +/* lasi_82596.c -- driver for the intel 82596 ethernet controller, as + munged into HPPA boxen . + + This driver is based upon 82596.c, original credits are below... + but there were too many hoops which HP wants jumped through to + keep this code in there in a sane manner. + + 3 primary sources of the mess -- + 1) hppa needs *lots* of cacheline flushing to keep this kind of + MMIO running. + + 2) The 82596 needs to see all of its pointers as their physical + address. Thus virt_to_bus/bus_to_virt are *everywhere*. + + 3) The implementation HP is using seems to be significantly pickier + about when and how the command and RX units are started. some + command ordering was changed. + + Examination of the mach driver leads one to believe that there + might be a saner way to pull this off... anyone who feels like a + full rewrite can be my guest. + + Split 02/13/2000 Sam Creasey (sammy@oh.verio.com) + + 02/01/2000 Initial modifications for parisc by Helge Deller (deller@gmx.de) + 03/02/2000 changes for better/correct(?) cache-flushing (deller) +*/ + +/* 82596.c: A generic 82596 ethernet driver for linux. */ +/* + Based on Apricot.c + Written 1994 by Mark Evans. + This driver is for the Apricot 82596 bus-master interface + + Modularised 12/94 Mark Evans + + + Modified to support the 82596 ethernet chips on 680x0 VME boards. + by Richard Hirst <richard@sleepie.demon.co.uk> + Renamed to be 82596.c + + 980825: Changed to receive directly in to sk_buffs which are + allocated at open() time. Eliminates copy on incoming frames + (small ones are still copied). Shared data now held in a + non-cached page, so we can run on 68060 in copyback mode. + + TBD: + * look at deferring rx frames rather than discarding (as per tulip) + * handle tx ring full as per tulip + * performance test to tune rx_copybreak + + Most of my modifications relate to the braindead big-endian + implementation by Intel. When the i596 is operating in + 'big-endian' mode, it thinks a 32 bit value of 0x12345678 + should be stored as 0x56781234. This is a real pain, when + you have linked lists which are shared by the 680x0 and the + i596. + + Driver skeleton + Written 1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, + National Security Agency. This software may only be used and distributed + according to the terms of the GNU General Public License as modified by SRC, + incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 + + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/gfp.h> + +/* DEBUG flags + */ + +#define DEB_INIT 0x0001 +#define DEB_PROBE 0x0002 +#define DEB_SERIOUS 0x0004 +#define DEB_ERRORS 0x0008 +#define DEB_MULTI 0x0010 +#define DEB_TDR 0x0020 +#define DEB_OPEN 0x0040 +#define DEB_RESET 0x0080 +#define DEB_ADDCMD 0x0100 +#define DEB_STATUS 0x0200 +#define DEB_STARTTX 0x0400 +#define DEB_RXADDR 0x0800 +#define DEB_TXADDR 0x1000 +#define DEB_RXFRAME 0x2000 +#define DEB_INTS 0x4000 +#define DEB_STRUCT 0x8000 +#define DEB_ANY 0xffff + + +#define DEB(x, y) if (i596_debug & (x)) { y; } + + +/* + * The MPU_PORT command allows direct access to the 82596. With PORT access + * the following commands are available (p5-18). The 32-bit port command + * must be word-swapped with the most significant word written first. + * This only applies to VME boards. + */ +#define PORT_RESET 0x00 /* reset 82596 */ +#define PORT_SELFTEST 0x01 /* selftest */ +#define PORT_ALTSCP 0x02 /* alternate SCB address */ +#define PORT_ALTDUMP 0x03 /* Alternate DUMP address */ + +static int i596_debug = (DEB_SERIOUS|DEB_PROBE); + +/* Copy frames shorter than rx_copybreak, otherwise pass on up in + * a full sized sk_buff. Value of 100 stolen from tulip.c (!alpha). + */ +static int rx_copybreak = 100; + +#define PKT_BUF_SZ 1536 +#define MAX_MC_CNT 64 + +#define ISCP_BUSY 0x0001 + +#define I596_NULL ((u32)0xffffffff) + +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +#define CMD_FLEX 0x0008 /* Enable flexible memory model */ + +enum commands { + CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7 +}; + +#define STAT_C 0x8000 /* Set to 0 after execution */ +#define STAT_B 0x4000 /* Command being executed */ +#define STAT_OK 0x2000 /* Command executed ok */ +#define STAT_A 0x1000 /* Command aborted */ + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define CUC_ABORT 0x0400 +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 +#define RX_ABORT 0x0040 + +#define TX_TIMEOUT (HZ/20) + + +struct i596_reg { + unsigned short porthi; + unsigned short portlo; + u32 ca; +}; + +#define EOF 0x8000 +#define SIZE_MASK 0x3fff + +struct i596_tbd { + unsigned short size; + unsigned short pad; + u32 next; + u32 data; + u32 cache_pad[5]; /* Total 32 bytes... */ +}; + +/* The command structure has two 'next' pointers; v_next is the address of + * the next command as seen by the CPU, b_next is the address of the next + * command as seen by the 82596. The b_next pointer, as used by the 82596 + * always references the status field of the next command, rather than the + * v_next field, because the 82596 is unaware of v_next. It may seem more + * logical to put v_next at the end of the structure, but we cannot do that + * because the 82596 expects other fields to be there, depending on command + * type. + */ + +struct i596_cmd { + struct i596_cmd *v_next; /* Address from CPUs viewpoint */ + unsigned short status; + unsigned short command; + u32 b_next; /* Address from i596 viewpoint */ +}; + +struct tx_cmd { + struct i596_cmd cmd; + u32 tbd; + unsigned short size; + unsigned short pad; + struct sk_buff *skb; /* So we can free it after tx */ + dma_addr_t dma_addr; +#ifdef __LP64__ + u32 cache_pad[6]; /* Total 64 bytes... */ +#else + u32 cache_pad[1]; /* Total 32 bytes... */ +#endif +}; + +struct tdr_cmd { + struct i596_cmd cmd; + unsigned short status; + unsigned short pad; +}; + +struct mc_cmd { + struct i596_cmd cmd; + short mc_cnt; + char mc_addrs[MAX_MC_CNT*6]; +}; + +struct sa_cmd { + struct i596_cmd cmd; + char eth_addr[8]; +}; + +struct cf_cmd { + struct i596_cmd cmd; + char i596_config[16]; +}; + +struct i596_rfd { + unsigned short stat; + unsigned short cmd; + u32 b_next; /* Address from i596 viewpoint */ + u32 rbd; + unsigned short count; + unsigned short size; + struct i596_rfd *v_next; /* Address from CPUs viewpoint */ + struct i596_rfd *v_prev; +#ifndef __LP64__ + u32 cache_pad[2]; /* Total 32 bytes... */ +#endif +}; + +struct i596_rbd { + /* hardware data */ + unsigned short count; + unsigned short zero1; + u32 b_next; + u32 b_data; /* Address from i596 viewpoint */ + unsigned short size; + unsigned short zero2; + /* driver data */ + struct sk_buff *skb; + struct i596_rbd *v_next; + u32 b_addr; /* This rbd addr from i596 view */ + unsigned char *v_data; /* Address from CPUs viewpoint */ + /* Total 32 bytes... */ +#ifdef __LP64__ + u32 cache_pad[4]; +#endif +}; + +/* These values as chosen so struct i596_dma fits in one page... */ + +#define TX_RING_SIZE 32 +#define RX_RING_SIZE 16 + +struct i596_scb { + unsigned short status; + unsigned short command; + u32 cmd; + u32 rfd; + u32 crc_err; + u32 align_err; + u32 resource_err; + u32 over_err; + u32 rcvdt_err; + u32 short_err; + unsigned short t_on; + unsigned short t_off; +}; + +struct i596_iscp { + u32 stat; + u32 scb; +}; + +struct i596_scp { + u32 sysbus; + u32 pad; + u32 iscp; +}; + +struct i596_dma { + struct i596_scp scp __attribute__((aligned(32))); + volatile struct i596_iscp iscp __attribute__((aligned(32))); + volatile struct i596_scb scb __attribute__((aligned(32))); + struct sa_cmd sa_cmd __attribute__((aligned(32))); + struct cf_cmd cf_cmd __attribute__((aligned(32))); + struct tdr_cmd tdr_cmd __attribute__((aligned(32))); + struct mc_cmd mc_cmd __attribute__((aligned(32))); + struct i596_rfd rfds[RX_RING_SIZE] __attribute__((aligned(32))); + struct i596_rbd rbds[RX_RING_SIZE] __attribute__((aligned(32))); + struct tx_cmd tx_cmds[TX_RING_SIZE] __attribute__((aligned(32))); + struct i596_tbd tbds[TX_RING_SIZE] __attribute__((aligned(32))); +}; + +struct i596_private { + struct i596_dma *dma; + u32 stat; + int last_restart; + struct i596_rfd *rfd_head; + struct i596_rbd *rbd_head; + struct i596_cmd *cmd_tail; + struct i596_cmd *cmd_head; + int cmd_backlog; + u32 last_cmd; + int next_tx_cmd; + int options; + spinlock_t lock; /* serialize access to chip */ + dma_addr_t dma_addr; + void __iomem *mpu_port; + void __iomem *ca; +}; + +static const char init_setup[] = +{ + 0x8E, /* length, prefetch on */ + 0xC8, /* fifo to 8, monitor off */ + 0x80, /* don't save bad frames */ + 0x2E, /* No source address insertion, 8 byte preamble */ + 0x00, /* priority and backoff defaults */ + 0x60, /* interframe spacing */ + 0x00, /* slot time LSB */ + 0xf2, /* slot time and retries */ + 0x00, /* promiscuous mode */ + 0x00, /* collision detect */ + 0x40, /* minimum frame length */ + 0xff, + 0x00, + 0x7f /* *multi IA */ }; + +static int i596_open(struct net_device *dev); +static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t i596_interrupt(int irq, void *dev_id); +static int i596_close(struct net_device *dev); +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd); +static void i596_tx_timeout (struct net_device *dev); +static void print_eth(unsigned char *buf, char *str); +static void set_multicast_list(struct net_device *dev); +static inline void ca(struct net_device *dev); +static void mpu_port(struct net_device *dev, int c, dma_addr_t x); + +static int rx_ring_size = RX_RING_SIZE; +static int ticks_limit = 100; +static int max_cmd_backlog = TX_RING_SIZE-1; + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void i596_poll_controller(struct net_device *dev); +#endif + + +static inline int wait_istat(struct net_device *dev, struct i596_dma *dma, int delcnt, char *str) +{ + DMA_INV(dev, &(dma->iscp), sizeof(struct i596_iscp)); + while (--delcnt && dma->iscp.stat) { + udelay(10); + DMA_INV(dev, &(dma->iscp), sizeof(struct i596_iscp)); + } + if (!delcnt) { + printk(KERN_ERR "%s: %s, iscp.stat %04x, didn't clear\n", + dev->name, str, SWAP16(dma->iscp.stat)); + return -1; + } else + return 0; +} + + +static inline int wait_cmd(struct net_device *dev, struct i596_dma *dma, int delcnt, char *str) +{ + DMA_INV(dev, &(dma->scb), sizeof(struct i596_scb)); + while (--delcnt && dma->scb.command) { + udelay(10); + DMA_INV(dev, &(dma->scb), sizeof(struct i596_scb)); + } + if (!delcnt) { + printk(KERN_ERR "%s: %s, status %4.4x, cmd %4.4x.\n", + dev->name, str, + SWAP16(dma->scb.status), + SWAP16(dma->scb.command)); + return -1; + } else + return 0; +} + + +static void i596_display_data(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + struct i596_dma *dma = lp->dma; + struct i596_cmd *cmd; + struct i596_rfd *rfd; + struct i596_rbd *rbd; + + printk(KERN_DEBUG "lp and scp at %p, .sysbus = %08x, .iscp = %08x\n", + &dma->scp, dma->scp.sysbus, SWAP32(dma->scp.iscp)); + printk(KERN_DEBUG "iscp at %p, iscp.stat = %08x, .scb = %08x\n", + &dma->iscp, SWAP32(dma->iscp.stat), SWAP32(dma->iscp.scb)); + printk(KERN_DEBUG "scb at %p, scb.status = %04x, .command = %04x," + " .cmd = %08x, .rfd = %08x\n", + &dma->scb, SWAP16(dma->scb.status), SWAP16(dma->scb.command), + SWAP16(dma->scb.cmd), SWAP32(dma->scb.rfd)); + printk(KERN_DEBUG " errors: crc %x, align %x, resource %x," + " over %x, rcvdt %x, short %x\n", + SWAP32(dma->scb.crc_err), SWAP32(dma->scb.align_err), + SWAP32(dma->scb.resource_err), SWAP32(dma->scb.over_err), + SWAP32(dma->scb.rcvdt_err), SWAP32(dma->scb.short_err)); + cmd = lp->cmd_head; + while (cmd != NULL) { + printk(KERN_DEBUG + "cmd at %p, .status = %04x, .command = %04x," + " .b_next = %08x\n", + cmd, SWAP16(cmd->status), SWAP16(cmd->command), + SWAP32(cmd->b_next)); + cmd = cmd->v_next; + } + rfd = lp->rfd_head; + printk(KERN_DEBUG "rfd_head = %p\n", rfd); + do { + printk(KERN_DEBUG + " %p .stat %04x, .cmd %04x, b_next %08x, rbd %08x," + " count %04x\n", + rfd, SWAP16(rfd->stat), SWAP16(rfd->cmd), + SWAP32(rfd->b_next), SWAP32(rfd->rbd), + SWAP16(rfd->count)); + rfd = rfd->v_next; + } while (rfd != lp->rfd_head); + rbd = lp->rbd_head; + printk(KERN_DEBUG "rbd_head = %p\n", rbd); + do { + printk(KERN_DEBUG + " %p .count %04x, b_next %08x, b_data %08x," + " size %04x\n", + rbd, SWAP16(rbd->count), SWAP32(rbd->b_next), + SWAP32(rbd->b_data), SWAP16(rbd->size)); + rbd = rbd->v_next; + } while (rbd != lp->rbd_head); + DMA_INV(dev, dma, sizeof(struct i596_dma)); +} + + +#define virt_to_dma(lp, v) ((lp)->dma_addr + (dma_addr_t)((unsigned long)(v)-(unsigned long)((lp)->dma))) + +static inline int init_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + struct i596_dma *dma = lp->dma; + int i; + struct i596_rfd *rfd; + struct i596_rbd *rbd; + + /* First build the Receive Buffer Descriptor List */ + + for (i = 0, rbd = dma->rbds; i < rx_ring_size; i++, rbd++) { + dma_addr_t dma_addr; + struct sk_buff *skb; + + skb = netdev_alloc_skb_ip_align(dev, PKT_BUF_SZ); + if (skb == NULL) + return -1; + dma_addr = dma_map_single(dev->dev.parent, skb->data, + PKT_BUF_SZ, DMA_FROM_DEVICE); + rbd->v_next = rbd+1; + rbd->b_next = SWAP32(virt_to_dma(lp, rbd+1)); + rbd->b_addr = SWAP32(virt_to_dma(lp, rbd)); + rbd->skb = skb; + rbd->v_data = skb->data; + rbd->b_data = SWAP32(dma_addr); + rbd->size = SWAP16(PKT_BUF_SZ); + } + lp->rbd_head = dma->rbds; + rbd = dma->rbds + rx_ring_size - 1; + rbd->v_next = dma->rbds; + rbd->b_next = SWAP32(virt_to_dma(lp, dma->rbds)); + + /* Now build the Receive Frame Descriptor List */ + + for (i = 0, rfd = dma->rfds; i < rx_ring_size; i++, rfd++) { + rfd->rbd = I596_NULL; + rfd->v_next = rfd+1; + rfd->v_prev = rfd-1; + rfd->b_next = SWAP32(virt_to_dma(lp, rfd+1)); + rfd->cmd = SWAP16(CMD_FLEX); + } + lp->rfd_head = dma->rfds; + dma->scb.rfd = SWAP32(virt_to_dma(lp, dma->rfds)); + rfd = dma->rfds; + rfd->rbd = SWAP32(virt_to_dma(lp, lp->rbd_head)); + rfd->v_prev = dma->rfds + rx_ring_size - 1; + rfd = dma->rfds + rx_ring_size - 1; + rfd->v_next = dma->rfds; + rfd->b_next = SWAP32(virt_to_dma(lp, dma->rfds)); + rfd->cmd = SWAP16(CMD_EOL|CMD_FLEX); + + DMA_WBACK_INV(dev, dma, sizeof(struct i596_dma)); + return 0; +} + +static inline void remove_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + struct i596_rbd *rbd; + int i; + + for (i = 0, rbd = lp->dma->rbds; i < rx_ring_size; i++, rbd++) { + if (rbd->skb == NULL) + break; + dma_unmap_single(dev->dev.parent, + (dma_addr_t)SWAP32(rbd->b_data), + PKT_BUF_SZ, DMA_FROM_DEVICE); + dev_kfree_skb(rbd->skb); + } +} + + +static void rebuild_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + struct i596_dma *dma = lp->dma; + int i; + + /* Ensure rx frame/buffer descriptors are tidy */ + + for (i = 0; i < rx_ring_size; i++) { + dma->rfds[i].rbd = I596_NULL; + dma->rfds[i].cmd = SWAP16(CMD_FLEX); + } + dma->rfds[rx_ring_size-1].cmd = SWAP16(CMD_EOL|CMD_FLEX); + lp->rfd_head = dma->rfds; + dma->scb.rfd = SWAP32(virt_to_dma(lp, dma->rfds)); + lp->rbd_head = dma->rbds; + dma->rfds[0].rbd = SWAP32(virt_to_dma(lp, dma->rbds)); + + DMA_WBACK_INV(dev, dma, sizeof(struct i596_dma)); +} + + +static int init_i596_mem(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + struct i596_dma *dma = lp->dma; + unsigned long flags; + + mpu_port(dev, PORT_RESET, 0); + udelay(100); /* Wait 100us - seems to help */ + + /* change the scp address */ + + lp->last_cmd = jiffies; + + dma->scp.sysbus = SYSBUS; + dma->scp.iscp = SWAP32(virt_to_dma(lp, &(dma->iscp))); + dma->iscp.scb = SWAP32(virt_to_dma(lp, &(dma->scb))); + dma->iscp.stat = SWAP32(ISCP_BUSY); + lp->cmd_backlog = 0; + + lp->cmd_head = NULL; + dma->scb.cmd = I596_NULL; + + DEB(DEB_INIT, printk(KERN_DEBUG "%s: starting i82596.\n", dev->name)); + + DMA_WBACK(dev, &(dma->scp), sizeof(struct i596_scp)); + DMA_WBACK(dev, &(dma->iscp), sizeof(struct i596_iscp)); + DMA_WBACK(dev, &(dma->scb), sizeof(struct i596_scb)); + + mpu_port(dev, PORT_ALTSCP, virt_to_dma(lp, &dma->scp)); + ca(dev); + if (wait_istat(dev, dma, 1000, "initialization timed out")) + goto failed; + DEB(DEB_INIT, printk(KERN_DEBUG + "%s: i82596 initialization successful\n", + dev->name)); + + if (request_irq(dev->irq, i596_interrupt, 0, "i82596", dev)) { + printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq); + goto failed; + } + + /* Ensure rx frame/buffer descriptors are tidy */ + rebuild_rx_bufs(dev); + + dma->scb.command = 0; + DMA_WBACK(dev, &(dma->scb), sizeof(struct i596_scb)); + + DEB(DEB_INIT, printk(KERN_DEBUG + "%s: queuing CmdConfigure\n", dev->name)); + memcpy(dma->cf_cmd.i596_config, init_setup, 14); + dma->cf_cmd.cmd.command = SWAP16(CmdConfigure); + DMA_WBACK(dev, &(dma->cf_cmd), sizeof(struct cf_cmd)); + i596_add_cmd(dev, &dma->cf_cmd.cmd); + + DEB(DEB_INIT, printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name)); + memcpy(dma->sa_cmd.eth_addr, dev->dev_addr, 6); + dma->sa_cmd.cmd.command = SWAP16(CmdSASetup); + DMA_WBACK(dev, &(dma->sa_cmd), sizeof(struct sa_cmd)); + i596_add_cmd(dev, &dma->sa_cmd.cmd); + + DEB(DEB_INIT, printk(KERN_DEBUG "%s: queuing CmdTDR\n", dev->name)); + dma->tdr_cmd.cmd.command = SWAP16(CmdTDR); + DMA_WBACK(dev, &(dma->tdr_cmd), sizeof(struct tdr_cmd)); + i596_add_cmd(dev, &dma->tdr_cmd.cmd); + + spin_lock_irqsave (&lp->lock, flags); + + if (wait_cmd(dev, dma, 1000, "timed out waiting to issue RX_START")) { + spin_unlock_irqrestore (&lp->lock, flags); + goto failed_free_irq; + } + DEB(DEB_INIT, printk(KERN_DEBUG "%s: Issuing RX_START\n", dev->name)); + dma->scb.command = SWAP16(RX_START); + dma->scb.rfd = SWAP32(virt_to_dma(lp, dma->rfds)); + DMA_WBACK(dev, &(dma->scb), sizeof(struct i596_scb)); + + ca(dev); + + spin_unlock_irqrestore (&lp->lock, flags); + if (wait_cmd(dev, dma, 1000, "RX_START not processed")) + goto failed_free_irq; + DEB(DEB_INIT, printk(KERN_DEBUG + "%s: Receive unit started OK\n", dev->name)); + return 0; + +failed_free_irq: + free_irq(dev->irq, dev); +failed: + printk(KERN_ERR "%s: Failed to initialise 82596\n", dev->name); + mpu_port(dev, PORT_RESET, 0); + return -1; +} + + +static inline int i596_rx(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + struct i596_rfd *rfd; + struct i596_rbd *rbd; + int frames = 0; + + DEB(DEB_RXFRAME, printk(KERN_DEBUG + "i596_rx(), rfd_head %p, rbd_head %p\n", + lp->rfd_head, lp->rbd_head)); + + + rfd = lp->rfd_head; /* Ref next frame to check */ + + DMA_INV(dev, rfd, sizeof(struct i596_rfd)); + while (rfd->stat & SWAP16(STAT_C)) { /* Loop while complete frames */ + if (rfd->rbd == I596_NULL) + rbd = NULL; + else if (rfd->rbd == lp->rbd_head->b_addr) { + rbd = lp->rbd_head; + DMA_INV(dev, rbd, sizeof(struct i596_rbd)); + } else { + printk(KERN_ERR "%s: rbd chain broken!\n", dev->name); + /* XXX Now what? */ + rbd = NULL; + } + DEB(DEB_RXFRAME, printk(KERN_DEBUG + " rfd %p, rfd.rbd %08x, rfd.stat %04x\n", + rfd, rfd->rbd, rfd->stat)); + + if (rbd != NULL && (rfd->stat & SWAP16(STAT_OK))) { + /* a good frame */ + int pkt_len = SWAP16(rbd->count) & 0x3fff; + struct sk_buff *skb = rbd->skb; + int rx_in_place = 0; + + DEB(DEB_RXADDR, print_eth(rbd->v_data, "received")); + frames++; + + /* Check if the packet is long enough to just accept + * without copying to a properly sized skbuff. + */ + + if (pkt_len > rx_copybreak) { + struct sk_buff *newskb; + dma_addr_t dma_addr; + + dma_unmap_single(dev->dev.parent, + (dma_addr_t)SWAP32(rbd->b_data), + PKT_BUF_SZ, DMA_FROM_DEVICE); + /* Get fresh skbuff to replace filled one. */ + newskb = netdev_alloc_skb_ip_align(dev, + PKT_BUF_SZ); + if (newskb == NULL) { + skb = NULL; /* drop pkt */ + goto memory_squeeze; + } + + /* Pass up the skb already on the Rx ring. */ + skb_put(skb, pkt_len); + rx_in_place = 1; + rbd->skb = newskb; + dma_addr = dma_map_single(dev->dev.parent, + newskb->data, + PKT_BUF_SZ, + DMA_FROM_DEVICE); + rbd->v_data = newskb->data; + rbd->b_data = SWAP32(dma_addr); + DMA_WBACK_INV(dev, rbd, sizeof(struct i596_rbd)); + } else + skb = netdev_alloc_skb_ip_align(dev, pkt_len); +memory_squeeze: + if (skb == NULL) { + /* XXX tulip.c can defer packets here!! */ + printk(KERN_ERR + "%s: i596_rx Memory squeeze, dropping packet.\n", + dev->name); + dev->stats.rx_dropped++; + } else { + if (!rx_in_place) { + /* 16 byte align the data fields */ + dma_sync_single_for_cpu(dev->dev.parent, + (dma_addr_t)SWAP32(rbd->b_data), + PKT_BUF_SZ, DMA_FROM_DEVICE); + memcpy(skb_put(skb, pkt_len), rbd->v_data, pkt_len); + dma_sync_single_for_device(dev->dev.parent, + (dma_addr_t)SWAP32(rbd->b_data), + PKT_BUF_SZ, DMA_FROM_DEVICE); + } + skb->len = pkt_len; + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + } else { + DEB(DEB_ERRORS, printk(KERN_DEBUG + "%s: Error, rfd.stat = 0x%04x\n", + dev->name, rfd->stat)); + dev->stats.rx_errors++; + if (rfd->stat & SWAP16(0x0100)) + dev->stats.collisions++; + if (rfd->stat & SWAP16(0x8000)) + dev->stats.rx_length_errors++; + if (rfd->stat & SWAP16(0x0001)) + dev->stats.rx_over_errors++; + if (rfd->stat & SWAP16(0x0002)) + dev->stats.rx_fifo_errors++; + if (rfd->stat & SWAP16(0x0004)) + dev->stats.rx_frame_errors++; + if (rfd->stat & SWAP16(0x0008)) + dev->stats.rx_crc_errors++; + if (rfd->stat & SWAP16(0x0010)) + dev->stats.rx_length_errors++; + } + + /* Clear the buffer descriptor count and EOF + F flags */ + + if (rbd != NULL && (rbd->count & SWAP16(0x4000))) { + rbd->count = 0; + lp->rbd_head = rbd->v_next; + DMA_WBACK_INV(dev, rbd, sizeof(struct i596_rbd)); + } + + /* Tidy the frame descriptor, marking it as end of list */ + + rfd->rbd = I596_NULL; + rfd->stat = 0; + rfd->cmd = SWAP16(CMD_EOL|CMD_FLEX); + rfd->count = 0; + + /* Update record of next frame descriptor to process */ + + lp->dma->scb.rfd = rfd->b_next; + lp->rfd_head = rfd->v_next; + DMA_WBACK_INV(dev, rfd, sizeof(struct i596_rfd)); + + /* Remove end-of-list from old end descriptor */ + + rfd->v_prev->cmd = SWAP16(CMD_FLEX); + DMA_WBACK_INV(dev, rfd->v_prev, sizeof(struct i596_rfd)); + rfd = lp->rfd_head; + DMA_INV(dev, rfd, sizeof(struct i596_rfd)); + } + + DEB(DEB_RXFRAME, printk(KERN_DEBUG "frames %d\n", frames)); + + return 0; +} + + +static inline void i596_cleanup_cmd(struct net_device *dev, struct i596_private *lp) +{ + struct i596_cmd *ptr; + + while (lp->cmd_head != NULL) { + ptr = lp->cmd_head; + lp->cmd_head = ptr->v_next; + lp->cmd_backlog--; + + switch (SWAP16(ptr->command) & 0x7) { + case CmdTx: + { + struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; + struct sk_buff *skb = tx_cmd->skb; + dma_unmap_single(dev->dev.parent, + tx_cmd->dma_addr, + skb->len, DMA_TO_DEVICE); + + dev_kfree_skb(skb); + + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; + + ptr->v_next = NULL; + ptr->b_next = I596_NULL; + tx_cmd->cmd.command = 0; /* Mark as free */ + break; + } + default: + ptr->v_next = NULL; + ptr->b_next = I596_NULL; + } + DMA_WBACK_INV(dev, ptr, sizeof(struct i596_cmd)); + } + + wait_cmd(dev, lp->dma, 100, "i596_cleanup_cmd timed out"); + lp->dma->scb.cmd = I596_NULL; + DMA_WBACK(dev, &(lp->dma->scb), sizeof(struct i596_scb)); +} + + +static inline void i596_reset(struct net_device *dev, struct i596_private *lp) +{ + unsigned long flags; + + DEB(DEB_RESET, printk(KERN_DEBUG "i596_reset\n")); + + spin_lock_irqsave (&lp->lock, flags); + + wait_cmd(dev, lp->dma, 100, "i596_reset timed out"); + + netif_stop_queue(dev); + + /* FIXME: this command might cause an lpmc */ + lp->dma->scb.command = SWAP16(CUC_ABORT | RX_ABORT); + DMA_WBACK(dev, &(lp->dma->scb), sizeof(struct i596_scb)); + ca(dev); + + /* wait for shutdown */ + wait_cmd(dev, lp->dma, 1000, "i596_reset 2 timed out"); + spin_unlock_irqrestore (&lp->lock, flags); + + i596_cleanup_cmd(dev, lp); + i596_rx(dev); + + netif_start_queue(dev); + init_i596_mem(dev); +} + + +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) +{ + struct i596_private *lp = netdev_priv(dev); + struct i596_dma *dma = lp->dma; + unsigned long flags; + + DEB(DEB_ADDCMD, printk(KERN_DEBUG "i596_add_cmd cmd_head %p\n", + lp->cmd_head)); + + cmd->status = 0; + cmd->command |= SWAP16(CMD_EOL | CMD_INTR); + cmd->v_next = NULL; + cmd->b_next = I596_NULL; + DMA_WBACK(dev, cmd, sizeof(struct i596_cmd)); + + spin_lock_irqsave (&lp->lock, flags); + + if (lp->cmd_head != NULL) { + lp->cmd_tail->v_next = cmd; + lp->cmd_tail->b_next = SWAP32(virt_to_dma(lp, &cmd->status)); + DMA_WBACK(dev, lp->cmd_tail, sizeof(struct i596_cmd)); + } else { + lp->cmd_head = cmd; + wait_cmd(dev, dma, 100, "i596_add_cmd timed out"); + dma->scb.cmd = SWAP32(virt_to_dma(lp, &cmd->status)); + dma->scb.command = SWAP16(CUC_START); + DMA_WBACK(dev, &(dma->scb), sizeof(struct i596_scb)); + ca(dev); + } + lp->cmd_tail = cmd; + lp->cmd_backlog++; + + spin_unlock_irqrestore (&lp->lock, flags); + + if (lp->cmd_backlog > max_cmd_backlog) { + unsigned long tickssofar = jiffies - lp->last_cmd; + + if (tickssofar < ticks_limit) + return; + + printk(KERN_ERR + "%s: command unit timed out, status resetting.\n", + dev->name); +#if 1 + i596_reset(dev, lp); +#endif + } +} + +static int i596_open(struct net_device *dev) +{ + DEB(DEB_OPEN, printk(KERN_DEBUG + "%s: i596_open() irq %d.\n", dev->name, dev->irq)); + + if (init_rx_bufs(dev)) { + printk(KERN_ERR "%s: Failed to init rx bufs\n", dev->name); + return -EAGAIN; + } + if (init_i596_mem(dev)) { + printk(KERN_ERR "%s: Failed to init memory\n", dev->name); + goto out_remove_rx_bufs; + } + netif_start_queue(dev); + + return 0; + +out_remove_rx_bufs: + remove_rx_bufs(dev); + return -EAGAIN; +} + +static void i596_tx_timeout (struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + + /* Transmitter timeout, serious problems. */ + DEB(DEB_ERRORS, printk(KERN_DEBUG + "%s: transmit timed out, status resetting.\n", + dev->name)); + + dev->stats.tx_errors++; + + /* Try to restart the adaptor */ + if (lp->last_restart == dev->stats.tx_packets) { + DEB(DEB_ERRORS, printk(KERN_DEBUG "Resetting board.\n")); + /* Shutdown and restart */ + i596_reset (dev, lp); + } else { + /* Issue a channel attention signal */ + DEB(DEB_ERRORS, printk(KERN_DEBUG "Kicking board.\n")); + lp->dma->scb.command = SWAP16(CUC_START | RX_START); + DMA_WBACK_INV(dev, &(lp->dma->scb), sizeof(struct i596_scb)); + ca (dev); + lp->last_restart = dev->stats.tx_packets; + } + + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue (dev); +} + + +static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + struct tx_cmd *tx_cmd; + struct i596_tbd *tbd; + short length = skb->len; + + DEB(DEB_STARTTX, printk(KERN_DEBUG + "%s: i596_start_xmit(%x,%p) called\n", + dev->name, skb->len, skb->data)); + + if (length < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + length = ETH_ZLEN; + } + + netif_stop_queue(dev); + + tx_cmd = lp->dma->tx_cmds + lp->next_tx_cmd; + tbd = lp->dma->tbds + lp->next_tx_cmd; + + if (tx_cmd->cmd.command) { + DEB(DEB_ERRORS, printk(KERN_DEBUG + "%s: xmit ring full, dropping packet.\n", + dev->name)); + dev->stats.tx_dropped++; + + dev_kfree_skb(skb); + } else { + if (++lp->next_tx_cmd == TX_RING_SIZE) + lp->next_tx_cmd = 0; + tx_cmd->tbd = SWAP32(virt_to_dma(lp, tbd)); + tbd->next = I596_NULL; + + tx_cmd->cmd.command = SWAP16(CMD_FLEX | CmdTx); + tx_cmd->skb = skb; + + tx_cmd->pad = 0; + tx_cmd->size = 0; + tbd->pad = 0; + tbd->size = SWAP16(EOF | length); + + tx_cmd->dma_addr = dma_map_single(dev->dev.parent, skb->data, + skb->len, DMA_TO_DEVICE); + tbd->data = SWAP32(tx_cmd->dma_addr); + + DEB(DEB_TXADDR, print_eth(skb->data, "tx-queued")); + DMA_WBACK_INV(dev, tx_cmd, sizeof(struct tx_cmd)); + DMA_WBACK_INV(dev, tbd, sizeof(struct i596_tbd)); + i596_add_cmd(dev, &tx_cmd->cmd); + + dev->stats.tx_packets++; + dev->stats.tx_bytes += length; + } + + netif_start_queue(dev); + + return NETDEV_TX_OK; +} + +static void print_eth(unsigned char *add, char *str) +{ + printk(KERN_DEBUG "i596 0x%p, %pM --> %pM %02X%02X, %s\n", + add, add + 6, add, add[12], add[13], str); +} +static const struct net_device_ops i596_netdev_ops = { + .ndo_open = i596_open, + .ndo_stop = i596_close, + .ndo_start_xmit = i596_start_xmit, + .ndo_set_multicast_list = set_multicast_list, + .ndo_tx_timeout = i596_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = i596_poll_controller, +#endif +}; + +static int __devinit i82596_probe(struct net_device *dev) +{ + int i; + struct i596_private *lp = netdev_priv(dev); + struct i596_dma *dma; + + /* This lot is ensure things have been cache line aligned. */ + BUILD_BUG_ON(sizeof(struct i596_rfd) != 32); + BUILD_BUG_ON(sizeof(struct i596_rbd) & 31); + BUILD_BUG_ON(sizeof(struct tx_cmd) & 31); + BUILD_BUG_ON(sizeof(struct i596_tbd) != 32); +#ifndef __LP64__ + BUILD_BUG_ON(sizeof(struct i596_dma) > 4096); +#endif + + if (!dev->base_addr || !dev->irq) + return -ENODEV; + + dma = (struct i596_dma *) DMA_ALLOC(dev->dev.parent, + sizeof(struct i596_dma), &lp->dma_addr, GFP_KERNEL); + if (!dma) { + printk(KERN_ERR "%s: Couldn't get shared memory\n", __FILE__); + return -ENOMEM; + } + + dev->netdev_ops = &i596_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + memset(dma, 0, sizeof(struct i596_dma)); + lp->dma = dma; + + dma->scb.command = 0; + dma->scb.cmd = I596_NULL; + dma->scb.rfd = I596_NULL; + spin_lock_init(&lp->lock); + + DMA_WBACK_INV(dev, dma, sizeof(struct i596_dma)); + + i = register_netdev(dev); + if (i) { + DMA_FREE(dev->dev.parent, sizeof(struct i596_dma), + (void *)dma, lp->dma_addr); + return i; + } + + DEB(DEB_PROBE, printk(KERN_INFO "%s: 82596 at %#3lx, %pM IRQ %d.\n", + dev->name, dev->base_addr, dev->dev_addr, + dev->irq)); + DEB(DEB_INIT, printk(KERN_INFO + "%s: dma at 0x%p (%d bytes), lp->scb at 0x%p\n", + dev->name, dma, (int)sizeof(struct i596_dma), + &dma->scb)); + + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void i596_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + i596_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +static irqreturn_t i596_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct i596_private *lp; + struct i596_dma *dma; + unsigned short status, ack_cmd = 0; + + lp = netdev_priv(dev); + dma = lp->dma; + + spin_lock (&lp->lock); + + wait_cmd(dev, dma, 100, "i596 interrupt, timeout"); + status = SWAP16(dma->scb.status); + + DEB(DEB_INTS, printk(KERN_DEBUG + "%s: i596 interrupt, IRQ %d, status %4.4x.\n", + dev->name, dev->irq, status)); + + ack_cmd = status & 0xf000; + + if (!ack_cmd) { + DEB(DEB_ERRORS, printk(KERN_DEBUG + "%s: interrupt with no events\n", + dev->name)); + spin_unlock (&lp->lock); + return IRQ_NONE; + } + + if ((status & 0x8000) || (status & 0x2000)) { + struct i596_cmd *ptr; + + if ((status & 0x8000)) + DEB(DEB_INTS, + printk(KERN_DEBUG + "%s: i596 interrupt completed command.\n", + dev->name)); + if ((status & 0x2000)) + DEB(DEB_INTS, + printk(KERN_DEBUG + "%s: i596 interrupt command unit inactive %x.\n", + dev->name, status & 0x0700)); + + while (lp->cmd_head != NULL) { + DMA_INV(dev, lp->cmd_head, sizeof(struct i596_cmd)); + if (!(lp->cmd_head->status & SWAP16(STAT_C))) + break; + + ptr = lp->cmd_head; + + DEB(DEB_STATUS, + printk(KERN_DEBUG + "cmd_head->status = %04x, ->command = %04x\n", + SWAP16(lp->cmd_head->status), + SWAP16(lp->cmd_head->command))); + lp->cmd_head = ptr->v_next; + lp->cmd_backlog--; + + switch (SWAP16(ptr->command) & 0x7) { + case CmdTx: + { + struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; + struct sk_buff *skb = tx_cmd->skb; + + if (ptr->status & SWAP16(STAT_OK)) { + DEB(DEB_TXADDR, + print_eth(skb->data, "tx-done")); + } else { + dev->stats.tx_errors++; + if (ptr->status & SWAP16(0x0020)) + dev->stats.collisions++; + if (!(ptr->status & SWAP16(0x0040))) + dev->stats.tx_heartbeat_errors++; + if (ptr->status & SWAP16(0x0400)) + dev->stats.tx_carrier_errors++; + if (ptr->status & SWAP16(0x0800)) + dev->stats.collisions++; + if (ptr->status & SWAP16(0x1000)) + dev->stats.tx_aborted_errors++; + } + dma_unmap_single(dev->dev.parent, + tx_cmd->dma_addr, + skb->len, DMA_TO_DEVICE); + dev_kfree_skb_irq(skb); + + tx_cmd->cmd.command = 0; /* Mark free */ + break; + } + case CmdTDR: + { + unsigned short status = SWAP16(((struct tdr_cmd *)ptr)->status); + + if (status & 0x8000) { + DEB(DEB_ANY, + printk(KERN_DEBUG "%s: link ok.\n", + dev->name)); + } else { + if (status & 0x4000) + printk(KERN_ERR + "%s: Transceiver problem.\n", + dev->name); + if (status & 0x2000) + printk(KERN_ERR + "%s: Termination problem.\n", + dev->name); + if (status & 0x1000) + printk(KERN_ERR + "%s: Short circuit.\n", + dev->name); + + DEB(DEB_TDR, + printk(KERN_DEBUG "%s: Time %d.\n", + dev->name, status & 0x07ff)); + } + break; + } + case CmdConfigure: + /* + * Zap command so set_multicast_list() know + * it is free + */ + ptr->command = 0; + break; + } + ptr->v_next = NULL; + ptr->b_next = I596_NULL; + DMA_WBACK(dev, ptr, sizeof(struct i596_cmd)); + lp->last_cmd = jiffies; + } + + /* This mess is arranging that only the last of any outstanding + * commands has the interrupt bit set. Should probably really + * only add to the cmd queue when the CU is stopped. + */ + ptr = lp->cmd_head; + while ((ptr != NULL) && (ptr != lp->cmd_tail)) { + struct i596_cmd *prev = ptr; + + ptr->command &= SWAP16(0x1fff); + ptr = ptr->v_next; + DMA_WBACK_INV(dev, prev, sizeof(struct i596_cmd)); + } + + if (lp->cmd_head != NULL) + ack_cmd |= CUC_START; + dma->scb.cmd = SWAP32(virt_to_dma(lp, &lp->cmd_head->status)); + DMA_WBACK_INV(dev, &dma->scb, sizeof(struct i596_scb)); + } + if ((status & 0x1000) || (status & 0x4000)) { + if ((status & 0x4000)) + DEB(DEB_INTS, + printk(KERN_DEBUG + "%s: i596 interrupt received a frame.\n", + dev->name)); + i596_rx(dev); + /* Only RX_START if stopped - RGH 07-07-96 */ + if (status & 0x1000) { + if (netif_running(dev)) { + DEB(DEB_ERRORS, + printk(KERN_DEBUG + "%s: i596 interrupt receive unit inactive, status 0x%x\n", + dev->name, status)); + ack_cmd |= RX_START; + dev->stats.rx_errors++; + dev->stats.rx_fifo_errors++; + rebuild_rx_bufs(dev); + } + } + } + wait_cmd(dev, dma, 100, "i596 interrupt, timeout"); + dma->scb.command = SWAP16(ack_cmd); + DMA_WBACK(dev, &dma->scb, sizeof(struct i596_scb)); + + /* DANGER: I suspect that some kind of interrupt + acknowledgement aside from acking the 82596 might be needed + here... but it's running acceptably without */ + + ca(dev); + + wait_cmd(dev, dma, 100, "i596 interrupt, exit timeout"); + DEB(DEB_INTS, printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name)); + + spin_unlock (&lp->lock); + return IRQ_HANDLED; +} + +static int i596_close(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + unsigned long flags; + + netif_stop_queue(dev); + + DEB(DEB_INIT, + printk(KERN_DEBUG + "%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, SWAP16(lp->dma->scb.status))); + + spin_lock_irqsave(&lp->lock, flags); + + wait_cmd(dev, lp->dma, 100, "close1 timed out"); + lp->dma->scb.command = SWAP16(CUC_ABORT | RX_ABORT); + DMA_WBACK(dev, &lp->dma->scb, sizeof(struct i596_scb)); + + ca(dev); + + wait_cmd(dev, lp->dma, 100, "close2 timed out"); + spin_unlock_irqrestore(&lp->lock, flags); + DEB(DEB_STRUCT, i596_display_data(dev)); + i596_cleanup_cmd(dev, lp); + + free_irq(dev->irq, dev); + remove_rx_bufs(dev); + + return 0; +} + +/* + * Set or clear the multicast filter for this adaptor. + */ + +static void set_multicast_list(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + struct i596_dma *dma = lp->dma; + int config = 0, cnt; + + DEB(DEB_MULTI, + printk(KERN_DEBUG + "%s: set multicast list, %d entries, promisc %s, allmulti %s\n", + dev->name, netdev_mc_count(dev), + dev->flags & IFF_PROMISC ? "ON" : "OFF", + dev->flags & IFF_ALLMULTI ? "ON" : "OFF")); + + if ((dev->flags & IFF_PROMISC) && + !(dma->cf_cmd.i596_config[8] & 0x01)) { + dma->cf_cmd.i596_config[8] |= 0x01; + config = 1; + } + if (!(dev->flags & IFF_PROMISC) && + (dma->cf_cmd.i596_config[8] & 0x01)) { + dma->cf_cmd.i596_config[8] &= ~0x01; + config = 1; + } + if ((dev->flags & IFF_ALLMULTI) && + (dma->cf_cmd.i596_config[11] & 0x20)) { + dma->cf_cmd.i596_config[11] &= ~0x20; + config = 1; + } + if (!(dev->flags & IFF_ALLMULTI) && + !(dma->cf_cmd.i596_config[11] & 0x20)) { + dma->cf_cmd.i596_config[11] |= 0x20; + config = 1; + } + if (config) { + if (dma->cf_cmd.cmd.command) + printk(KERN_INFO + "%s: config change request already queued\n", + dev->name); + else { + dma->cf_cmd.cmd.command = SWAP16(CmdConfigure); + DMA_WBACK_INV(dev, &dma->cf_cmd, sizeof(struct cf_cmd)); + i596_add_cmd(dev, &dma->cf_cmd.cmd); + } + } + + cnt = netdev_mc_count(dev); + if (cnt > MAX_MC_CNT) { + cnt = MAX_MC_CNT; + printk(KERN_NOTICE "%s: Only %d multicast addresses supported", + dev->name, cnt); + } + + if (!netdev_mc_empty(dev)) { + struct netdev_hw_addr *ha; + unsigned char *cp; + struct mc_cmd *cmd; + + cmd = &dma->mc_cmd; + cmd->cmd.command = SWAP16(CmdMulticastList); + cmd->mc_cnt = SWAP16(netdev_mc_count(dev) * 6); + cp = cmd->mc_addrs; + netdev_for_each_mc_addr(ha, dev) { + if (!cnt--) + break; + memcpy(cp, ha->addr, 6); + if (i596_debug > 1) + DEB(DEB_MULTI, + printk(KERN_DEBUG + "%s: Adding address %pM\n", + dev->name, cp)); + cp += 6; + } + DMA_WBACK_INV(dev, &dma->mc_cmd, sizeof(struct mc_cmd)); + i596_add_cmd(dev, &cmd->cmd); + } +} diff --git a/drivers/net/ethernet/i825xx/lp486e.c b/drivers/net/ethernet/i825xx/lp486e.c new file mode 100644 index 000000000000..385a95311cd2 --- /dev/null +++ b/drivers/net/ethernet/i825xx/lp486e.c @@ -0,0 +1,1339 @@ +/* Intel Professional Workstation/panther ethernet driver */ +/* lp486e.c: A panther 82596 ethernet driver for linux. */ +/* + History and copyrights: + + Driver skeleton + Written 1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, + National Security Agency. This software may only be used and + distributed according to the terms of the GNU General Public License + as modified by SRC, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Apricot + Written 1994 by Mark Evans. + This driver is for the Apricot 82596 bus-master interface + + Modularised 12/94 Mark Evans + + Professional Workstation + Derived from apricot.c by Ard van Breemen + <ard@murphy.nl>|<ard@cstmel.hobby.nl>|<ard@cstmel.nl.eu.org> + + Credits: + Thanks to Murphy Software BV for letting me write this in their time. + Well, actually, I get paid doing this... + (Also: see http://www.murphy.nl for murphy, and my homepage ~ard for + more information on the Professional Workstation) + + Present version + aeb@cwi.nl +*/ +/* + There are currently two motherboards that I know of in the + professional workstation. The only one that I know is the + intel panther motherboard. -- ard +*/ +/* +The pws is equipped with an intel 82596. This is a very intelligent controller +which runs its own micro-code. Communication with the hostprocessor is done +through linked lists of commands and buffers in the hostprocessors memory. +A complete description of the 82596 is available from intel. Search for +a file called "29021806.pdf". It is a complete description of the chip itself. +To use it for the pws some additions are needed regarding generation of +the PORT and CA signal, and the interrupt glue needed for a pc. +I/O map: +PORT SIZE ACTION MEANING +0xCB0 2 WRITE Lower 16 bits for PORT command +0xCB2 2 WRITE Upper 16 bits for PORT command, and issue of PORT command +0xCB4 1 WRITE Generation of CA signal +0xCB8 1 WRITE Clear interrupt glue +All other communication is through memory! +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/bitops.h> + +#include <asm/io.h> +#include <asm/dma.h> + +#define DRV_NAME "lp486e" + +/* debug print flags */ +#define LOG_SRCDST 0x80000000 +#define LOG_STATINT 0x40000000 +#define LOG_STARTINT 0x20000000 + +#define i596_debug debug + +static int i596_debug = 0; + +static const char * const medianame[] = { + "10baseT", "AUI", + "10baseT-FD", "AUI-FD", +}; + +#define LP486E_TOTAL_SIZE 16 + +#define I596_NULL (0xffffffff) + +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +#define CMD_FLEX 0x0008 /* Enable flexible memory model */ + +enum commands { + CmdNOP = 0, + CmdIASetup = 1, + CmdConfigure = 2, + CmdMulticastList = 3, + CmdTx = 4, + CmdTDR = 5, + CmdDump = 6, + CmdDiagnose = 7 +}; + +#if 0 +static const char *CUcmdnames[8] = { "NOP", "IASetup", "Configure", "MulticastList", + "Tx", "TDR", "Dump", "Diagnose" }; +#endif + +/* Status word bits */ +#define STAT_CX 0x8000 /* The CU finished executing a command + with the Interrupt bit set */ +#define STAT_FR 0x4000 /* The RU finished receiving a frame */ +#define STAT_CNA 0x2000 /* The CU left the active state */ +#define STAT_RNR 0x1000 /* The RU left the active state */ +#define STAT_ACK (STAT_CX | STAT_FR | STAT_CNA | STAT_RNR) +#define STAT_CUS 0x0700 /* Status of CU: 0: idle, 1: suspended, + 2: active, 3-7: unused */ +#define STAT_RUS 0x00f0 /* Status of RU: 0: idle, 1: suspended, + 2: no resources, 4: ready, + 10: no resources due to no more RBDs, + 12: no more RBDs, other: unused */ +#define STAT_T 0x0008 /* Bus throttle timers loaded */ +#define STAT_ZERO 0x0807 /* Always zero */ + +#if 0 +static char *CUstates[8] = { + "idle", "suspended", "active", 0, 0, 0, 0, 0 +}; +static char *RUstates[16] = { + "idle", "suspended", "no resources", 0, "ready", 0, 0, 0, + 0, 0, "no RBDs", 0, "out of RBDs", 0, 0, 0 +}; + +static void +i596_out_status(int status) { + int bad = 0; + char *s; + + printk("status %4.4x:", status); + if (status == 0xffff) + printk(" strange..\n"); + else { + if (status & STAT_CX) + printk(" CU done"); + if (status & STAT_CNA) + printk(" CU stopped"); + if (status & STAT_FR) + printk(" got a frame"); + if (status & STAT_RNR) + printk(" RU stopped"); + if (status & STAT_T) + printk(" throttled"); + if (status & STAT_ZERO) + bad = 1; + s = CUstates[(status & STAT_CUS) >> 8]; + if (!s) + bad = 1; + else + printk(" CU(%s)", s); + s = RUstates[(status & STAT_RUS) >> 4]; + if (!s) + bad = 1; + else + printk(" RU(%s)", s); + if (bad) + printk(" bad status"); + printk("\n"); + } +} +#endif + +/* Command word bits */ +#define ACK_CX 0x8000 +#define ACK_FR 0x4000 +#define ACK_CNA 0x2000 +#define ACK_RNR 0x1000 + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define CUC_ABORT 0x0400 + +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 +#define RX_ABORT 0x0040 + +typedef u32 phys_addr; + +static inline phys_addr +va_to_pa(void *x) { + return x ? virt_to_bus(x) : I596_NULL; +} + +static inline void * +pa_to_va(phys_addr x) { + return (x == I596_NULL) ? NULL : bus_to_virt(x); +} + +/* status bits for cmd */ +#define CMD_STAT_C 0x8000 /* CU command complete */ +#define CMD_STAT_B 0x4000 /* CU command in progress */ +#define CMD_STAT_OK 0x2000 /* CU command completed without errors */ +#define CMD_STAT_A 0x1000 /* CU command abnormally terminated */ + +struct i596_cmd { /* 8 bytes */ + unsigned short status; + unsigned short command; + phys_addr pa_next; /* va_to_pa(struct i596_cmd *next) */ +}; + +#define EOF 0x8000 +#define SIZE_MASK 0x3fff + +struct i596_tbd { + unsigned short size; + unsigned short pad; + phys_addr pa_next; /* va_to_pa(struct i596_tbd *next) */ + phys_addr pa_data; /* va_to_pa(char *data) */ + struct sk_buff *skb; +}; + +struct tx_cmd { + struct i596_cmd cmd; + phys_addr pa_tbd; /* va_to_pa(struct i596_tbd *tbd) */ + unsigned short size; + unsigned short pad; +}; + +/* status bits for rfd */ +#define RFD_STAT_C 0x8000 /* Frame reception complete */ +#define RFD_STAT_B 0x4000 /* Frame reception in progress */ +#define RFD_STAT_OK 0x2000 /* Frame received without errors */ +#define RFD_STATUS 0x1fff +#define RFD_LENGTH_ERR 0x1000 +#define RFD_CRC_ERR 0x0800 +#define RFD_ALIGN_ERR 0x0400 +#define RFD_NOBUFS_ERR 0x0200 +#define RFD_DMA_ERR 0x0100 /* DMA overrun failure to acquire system bus */ +#define RFD_SHORT_FRAME_ERR 0x0080 +#define RFD_NOEOP_ERR 0x0040 +#define RFD_TRUNC_ERR 0x0020 +#define RFD_MULTICAST 0x0002 /* 0: destination had our address + 1: destination was broadcast/multicast */ +#define RFD_COLLISION 0x0001 + +/* receive frame descriptor */ +struct i596_rfd { + unsigned short stat; + unsigned short cmd; + phys_addr pa_next; /* va_to_pa(struct i596_rfd *next) */ + phys_addr pa_rbd; /* va_to_pa(struct i596_rbd *rbd) */ + unsigned short count; + unsigned short size; + char data[1532]; +}; + +#define RBD_EL 0x8000 +#define RBD_P 0x4000 +#define RBD_SIZEMASK 0x3fff +#define RBD_EOF 0x8000 +#define RBD_F 0x4000 + +/* receive buffer descriptor */ +struct i596_rbd { + unsigned short size; + unsigned short pad; + phys_addr pa_next; /* va_to_pa(struct i596_tbd *next) */ + phys_addr pa_data; /* va_to_pa(char *data) */ + phys_addr pa_prev; /* va_to_pa(struct i596_tbd *prev) */ + + /* Driver private part */ + struct sk_buff *skb; +}; + +#define RX_RING_SIZE 64 +#define RX_SKBSIZE (ETH_FRAME_LEN+10) +#define RX_RBD_SIZE 32 + +/* System Control Block - 40 bytes */ +struct i596_scb { + u16 status; /* 0 */ + u16 command; /* 2 */ + phys_addr pa_cmd; /* 4 - va_to_pa(struct i596_cmd *cmd) */ + phys_addr pa_rfd; /* 8 - va_to_pa(struct i596_rfd *rfd) */ + u32 crc_err; /* 12 */ + u32 align_err; /* 16 */ + u32 resource_err; /* 20 */ + u32 over_err; /* 24 */ + u32 rcvdt_err; /* 28 */ + u32 short_err; /* 32 */ + u16 t_on; /* 36 */ + u16 t_off; /* 38 */ +}; + +/* Intermediate System Configuration Pointer - 8 bytes */ +struct i596_iscp { + u32 busy; /* 0 */ + phys_addr pa_scb; /* 4 - va_to_pa(struct i596_scb *scb) */ +}; + +/* System Configuration Pointer - 12 bytes */ +struct i596_scp { + u32 sysbus; /* 0 */ + u32 pad; /* 4 */ + phys_addr pa_iscp; /* 8 - va_to_pa(struct i596_iscp *iscp) */ +}; + +/* Selftest and dump results - needs 16-byte alignment */ +/* + * The size of the dump area is 304 bytes. When the dump is executed + * by the Port command an extra word will be appended to the dump area. + * The extra word is a copy of the Dump status word (containing the + * C, B, OK bits). [I find 0xa006, with a0 for C+OK and 6 for dump] + */ +struct i596_dump { + u16 dump[153]; /* (304 = 130h) + 2 bytes */ +}; + +struct i596_private { /* aligned to a 16-byte boundary */ + struct i596_scp scp; /* 0 - needs 16-byte alignment */ + struct i596_iscp iscp; /* 12 */ + struct i596_scb scb; /* 20 */ + u32 dummy; /* 60 */ + struct i596_dump dump; /* 64 - needs 16-byte alignment */ + + struct i596_cmd set_add; + char eth_addr[8]; /* directly follows set_add */ + + struct i596_cmd set_conf; + char i596_config[16]; /* directly follows set_conf */ + + struct i596_cmd tdr; + unsigned long tdr_stat; /* directly follows tdr */ + + int last_restart; + struct i596_rbd *rbd_list; + struct i596_rbd *rbd_tail; + struct i596_rfd *rx_tail; + struct i596_cmd *cmd_tail; + struct i596_cmd *cmd_head; + int cmd_backlog; + unsigned long last_cmd; + spinlock_t cmd_lock; +}; + +static char init_setup[14] = { + 0x8E, /* length 14 bytes, prefetch on */ + 0xC8, /* default: fifo to 8, monitor off */ + 0x40, /* default: don't save bad frames (apricot.c had 0x80) */ + 0x2E, /* (default is 0x26) + No source address insertion, 8 byte preamble */ + 0x00, /* default priority and backoff */ + 0x60, /* default interframe spacing */ + 0x00, /* default slot time LSB */ + 0xf2, /* default slot time and nr of retries */ + 0x00, /* default various bits + (0: promiscuous mode, 1: broadcast disable, + 2: encoding mode, 3: transmit on no CRS, + 4: no CRC insertion, 5: CRC type, + 6: bit stuffing, 7: padding) */ + 0x00, /* default carrier sense and collision detect */ + 0x40, /* default minimum frame length */ + 0xff, /* (default is 0xff, and that is what apricot.c has; + elp486.c has 0xfb: Enable crc append in memory.) */ + 0x00, /* default: not full duplex */ + 0x7f /* (default is 0x3f) multi IA */ +}; + +static int i596_open(struct net_device *dev); +static netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t i596_interrupt(int irq, void *dev_id); +static int i596_close(struct net_device *dev); +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd); +static void print_eth(char *); +static void set_multicast_list(struct net_device *dev); +static void i596_tx_timeout(struct net_device *dev); + +static int +i596_timeout(struct net_device *dev, char *msg, int ct) { + struct i596_private *lp; + int boguscnt = ct; + + lp = netdev_priv(dev); + while (lp->scb.command) { + if (--boguscnt == 0) { + printk("%s: %s timed out - stat %4.4x, cmd %4.4x\n", + dev->name, msg, + lp->scb.status, lp->scb.command); + return 1; + } + udelay(5); + barrier(); + } + return 0; +} + +static inline int +init_rx_bufs(struct net_device *dev, int num) { + struct i596_private *lp; + struct i596_rfd *rfd; + int i; + // struct i596_rbd *rbd; + + lp = netdev_priv(dev); + lp->scb.pa_rfd = I596_NULL; + + for (i = 0; i < num; i++) { + rfd = kmalloc(sizeof(struct i596_rfd), GFP_KERNEL); + if (rfd == NULL) + break; + + rfd->stat = 0; + rfd->pa_rbd = I596_NULL; + rfd->count = 0; + rfd->size = 1532; + if (i == 0) { + rfd->cmd = CMD_EOL; + lp->rx_tail = rfd; + } else { + rfd->cmd = 0; + } + rfd->pa_next = lp->scb.pa_rfd; + lp->scb.pa_rfd = va_to_pa(rfd); + lp->rx_tail->pa_next = lp->scb.pa_rfd; + } + +#if 0 + for (i = 0; i<RX_RBD_SIZE; i++) { + rbd = kmalloc(sizeof(struct i596_rbd), GFP_KERNEL); + if (rbd) { + rbd->pad = 0; + rbd->count = 0; + rbd->skb = dev_alloc_skb(RX_SKBSIZE); + if (!rbd->skb) { + printk("dev_alloc_skb failed"); + } + rbd->next = rfd->rbd; + if (i) { + rfd->rbd->prev = rbd; + rbd->size = RX_SKBSIZE; + } else { + rbd->size = (RX_SKBSIZE | RBD_EL); + lp->rbd_tail = rbd; + } + + rfd->rbd = rbd; + } else { + printk("Could not kmalloc rbd\n"); + } + } + lp->rbd_tail->next = rfd->rbd; +#endif + return i; +} + +static inline void +remove_rx_bufs(struct net_device *dev) { + struct i596_private *lp; + struct i596_rfd *rfd; + + lp = netdev_priv(dev); + lp->rx_tail->pa_next = I596_NULL; + + do { + rfd = pa_to_va(lp->scb.pa_rfd); + lp->scb.pa_rfd = rfd->pa_next; + kfree(rfd); + } while (rfd != lp->rx_tail); + + lp->rx_tail = NULL; + +#if 0 + for (lp->rbd_list) { + } +#endif +} + +#define PORT_RESET 0x00 /* reset 82596 */ +#define PORT_SELFTEST 0x01 /* selftest */ +#define PORT_ALTSCP 0x02 /* alternate SCB address */ +#define PORT_DUMP 0x03 /* dump */ + +#define IOADDR 0xcb0 /* real constant */ +#define IRQ 10 /* default IRQ - can be changed by ECU */ + +/* The 82596 requires two 16-bit write cycles for a port command */ +static inline void +PORT(phys_addr a, unsigned int cmd) { + if (a & 0xf) + printk("lp486e.c: PORT: address not aligned\n"); + outw(((a & 0xffff) | cmd), IOADDR); + outw(((a>>16) & 0xffff), IOADDR+2); +} + +static inline void +CA(void) { + outb(0, IOADDR+4); + udelay(8); +} + +static inline void +CLEAR_INT(void) { + outb(0, IOADDR+8); +} + +#if 0 +/* selftest or dump */ +static void +i596_port_do(struct net_device *dev, int portcmd, char *cmdname) { + struct i596_private *lp = netdev_priv(dev); + u16 *outp; + int i, m; + + memset((void *)&(lp->dump), 0, sizeof(struct i596_dump)); + outp = &(lp->dump.dump[0]); + + PORT(va_to_pa(outp), portcmd); + mdelay(30); /* random, unmotivated */ + + printk("lp486e i82596 %s result:\n", cmdname); + for (m = ARRAY_SIZE(lp->dump.dump); m && lp->dump.dump[m-1] == 0; m--) + ; + for (i = 0; i < m; i++) { + printk(" %04x", lp->dump.dump[i]); + if (i%8 == 7) + printk("\n"); + } + printk("\n"); +} +#endif + +static int +i596_scp_setup(struct net_device *dev) { + struct i596_private *lp = netdev_priv(dev); + int boguscnt; + + /* Setup SCP, ISCP, SCB */ + /* + * sysbus bits: + * only a single byte is significant - here 0x44 + * 0x80: big endian mode (details depend on stepping) + * 0x40: 1 + * 0x20: interrupt pin is active low + * 0x10: lock function disabled + * 0x08: external triggering of bus throttle timers + * 0x06: 00: 82586 compat mode, 01: segmented mode, 10: linear mode + * 0x01: unused + */ + lp->scp.sysbus = 0x00440000; /* linear mode */ + lp->scp.pad = 0; /* must be zero */ + lp->scp.pa_iscp = va_to_pa(&(lp->iscp)); + + /* + * The CPU sets the ISCP to 1 before it gives the first CA() + */ + lp->iscp.busy = 0x0001; + lp->iscp.pa_scb = va_to_pa(&(lp->scb)); + + lp->scb.command = 0; + lp->scb.status = 0; + lp->scb.pa_cmd = I596_NULL; + /* lp->scb.pa_rfd has been initialised already */ + + lp->last_cmd = jiffies; + lp->cmd_backlog = 0; + lp->cmd_head = NULL; + + /* + * Reset the 82596. + * We need to wait 10 systemclock cycles, and + * 5 serial clock cycles. + */ + PORT(0, PORT_RESET); /* address part ignored */ + udelay(100); + + /* + * Before the CA signal is asserted, the default SCP address + * (0x00fffff4) can be changed to a 16-byte aligned value + */ + PORT(va_to_pa(&lp->scp), PORT_ALTSCP); /* change the scp address */ + + /* + * The initialization procedure begins when a + * Channel Attention signal is asserted after a reset. + */ + + CA(); + + /* + * The ISCP busy is cleared by the 82596 after the SCB address is read. + */ + boguscnt = 100; + while (lp->iscp.busy) { + if (--boguscnt == 0) { + /* No i82596 present? */ + printk("%s: i82596 initialization timed out\n", + dev->name); + return 1; + } + udelay(5); + barrier(); + } + /* I find here boguscnt==100, so no delay was required. */ + + return 0; +} + +static int +init_i596(struct net_device *dev) { + struct i596_private *lp; + + if (i596_scp_setup(dev)) + return 1; + + lp = netdev_priv(dev); + lp->scb.command = 0; + + memcpy ((void *)lp->i596_config, init_setup, 14); + lp->set_conf.command = CmdConfigure; + i596_add_cmd(dev, (void *)&lp->set_conf); + + memcpy ((void *)lp->eth_addr, dev->dev_addr, 6); + lp->set_add.command = CmdIASetup; + i596_add_cmd(dev, (struct i596_cmd *)&lp->set_add); + + lp->tdr.command = CmdTDR; + i596_add_cmd(dev, (struct i596_cmd *)&lp->tdr); + + if (lp->scb.command && i596_timeout(dev, "i82596 init", 200)) + return 1; + + lp->scb.command = RX_START; + CA(); + + barrier(); + + if (lp->scb.command && i596_timeout(dev, "Receive Unit start", 100)) + return 1; + + return 0; +} + +/* Receive a single frame */ +static inline int +i596_rx_one(struct net_device *dev, struct i596_private *lp, + struct i596_rfd *rfd, int *frames) { + + if (rfd->stat & RFD_STAT_OK) { + /* a good frame */ + int pkt_len = (rfd->count & 0x3fff); + struct sk_buff *skb = dev_alloc_skb(pkt_len); + + (*frames)++; + + if (rfd->cmd & CMD_EOL) + printk("Received on EOL\n"); + + if (skb == NULL) { + printk ("%s: i596_rx Memory squeeze, " + "dropping packet.\n", dev->name); + dev->stats.rx_dropped++; + return 1; + } + + memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len); + + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + } else { +#if 0 + printk("Frame reception error status %04x\n", + rfd->stat); +#endif + dev->stats.rx_errors++; + if (rfd->stat & RFD_COLLISION) + dev->stats.collisions++; + if (rfd->stat & RFD_SHORT_FRAME_ERR) + dev->stats.rx_length_errors++; + if (rfd->stat & RFD_DMA_ERR) + dev->stats.rx_over_errors++; + if (rfd->stat & RFD_NOBUFS_ERR) + dev->stats.rx_fifo_errors++; + if (rfd->stat & RFD_ALIGN_ERR) + dev->stats.rx_frame_errors++; + if (rfd->stat & RFD_CRC_ERR) + dev->stats.rx_crc_errors++; + if (rfd->stat & RFD_LENGTH_ERR) + dev->stats.rx_length_errors++; + } + rfd->stat = rfd->count = 0; + return 0; +} + +static int +i596_rx(struct net_device *dev) { + struct i596_private *lp = netdev_priv(dev); + struct i596_rfd *rfd; + int frames = 0; + + while (1) { + rfd = pa_to_va(lp->scb.pa_rfd); + if (!rfd) { + printk(KERN_ERR "i596_rx: NULL rfd?\n"); + return 0; + } +#if 1 + if (rfd->stat && !(rfd->stat & (RFD_STAT_C | RFD_STAT_B))) + printk("SF:%p-%04x\n", rfd, rfd->stat); +#endif + if (!(rfd->stat & RFD_STAT_C)) + break; /* next one not ready */ + if (i596_rx_one(dev, lp, rfd, &frames)) + break; /* out of memory */ + rfd->cmd = CMD_EOL; + lp->rx_tail->cmd = 0; + lp->rx_tail = rfd; + lp->scb.pa_rfd = rfd->pa_next; + barrier(); + } + + return frames; +} + +static void +i596_cleanup_cmd(struct net_device *dev) { + struct i596_private *lp; + struct i596_cmd *cmd; + + lp = netdev_priv(dev); + while (lp->cmd_head) { + cmd = (struct i596_cmd *)lp->cmd_head; + + lp->cmd_head = pa_to_va(lp->cmd_head->pa_next); + lp->cmd_backlog--; + + switch ((cmd->command) & 0x7) { + case CmdTx: { + struct tx_cmd *tx_cmd = (struct tx_cmd *) cmd; + struct i596_tbd * tx_cmd_tbd; + tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd); + + dev_kfree_skb_any(tx_cmd_tbd->skb); + + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)tx_cmd); + netif_wake_queue(dev); + break; + } + case CmdMulticastList: { + // unsigned short count = *((unsigned short *) (ptr + 1)); + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)cmd); + break; + } + default: { + cmd->pa_next = I596_NULL; + break; + } + } + barrier(); + } + + if (lp->scb.command && i596_timeout(dev, "i596_cleanup_cmd", 100)) + ; + + lp->scb.pa_cmd = va_to_pa(lp->cmd_head); +} + +static void i596_reset(struct net_device *dev, struct i596_private *lp, int ioaddr) { + + if (lp->scb.command && i596_timeout(dev, "i596_reset", 100)) + ; + + netif_stop_queue(dev); + + lp->scb.command = CUC_ABORT | RX_ABORT; + CA(); + barrier(); + + /* wait for shutdown */ + if (lp->scb.command && i596_timeout(dev, "i596_reset(2)", 400)) + ; + + i596_cleanup_cmd(dev); + i596_rx(dev); + + netif_start_queue(dev); + /*dev_kfree_skb(skb, FREE_WRITE);*/ + init_i596(dev); +} + +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) { + struct i596_private *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + unsigned long flags; + + cmd->status = 0; + cmd->command |= (CMD_EOL | CMD_INTR); + cmd->pa_next = I596_NULL; + + spin_lock_irqsave(&lp->cmd_lock, flags); + + if (lp->cmd_head) { + lp->cmd_tail->pa_next = va_to_pa(cmd); + } else { + lp->cmd_head = cmd; + if (lp->scb.command && i596_timeout(dev, "i596_add_cmd", 100)) + ; + lp->scb.pa_cmd = va_to_pa(cmd); + lp->scb.command = CUC_START; + CA(); + } + lp->cmd_tail = cmd; + lp->cmd_backlog++; + + lp->cmd_head = pa_to_va(lp->scb.pa_cmd); + spin_unlock_irqrestore(&lp->cmd_lock, flags); + + if (lp->cmd_backlog > 16) { + int tickssofar = jiffies - lp->last_cmd; + if (tickssofar < HZ/4) + return; + + printk(KERN_WARNING "%s: command unit timed out, status resetting.\n", dev->name); + i596_reset(dev, lp, ioaddr); + } +} + +static int i596_open(struct net_device *dev) +{ + int i; + + i = request_irq(dev->irq, i596_interrupt, IRQF_SHARED, dev->name, dev); + if (i) { + printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq); + return i; + } + + if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE) + printk(KERN_ERR "%s: only able to allocate %d receive buffers\n", dev->name, i); + + if (i < 4) { + free_irq(dev->irq, dev); + return -EAGAIN; + } + netif_start_queue(dev); + init_i596(dev); + return 0; /* Always succeed */ +} + +static netdev_tx_t i596_start_xmit (struct sk_buff *skb, struct net_device *dev) { + struct tx_cmd *tx_cmd; + short length; + + length = skb->len; + + if (length < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + length = ETH_ZLEN; + } + + tx_cmd = kmalloc((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC); + if (tx_cmd == NULL) { + printk(KERN_WARNING "%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name); + dev->stats.tx_dropped++; + dev_kfree_skb (skb); + } else { + struct i596_tbd *tx_cmd_tbd; + tx_cmd_tbd = (struct i596_tbd *) (tx_cmd + 1); + tx_cmd->pa_tbd = va_to_pa (tx_cmd_tbd); + tx_cmd_tbd->pa_next = I596_NULL; + + tx_cmd->cmd.command = (CMD_FLEX | CmdTx); + + tx_cmd->pad = 0; + tx_cmd->size = 0; + tx_cmd_tbd->pad = 0; + tx_cmd_tbd->size = (EOF | length); + + tx_cmd_tbd->pa_data = va_to_pa (skb->data); + tx_cmd_tbd->skb = skb; + + if (i596_debug & LOG_SRCDST) + print_eth (skb->data); + + i596_add_cmd (dev, (struct i596_cmd *) tx_cmd); + + dev->stats.tx_packets++; + } + + return NETDEV_TX_OK; +} + +static void +i596_tx_timeout (struct net_device *dev) { + struct i596_private *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + /* Transmitter timeout, serious problems. */ + printk(KERN_WARNING "%s: transmit timed out, status resetting.\n", dev->name); + dev->stats.tx_errors++; + + /* Try to restart the adaptor */ + if (lp->last_restart == dev->stats.tx_packets) { + printk ("Resetting board.\n"); + + /* Shutdown and restart */ + i596_reset (dev, lp, ioaddr); + } else { + /* Issue a channel attention signal */ + printk ("Kicking board.\n"); + lp->scb.command = (CUC_START | RX_START); + CA(); + lp->last_restart = dev->stats.tx_packets; + } + netif_wake_queue(dev); +} + +static void print_eth(char *add) +{ + int i; + + printk ("Dest "); + for (i = 0; i < 6; i++) + printk(" %2.2X", (unsigned char) add[i]); + printk ("\n"); + + printk ("Source"); + for (i = 0; i < 6; i++) + printk(" %2.2X", (unsigned char) add[i+6]); + printk ("\n"); + + printk ("type %2.2X%2.2X\n", + (unsigned char) add[12], (unsigned char) add[13]); +} + +static const struct net_device_ops i596_netdev_ops = { + .ndo_open = i596_open, + .ndo_stop = i596_close, + .ndo_start_xmit = i596_start_xmit, + .ndo_set_multicast_list = set_multicast_list, + .ndo_tx_timeout = i596_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __init lp486e_probe(struct net_device *dev) { + struct i596_private *lp; + unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 }; + unsigned char *bios; + int i, j; + int ret = -ENOMEM; + static int probed; + + if (probed) + return -ENODEV; + probed++; + + if (!request_region(IOADDR, LP486E_TOTAL_SIZE, DRV_NAME)) { + printk(KERN_ERR "lp486e: IO address 0x%x in use\n", IOADDR); + return -EBUSY; + } + + lp = netdev_priv(dev); + spin_lock_init(&lp->cmd_lock); + + /* + * Do we really have this thing? + */ + if (i596_scp_setup(dev)) { + ret = -ENODEV; + goto err_out_kfree; + } + + dev->base_addr = IOADDR; + dev->irq = IRQ; + + + /* + * How do we find the ethernet address? I don't know. + * One possibility is to look at the EISA configuration area + * [0xe8000-0xe9fff]. This contains the ethernet address + * but not at a fixed address - things depend on setup options. + * + * If we find no address, or the wrong address, use + * ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6 + * with the value found in the BIOS setup. + */ + bios = bus_to_virt(0xe8000); + for (j = 0; j < 0x2000; j++) { + if (bios[j] == 0 && bios[j+1] == 0xaa && bios[j+2] == 0) { + printk("%s: maybe address at BIOS 0x%x:", + dev->name, 0xe8000+j); + for (i = 0; i < 6; i++) { + eth_addr[i] = bios[i+j]; + printk(" %2.2X", eth_addr[i]); + } + printk("\n"); + } + } + + printk("%s: lp486e 82596 at %#3lx, IRQ %d,", + dev->name, dev->base_addr, dev->irq); + for (i = 0; i < 6; i++) + printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]); + printk("\n"); + + /* The LP486E-specific entries in the device structure. */ + dev->netdev_ops = &i596_netdev_ops; + dev->watchdog_timeo = 5*HZ; + +#if 0 + /* selftest reports 0x320925ae - don't know what that means */ + i596_port_do(dev, PORT_SELFTEST, "selftest"); + i596_port_do(dev, PORT_DUMP, "dump"); +#endif + return 0; + +err_out_kfree: + release_region(IOADDR, LP486E_TOTAL_SIZE); + return ret; +} + +static inline void +i596_handle_CU_completion(struct net_device *dev, + struct i596_private *lp, + unsigned short status, + unsigned short *ack_cmdp) { + struct i596_cmd *cmd; + int frames_out = 0; + int commands_done = 0; + int cmd_val; + unsigned long flags; + + spin_lock_irqsave(&lp->cmd_lock, flags); + cmd = lp->cmd_head; + + while (lp->cmd_head && (lp->cmd_head->status & CMD_STAT_C)) { + cmd = lp->cmd_head; + + lp->cmd_head = pa_to_va(lp->cmd_head->pa_next); + lp->cmd_backlog--; + + commands_done++; + cmd_val = cmd->command & 0x7; +#if 0 + printk("finished CU %s command (%d)\n", + CUcmdnames[cmd_val], cmd_val); +#endif + switch (cmd_val) { + case CmdTx: + { + struct tx_cmd *tx_cmd; + struct i596_tbd *tx_cmd_tbd; + + tx_cmd = (struct tx_cmd *) cmd; + tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd); + + frames_out++; + if (cmd->status & CMD_STAT_OK) { + if (i596_debug) + print_eth(pa_to_va(tx_cmd_tbd->pa_data)); + } else { + dev->stats.tx_errors++; + if (i596_debug) + printk("transmission failure:%04x\n", + cmd->status); + if (cmd->status & 0x0020) + dev->stats.collisions++; + if (!(cmd->status & 0x0040)) + dev->stats.tx_heartbeat_errors++; + if (cmd->status & 0x0400) + dev->stats.tx_carrier_errors++; + if (cmd->status & 0x0800) + dev->stats.collisions++; + if (cmd->status & 0x1000) + dev->stats.tx_aborted_errors++; + } + dev_kfree_skb_irq(tx_cmd_tbd->skb); + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)tx_cmd); + netif_wake_queue(dev); + break; + } + + case CmdMulticastList: + cmd->pa_next = I596_NULL; + kfree((unsigned char *)cmd); + break; + + case CmdTDR: + { + unsigned long status = *((unsigned long *) (cmd + 1)); + if (status & 0x8000) { + if (i596_debug) + printk("%s: link ok.\n", dev->name); + } else { + if (status & 0x4000) + printk("%s: Transceiver problem.\n", + dev->name); + if (status & 0x2000) + printk("%s: Termination problem.\n", + dev->name); + if (status & 0x1000) + printk("%s: Short circuit.\n", + dev->name); + printk("%s: Time %ld.\n", + dev->name, status & 0x07ff); + } + } + default: + cmd->pa_next = I596_NULL; + lp->last_cmd = jiffies; + + } + barrier(); + } + + cmd = lp->cmd_head; + while (cmd && (cmd != lp->cmd_tail)) { + cmd->command &= 0x1fff; + cmd = pa_to_va(cmd->pa_next); + barrier(); + } + + if (lp->cmd_head) + *ack_cmdp |= CUC_START; + lp->scb.pa_cmd = va_to_pa(lp->cmd_head); + spin_unlock_irqrestore(&lp->cmd_lock, flags); +} + +static irqreturn_t +i596_interrupt(int irq, void *dev_instance) +{ + struct net_device *dev = dev_instance; + struct i596_private *lp = netdev_priv(dev); + unsigned short status, ack_cmd = 0; + int frames_in = 0; + + /* + * The 82596 examines the command, performs the required action, + * and then clears the SCB command word. + */ + if (lp->scb.command && i596_timeout(dev, "interrupt", 40)) + ; + + /* + * The status word indicates the status of the 82596. + * It is modified only by the 82596. + * + * [So, we must not clear it. I find often status 0xffff, + * which is not one of the values allowed by the docs.] + */ + status = lp->scb.status; +#if 0 + if (i596_debug) { + printk("%s: i596 interrupt, ", dev->name); + i596_out_status(status); + } +#endif + /* Impossible, but it happens - perhaps when we get + a receive interrupt but scb.pa_rfd is I596_NULL. */ + if (status == 0xffff) { + printk("%s: i596_interrupt: got status 0xffff\n", dev->name); + goto out; + } + + ack_cmd = (status & STAT_ACK); + + if (status & (STAT_CX | STAT_CNA)) + i596_handle_CU_completion(dev, lp, status, &ack_cmd); + + if (status & (STAT_FR | STAT_RNR)) { + /* Restart the receive unit when it got inactive somehow */ + if ((status & STAT_RNR) && netif_running(dev)) + ack_cmd |= RX_START; + + if (status & STAT_FR) { + frames_in = i596_rx(dev); + if (!frames_in) + printk("receive frame reported, but no frames\n"); + } + } + + /* acknowledge the interrupt */ + /* + if ((lp->scb.pa_cmd != I596_NULL) && netif_running(dev)) + ack_cmd |= CUC_START; + */ + + if (lp->scb.command && i596_timeout(dev, "i596 interrupt", 100)) + ; + + lp->scb.command = ack_cmd; + + CLEAR_INT(); + CA(); + + out: + return IRQ_HANDLED; +} + +static int i596_close(struct net_device *dev) { + struct i596_private *lp = netdev_priv(dev); + + netif_stop_queue(dev); + + if (i596_debug) + printk("%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, lp->scb.status); + + lp->scb.command = (CUC_ABORT | RX_ABORT); + CA(); + + i596_cleanup_cmd(dev); + + if (lp->scb.command && i596_timeout(dev, "i596_close", 200)) + ; + + free_irq(dev->irq, dev); + remove_rx_bufs(dev); + + return 0; +} + +/* +* Set or clear the multicast filter for this adaptor. +*/ + +static void set_multicast_list(struct net_device *dev) { + struct i596_private *lp = netdev_priv(dev); + struct i596_cmd *cmd; + + if (i596_debug > 1) + printk ("%s: set multicast list %d\n", + dev->name, netdev_mc_count(dev)); + + if (!netdev_mc_empty(dev)) { + struct netdev_hw_addr *ha; + char *cp; + cmd = kmalloc(sizeof(struct i596_cmd) + 2 + + netdev_mc_count(dev) * 6, GFP_ATOMIC); + if (cmd == NULL) { + printk (KERN_ERR "%s: set_multicast Memory squeeze.\n", dev->name); + return; + } + cmd->command = CmdMulticastList; + *((unsigned short *) (cmd + 1)) = netdev_mc_count(dev) * 6; + cp = ((char *)(cmd + 1))+2; + netdev_for_each_mc_addr(ha, dev) { + memcpy(cp, ha->addr, 6); + cp += 6; + } + if (i596_debug & LOG_SRCDST) + print_eth (((char *)(cmd + 1)) + 2); + i596_add_cmd(dev, cmd); + } else { + if (lp->set_conf.pa_next != I596_NULL) { + return; + } + if (netdev_mc_empty(dev) && + !(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + lp->i596_config[8] &= ~0x01; + } else { + lp->i596_config[8] |= 0x01; + } + + i596_add_cmd(dev, (struct i596_cmd *) &lp->set_conf); + } +} + +MODULE_AUTHOR("Ard van Breemen <ard@cstmel.nl.eu.org>"); +MODULE_DESCRIPTION("Intel Panther onboard i82596 driver"); +MODULE_LICENSE("GPL"); + +static struct net_device *dev_lp486e; +static int full_duplex; +static int options; +static int io = IOADDR; +static int irq = IRQ; + +module_param(debug, int, 0); +//module_param(max_interrupt_work, int, 0); +//module_param(reverse_probe, int, 0); +//module_param(rx_copybreak, int, 0); +module_param(options, int, 0); +module_param(full_duplex, int, 0); + +static int __init lp486e_init_module(void) { + int err; + struct net_device *dev = alloc_etherdev(sizeof(struct i596_private)); + if (!dev) + return -ENOMEM; + + dev->irq = irq; + dev->base_addr = io; + err = lp486e_probe(dev); + if (err) { + free_netdev(dev); + return err; + } + err = register_netdev(dev); + if (err) { + release_region(dev->base_addr, LP486E_TOTAL_SIZE); + free_netdev(dev); + return err; + } + dev_lp486e = dev; + full_duplex = 0; + options = 0; + return 0; +} + +static void __exit lp486e_cleanup_module(void) { + unregister_netdev(dev_lp486e); + release_region(dev_lp486e->base_addr, LP486E_TOTAL_SIZE); + free_netdev(dev_lp486e); +} + +module_init(lp486e_init_module); +module_exit(lp486e_cleanup_module); diff --git a/drivers/net/ethernet/i825xx/ni52.c b/drivers/net/ethernet/i825xx/ni52.c new file mode 100644 index 000000000000..d973fc6c6b88 --- /dev/null +++ b/drivers/net/ethernet/i825xx/ni52.c @@ -0,0 +1,1346 @@ +/* + * net-3-driver for the NI5210 card (i82586 Ethernet chip) + * + * This is an extension to the Linux operating system, and is covered by the + * same GNU General Public License that covers that work. + * + * Alphacode 0.82 (96/09/29) for Linux 2.0.0 (or later) + * Copyrights (c) 1994,1995,1996 by M.Hipp (hippm@informatik.uni-tuebingen.de) + * [feel free to mail ....] + * + * when using as module: (no autoprobing!) + * run with e.g: + * insmod ni52.o io=0x360 irq=9 memstart=0xd0000 memend=0xd4000 + * + * CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!. + * + * If you find a bug, please report me: + * The kernel panic output and any kmsg from the ni52 driver + * the ni5210-driver-version and the linux-kernel version + * how many shared memory (memsize) on the netcard, + * bootprom: yes/no, base_addr, mem_start + * maybe the ni5210-card revision and the i82586 version + * + * autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340 + * mem_start: 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000, + * 0xd8000,0xcc000,0xce000,0xda000,0xdc000 + * + * sources: + * skeleton.c from Donald Becker + * + * I have also done a look in the following sources: (mail me if you need them) + * crynwr-packet-driver by Russ Nelson + * Garret A. Wollman's (fourth) i82586-driver for BSD + * (before getting an i82596 (yes 596 not 586) manual, the existing drivers + * helped me a lot to understand this tricky chip.) + * + * Known Problems: + * The internal sysbus seems to be slow. So we often lose packets because of + * overruns while receiving from a fast remote host. + * This can slow down TCP connections. Maybe the newer ni5210 cards are + * better. My experience is, that if a machine sends with more than about + * 500-600K/s the fifo/sysbus overflows. + * + * IMPORTANT NOTE: + * On fast networks, it's a (very) good idea to have 16K shared memory. With + * 8K, we can store only 4 receive frames, so it can (easily) happen that a + * remote machine 'overruns' our system. + * + * Known i82586/card problems (I'm sure, there are many more!): + * Running the NOP-mode, the i82586 sometimes seems to forget to report + * every xmit-interrupt until we restart the CU. + * Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit + * in the RBD-Struct which indicates an end of the RBD queue. + * Instead, the RU fetches another (randomly selected and + * usually used) RBD and begins to fill it. (Maybe, this happens only if + * the last buffer from the previous RFD fits exact into the queue and + * the next RFD can't fetch an initial RBD. Anyone knows more? ) + * + * results from ftp performance tests with Linux 1.2.5 + * send and receive about 350-400 KByte/s (peak up to 460 kbytes/s) + * sending in NOP-mode: peak performance up to 530K/s (but better don't + * run this mode) + */ + +/* + * 29.Sept.96: virt_to_bus changes for new memory scheme + * 19.Feb.96: more Mcast changes, module support (MH) + * + * 18.Nov.95: Mcast changes (AC). + * + * 23.April.95: fixed(?) receiving problems by configuring a RFD more + * than the number of RBD's. Can maybe cause other problems. + * 18.April.95: Added MODULE support (MH) + * 17.April.95: MC related changes in init586() and set_multicast_list(). + * removed use of 'jiffies' in init586() (MH) + * + * 19.Sep.94: Added Multicast support (not tested yet) (MH) + * + * 18.Sep.94: Workaround for 'EL-Bug'. Removed flexible RBD-handling. + * Now, every RFD has exact one RBD. (MH) + * + * 14.Sep.94: added promiscuous mode, a few cleanups (MH) + * + * 19.Aug.94: changed request_irq() parameter (MH) + * + * 20.July.94: removed cleanup bugs, removed a 16K-mem-probe-bug (MH) + * + * 19.July.94: lotsa cleanups .. (MH) + * + * 17.July.94: some patches ... verified to run with 1.1.29 (MH) + * + * 4.July.94: patches for Linux 1.1.24 (MH) + * + * 26.March.94: patches for Linux 1.0 and iomem-auto-probe (MH) + * + * 30.Sep.93: Added nop-chain .. driver now runs with only one Xmit-Buff, + * too (MH) + * + * < 30.Sep.93: first versions + */ + +static int debuglevel; /* debug-printk 0: off 1: a few 2: more */ +static int automatic_resume; /* experimental .. better should be zero */ +static int rfdadd; /* rfdadd=1 may be better for 8K MEM cards */ +static int fifo = 0x8; /* don't change */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <asm/io.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "ni52.h" + +#define DRV_NAME "ni52" + +#define DEBUG /* debug on */ +#define SYSBUSVAL 1 /* 8 Bit */ + +#define ni_attn586() { outb(0, dev->base_addr + NI52_ATTENTION); } +#define ni_reset586() { outb(0, dev->base_addr + NI52_RESET); } +#define ni_disint() { outb(0, dev->base_addr + NI52_INTDIS); } +#define ni_enaint() { outb(0, dev->base_addr + NI52_INTENA); } + +#define make32(ptr16) ((void __iomem *)(p->memtop + (short) (ptr16))) +#define make24(ptr32) ((char __iomem *)(ptr32)) - p->base +#define make16(ptr32) ((unsigned short) ((char __iomem *)(ptr32)\ + - p->memtop)) + +/******************* how to calculate the buffers ***************************** + + * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works + * --------------- in a different (more stable?) mode. Only in this mode it's + * possible to configure the driver with 'NO_NOPCOMMANDS' + +sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8; +sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT +sizeof(rfd) = 24; sizeof(rbd) = 12; +sizeof(tbd) = 8; sizeof(transmit_cmd) = 16; +sizeof(nop_cmd) = 8; + + * if you don't know the driver, better do not change these values: */ + +#define RECV_BUFF_SIZE 1524 /* slightly oversized */ +#define XMIT_BUFF_SIZE 1524 /* slightly oversized */ +#define NUM_XMIT_BUFFS 1 /* config for both, 8K and 16K shmem */ +#define NUM_RECV_BUFFS_8 4 /* config for 8K shared mem */ +#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */ +#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */ + +/**************************************************************************/ + + +#define NI52_TOTAL_SIZE 16 +#define NI52_ADDR0 0x02 +#define NI52_ADDR1 0x07 +#define NI52_ADDR2 0x01 + +static int ni52_probe1(struct net_device *dev, int ioaddr); +static irqreturn_t ni52_interrupt(int irq, void *dev_id); +static int ni52_open(struct net_device *dev); +static int ni52_close(struct net_device *dev); +static netdev_tx_t ni52_send_packet(struct sk_buff *, struct net_device *); +static struct net_device_stats *ni52_get_stats(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void ni52_timeout(struct net_device *dev); + +/* helper-functions */ +static int init586(struct net_device *dev); +static int check586(struct net_device *dev, unsigned size); +static void alloc586(struct net_device *dev); +static void startrecv586(struct net_device *dev); +static void __iomem *alloc_rfa(struct net_device *dev, void __iomem *ptr); +static void ni52_rcv_int(struct net_device *dev); +static void ni52_xmt_int(struct net_device *dev); +static void ni52_rnr_int(struct net_device *dev); + +struct priv { + char __iomem *base; + char __iomem *mapped; + char __iomem *memtop; + spinlock_t spinlock; + int reset; + struct rfd_struct __iomem *rfd_last, *rfd_top, *rfd_first; + struct scp_struct __iomem *scp; + struct iscp_struct __iomem *iscp; + struct scb_struct __iomem *scb; + struct tbd_struct __iomem *xmit_buffs[NUM_XMIT_BUFFS]; +#if (NUM_XMIT_BUFFS == 1) + struct transmit_cmd_struct __iomem *xmit_cmds[2]; + struct nop_cmd_struct __iomem *nop_cmds[2]; +#else + struct transmit_cmd_struct __iomem *xmit_cmds[NUM_XMIT_BUFFS]; + struct nop_cmd_struct __iomem *nop_cmds[NUM_XMIT_BUFFS]; +#endif + int nop_point, num_recv_buffs; + char __iomem *xmit_cbuffs[NUM_XMIT_BUFFS]; + int xmit_count, xmit_last; +}; + +/* wait for command with timeout: */ +static void wait_for_scb_cmd(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + int i; + for (i = 0; i < 16384; i++) { + if (readb(&p->scb->cmd_cuc) == 0) + break; + udelay(4); + if (i == 16383) { + printk(KERN_ERR "%s: scb_cmd timed out: %04x,%04x .. disabling i82586!!\n", + dev->name, readb(&p->scb->cmd_cuc), readb(&p->scb->cus)); + if (!p->reset) { + p->reset = 1; + ni_reset586(); + } + } + } +} + +static void wait_for_scb_cmd_ruc(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + int i; + for (i = 0; i < 16384; i++) { + if (readb(&p->scb->cmd_ruc) == 0) + break; + udelay(4); + if (i == 16383) { + printk(KERN_ERR "%s: scb_cmd (ruc) timed out: %04x,%04x .. disabling i82586!!\n", + dev->name, readb(&p->scb->cmd_ruc), + readb(&p->scb->rus)); + if (!p->reset) { + p->reset = 1; + ni_reset586(); + } + } + } +} + +static void wait_for_stat_compl(void __iomem *p) +{ + struct nop_cmd_struct __iomem *addr = p; + int i; + for (i = 0; i < 32767; i++) { + if (readw(&((addr)->cmd_status)) & STAT_COMPL) + break; + udelay(32); + } +} + +/********************************************** + * close device + */ +static int ni52_close(struct net_device *dev) +{ + free_irq(dev->irq, dev); + ni_reset586(); /* the hard way to stop the receiver */ + netif_stop_queue(dev); + return 0; +} + +/********************************************** + * open device + */ +static int ni52_open(struct net_device *dev) +{ + int ret; + + ni_disint(); + alloc586(dev); + init586(dev); + startrecv586(dev); + ni_enaint(); + + ret = request_irq(dev->irq, ni52_interrupt, 0, dev->name, dev); + if (ret) { + ni_reset586(); + return ret; + } + netif_start_queue(dev); + return 0; /* most done by init */ +} + +static int check_iscp(struct net_device *dev, void __iomem *addr) +{ + struct iscp_struct __iomem *iscp = addr; + struct priv *p = netdev_priv(dev); + memset_io(iscp, 0, sizeof(struct iscp_struct)); + + writel(make24(iscp), &p->scp->iscp); + writeb(1, &iscp->busy); + + ni_reset586(); + ni_attn586(); + mdelay(32); /* wait a while... */ + /* i82586 clears 'busy' after successful init */ + if (readb(&iscp->busy)) + return 0; + return 1; +} + +/********************************************** + * Check to see if there's an 82586 out there. + */ +static int check586(struct net_device *dev, unsigned size) +{ + struct priv *p = netdev_priv(dev); + int i; + + p->mapped = ioremap(dev->mem_start, size); + if (!p->mapped) + return 0; + + p->base = p->mapped + size - 0x01000000; + p->memtop = p->mapped + size; + p->scp = (struct scp_struct __iomem *)(p->base + SCP_DEFAULT_ADDRESS); + p->scb = (struct scb_struct __iomem *) p->mapped; + p->iscp = (struct iscp_struct __iomem *)p->scp - 1; + memset_io(p->scp, 0, sizeof(struct scp_struct)); + for (i = 0; i < sizeof(struct scp_struct); i++) + /* memory was writeable? */ + if (readb((char __iomem *)p->scp + i)) + goto Enodev; + writeb(SYSBUSVAL, &p->scp->sysbus); /* 1 = 8Bit-Bus, 0 = 16 Bit */ + if (readb(&p->scp->sysbus) != SYSBUSVAL) + goto Enodev; + + if (!check_iscp(dev, p->mapped)) + goto Enodev; + if (!check_iscp(dev, p->iscp)) + goto Enodev; + return 1; +Enodev: + iounmap(p->mapped); + return 0; +} + +/****************************************************************** + * set iscp at the right place, called by ni52_probe1 and open586. + */ +static void alloc586(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + ni_reset586(); + mdelay(32); + + memset_io(p->iscp, 0, sizeof(struct iscp_struct)); + memset_io(p->scp , 0, sizeof(struct scp_struct)); + + writel(make24(p->iscp), &p->scp->iscp); + writeb(SYSBUSVAL, &p->scp->sysbus); + writew(make16(p->scb), &p->iscp->scb_offset); + + writeb(1, &p->iscp->busy); + ni_reset586(); + ni_attn586(); + + mdelay(32); + + if (readb(&p->iscp->busy)) + printk(KERN_ERR "%s: Init-Problems (alloc).\n", dev->name); + + p->reset = 0; + + memset_io(p->scb, 0, sizeof(struct scb_struct)); +} + +/* set: io,irq,memstart,memend or set it when calling insmod */ +static int irq = 9; +static int io = 0x300; +static long memstart; /* e.g 0xd0000 */ +static long memend; /* e.g 0xd4000 */ + +/********************************************** + * probe the ni5210-card + */ +struct net_device * __init ni52_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct priv)); + static const int ports[] = {0x300, 0x280, 0x360, 0x320, 0x340, 0}; + const int *port; + struct priv *p; + int err = 0; + + if (!dev) + return ERR_PTR(-ENOMEM); + + p = netdev_priv(dev); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + io = dev->base_addr; + irq = dev->irq; + memstart = dev->mem_start; + memend = dev->mem_end; + } + + if (io > 0x1ff) { /* Check a single specified location. */ + err = ni52_probe1(dev, io); + } else if (io > 0) { /* Don't probe at all. */ + err = -ENXIO; + } else { + for (port = ports; *port && ni52_probe1(dev, *port) ; port++) + ; + if (*port) + goto got_it; +#ifdef FULL_IO_PROBE + for (io = 0x200; io < 0x400 && ni52_probe1(dev, io); io += 8) + ; + if (io < 0x400) + goto got_it; +#endif + err = -ENODEV; + } + if (err) + goto out; +got_it: + err = register_netdev(dev); + if (err) + goto out1; + return dev; +out1: + iounmap(p->mapped); + release_region(dev->base_addr, NI52_TOTAL_SIZE); +out: + free_netdev(dev); + return ERR_PTR(err); +} + +static const struct net_device_ops ni52_netdev_ops = { + .ndo_open = ni52_open, + .ndo_stop = ni52_close, + .ndo_get_stats = ni52_get_stats, + .ndo_tx_timeout = ni52_timeout, + .ndo_start_xmit = ni52_send_packet, + .ndo_set_multicast_list = set_multicast_list, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __init ni52_probe1(struct net_device *dev, int ioaddr) +{ + int i, size, retval; + struct priv *priv = netdev_priv(dev); + + dev->base_addr = ioaddr; + dev->irq = irq; + dev->mem_start = memstart; + dev->mem_end = memend; + + spin_lock_init(&priv->spinlock); + + if (!request_region(ioaddr, NI52_TOTAL_SIZE, DRV_NAME)) + return -EBUSY; + + if (!(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) || + !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2)) { + retval = -ENODEV; + goto out; + } + + for (i = 0; i < ETH_ALEN; i++) + dev->dev_addr[i] = inb(dev->base_addr+i); + + if (dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1 || + dev->dev_addr[2] != NI52_ADDR2) { + retval = -ENODEV; + goto out; + } + + printk(KERN_INFO "%s: NI5210 found at %#3lx, ", + dev->name, dev->base_addr); + + /* + * check (or search) IO-Memory, 8K and 16K + */ +#ifdef MODULE + size = dev->mem_end - dev->mem_start; + if (size != 0x2000 && size != 0x4000) { + printk("\n"); + printk(KERN_ERR "%s: Invalid memory size %d. Allowed is 0x2000 or 0x4000 bytes.\n", dev->name, size); + retval = -ENODEV; + goto out; + } + if (!check586(dev, size)) { + printk(KERN_ERR "?memcheck, Can't find memory at 0x%lx with size %d!\n", dev->mem_start, size); + retval = -ENODEV; + goto out; + } +#else + if (dev->mem_start != 0) { + /* no auto-mem-probe */ + size = 0x4000; /* check for 16K mem */ + if (!check586(dev, size)) { + size = 0x2000; /* check for 8K mem */ + if (!check586(dev, size)) { + printk(KERN_ERR "?memprobe, Can't find memory at 0x%lx!\n", dev->mem_start); + retval = -ENODEV; + goto out; + } + } + } else { + static const unsigned long memaddrs[] = { + 0xc8000, 0xca000, 0xcc000, 0xce000, 0xd0000, 0xd2000, + 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0 + }; + for (i = 0;; i++) { + if (!memaddrs[i]) { + printk(KERN_ERR "?memprobe, Can't find io-memory!\n"); + retval = -ENODEV; + goto out; + } + dev->mem_start = memaddrs[i]; + size = 0x2000; /* check for 8K mem */ + if (check586(dev, size)) + /* 8K-check */ + break; + size = 0x4000; /* check for 16K mem */ + if (check586(dev, size)) + /* 16K-check */ + break; + } + } + /* set mem_end showed by 'ifconfig' */ + dev->mem_end = dev->mem_start + size; +#endif + + alloc586(dev); + + /* set number of receive-buffs according to memsize */ + if (size == 0x2000) + priv->num_recv_buffs = NUM_RECV_BUFFS_8; + else + priv->num_recv_buffs = NUM_RECV_BUFFS_16; + + printk(KERN_DEBUG "Memaddr: 0x%lx, Memsize: %d, ", + dev->mem_start, size); + + if (dev->irq < 2) { + unsigned long irq_mask; + + irq_mask = probe_irq_on(); + ni_reset586(); + ni_attn586(); + + mdelay(20); + dev->irq = probe_irq_off(irq_mask); + if (!dev->irq) { + printk("?autoirq, Failed to detect IRQ line!\n"); + retval = -EAGAIN; + iounmap(priv->mapped); + goto out; + } + printk("IRQ %d (autodetected).\n", dev->irq); + } else { + if (dev->irq == 2) + dev->irq = 9; + printk("IRQ %d (assigned and not checked!).\n", dev->irq); + } + + dev->netdev_ops = &ni52_netdev_ops; + dev->watchdog_timeo = HZ/20; + + return 0; +out: + release_region(ioaddr, NI52_TOTAL_SIZE); + return retval; +} + +/********************************************** + * init the chip (ni52-interrupt should be disabled?!) + * needs a correct 'allocated' memory + */ + +static int init586(struct net_device *dev) +{ + void __iomem *ptr; + int i, result = 0; + struct priv *p = netdev_priv(dev); + struct configure_cmd_struct __iomem *cfg_cmd; + struct iasetup_cmd_struct __iomem *ias_cmd; + struct tdr_cmd_struct __iomem *tdr_cmd; + struct mcsetup_cmd_struct __iomem *mc_cmd; + struct netdev_hw_addr *ha; + int num_addrs = netdev_mc_count(dev); + + ptr = p->scb + 1; + + cfg_cmd = ptr; /* configure-command */ + writew(0, &cfg_cmd->cmd_status); + writew(CMD_CONFIGURE | CMD_LAST, &cfg_cmd->cmd_cmd); + writew(0xFFFF, &cfg_cmd->cmd_link); + + /* number of cfg bytes */ + writeb(0x0a, &cfg_cmd->byte_cnt); + /* fifo-limit (8=tx:32/rx:64) */ + writeb(fifo, &cfg_cmd->fifo); + /* hold or discard bad recv frames (bit 7) */ + writeb(0x40, &cfg_cmd->sav_bf); + /* addr_len |!src_insert |pre-len |loopback */ + writeb(0x2e, &cfg_cmd->adr_len); + writeb(0x00, &cfg_cmd->priority); + writeb(0x60, &cfg_cmd->ifs); + writeb(0x00, &cfg_cmd->time_low); + writeb(0xf2, &cfg_cmd->time_high); + writeb(0x00, &cfg_cmd->promisc); + if (dev->flags & IFF_ALLMULTI) { + int len = ((char __iomem *)p->iscp - (char __iomem *)ptr - 8) / 6; + if (num_addrs > len) { + printk(KERN_ERR "%s: switching to promisc. mode\n", + dev->name); + writeb(0x01, &cfg_cmd->promisc); + } + } + if (dev->flags & IFF_PROMISC) + writeb(0x01, &cfg_cmd->promisc); + writeb(0x00, &cfg_cmd->carr_coll); + writew(make16(cfg_cmd), &p->scb->cbl_offset); + writeb(0, &p->scb->cmd_ruc); + + writeb(CUC_START, &p->scb->cmd_cuc); /* cmd.-unit start */ + ni_attn586(); + + wait_for_stat_compl(cfg_cmd); + + if ((readw(&cfg_cmd->cmd_status) & (STAT_OK|STAT_COMPL)) != + (STAT_COMPL|STAT_OK)) { + printk(KERN_ERR "%s: configure command failed: %x\n", + dev->name, readw(&cfg_cmd->cmd_status)); + return 1; + } + + /* + * individual address setup + */ + + ias_cmd = ptr; + + writew(0, &ias_cmd->cmd_status); + writew(CMD_IASETUP | CMD_LAST, &ias_cmd->cmd_cmd); + writew(0xffff, &ias_cmd->cmd_link); + + memcpy_toio(&ias_cmd->iaddr, (char *)dev->dev_addr, ETH_ALEN); + + writew(make16(ias_cmd), &p->scb->cbl_offset); + + writeb(CUC_START, &p->scb->cmd_cuc); /* cmd.-unit start */ + ni_attn586(); + + wait_for_stat_compl(ias_cmd); + + if ((readw(&ias_cmd->cmd_status) & (STAT_OK|STAT_COMPL)) != + (STAT_OK|STAT_COMPL)) { + printk(KERN_ERR "%s (ni52): individual address setup command failed: %04x\n", dev->name, readw(&ias_cmd->cmd_status)); + return 1; + } + + /* + * TDR, wire check .. e.g. no resistor e.t.c + */ + + tdr_cmd = ptr; + + writew(0, &tdr_cmd->cmd_status); + writew(CMD_TDR | CMD_LAST, &tdr_cmd->cmd_cmd); + writew(0xffff, &tdr_cmd->cmd_link); + writew(0, &tdr_cmd->status); + + writew(make16(tdr_cmd), &p->scb->cbl_offset); + writeb(CUC_START, &p->scb->cmd_cuc); /* cmd.-unit start */ + ni_attn586(); + + wait_for_stat_compl(tdr_cmd); + + if (!(readw(&tdr_cmd->cmd_status) & STAT_COMPL)) + printk(KERN_ERR "%s: Problems while running the TDR.\n", + dev->name); + else { + udelay(16); + result = readw(&tdr_cmd->status); + writeb(readb(&p->scb->cus) & STAT_MASK, &p->scb->cmd_cuc); + ni_attn586(); /* ack the interrupts */ + + if (result & TDR_LNK_OK) + ; + else if (result & TDR_XCVR_PRB) + printk(KERN_ERR "%s: TDR: Transceiver problem. Check the cable(s)!\n", + dev->name); + else if (result & TDR_ET_OPN) + printk(KERN_ERR "%s: TDR: No correct termination %d clocks away.\n", + dev->name, result & TDR_TIMEMASK); + else if (result & TDR_ET_SRT) { + /* time == 0 -> strange :-) */ + if (result & TDR_TIMEMASK) + printk(KERN_ERR "%s: TDR: Detected a short circuit %d clocks away.\n", + dev->name, result & TDR_TIMEMASK); + } else + printk(KERN_ERR "%s: TDR: Unknown status %04x\n", + dev->name, result); + } + + /* + * Multicast setup + */ + if (num_addrs && !(dev->flags & IFF_PROMISC)) { + mc_cmd = ptr; + writew(0, &mc_cmd->cmd_status); + writew(CMD_MCSETUP | CMD_LAST, &mc_cmd->cmd_cmd); + writew(0xffff, &mc_cmd->cmd_link); + writew(num_addrs * 6, &mc_cmd->mc_cnt); + + i = 0; + netdev_for_each_mc_addr(ha, dev) + memcpy_toio(mc_cmd->mc_list[i++], ha->addr, 6); + + writew(make16(mc_cmd), &p->scb->cbl_offset); + writeb(CUC_START, &p->scb->cmd_cuc); + ni_attn586(); + + wait_for_stat_compl(mc_cmd); + + if ((readw(&mc_cmd->cmd_status) & (STAT_COMPL|STAT_OK)) + != (STAT_COMPL|STAT_OK)) + printk(KERN_ERR "%s: Can't apply multicast-address-list.\n", dev->name); + } + + /* + * alloc nop/xmit-cmds + */ +#if (NUM_XMIT_BUFFS == 1) + for (i = 0; i < 2; i++) { + p->nop_cmds[i] = ptr; + writew(CMD_NOP, &p->nop_cmds[i]->cmd_cmd); + writew(0, &p->nop_cmds[i]->cmd_status); + writew(make16(p->nop_cmds[i]), &p->nop_cmds[i]->cmd_link); + ptr = ptr + sizeof(struct nop_cmd_struct); + } +#else + for (i = 0; i < NUM_XMIT_BUFFS; i++) { + p->nop_cmds[i] = ptr; + writew(CMD_NOP, &p->nop_cmds[i]->cmd_cmd); + writew(0, &p->nop_cmds[i]->cmd_status); + writew(make16(p->nop_cmds[i]), &p->nop_cmds[i]->cmd_link); + ptr = ptr + sizeof(struct nop_cmd_struct); + } +#endif + + ptr = alloc_rfa(dev, ptr); /* init receive-frame-area */ + + /* + * alloc xmit-buffs / init xmit_cmds + */ + for (i = 0; i < NUM_XMIT_BUFFS; i++) { + /* Transmit cmd/buff 0 */ + p->xmit_cmds[i] = ptr; + ptr = ptr + sizeof(struct transmit_cmd_struct); + p->xmit_cbuffs[i] = ptr; /* char-buffs */ + ptr = ptr + XMIT_BUFF_SIZE; + p->xmit_buffs[i] = ptr; /* TBD */ + ptr = ptr + sizeof(struct tbd_struct); + if ((void __iomem *)ptr > (void __iomem *)p->iscp) { + printk(KERN_ERR "%s: not enough shared-mem for your configuration!\n", + dev->name); + return 1; + } + memset_io(p->xmit_cmds[i], 0, + sizeof(struct transmit_cmd_struct)); + memset_io(p->xmit_buffs[i], 0, + sizeof(struct tbd_struct)); + writew(make16(p->nop_cmds[(i+1)%NUM_XMIT_BUFFS]), + &p->xmit_cmds[i]->cmd_link); + writew(STAT_COMPL, &p->xmit_cmds[i]->cmd_status); + writew(CMD_XMIT|CMD_INT, &p->xmit_cmds[i]->cmd_cmd); + writew(make16(p->xmit_buffs[i]), &p->xmit_cmds[i]->tbd_offset); + writew(0xffff, &p->xmit_buffs[i]->next); + writel(make24(p->xmit_cbuffs[i]), &p->xmit_buffs[i]->buffer); + } + + p->xmit_count = 0; + p->xmit_last = 0; +#ifndef NO_NOPCOMMANDS + p->nop_point = 0; +#endif + + /* + * 'start transmitter' + */ +#ifndef NO_NOPCOMMANDS + writew(make16(p->nop_cmds[0]), &p->scb->cbl_offset); + writeb(CUC_START, &p->scb->cmd_cuc); + ni_attn586(); + wait_for_scb_cmd(dev); +#else + writew(make16(p->xmit_cmds[0]), &p->xmit_cmds[0]->cmd_link); + writew(CMD_XMIT | CMD_SUSPEND | CMD_INT, &p->xmit_cmds[0]->cmd_cmd); +#endif + + /* + * ack. interrupts + */ + writeb(readb(&p->scb->cus) & STAT_MASK, &p->scb->cmd_cuc); + ni_attn586(); + udelay(16); + + ni_enaint(); + + return 0; +} + +/****************************************************** + * This is a helper routine for ni52_rnr_int() and init586(). + * It sets up the Receive Frame Area (RFA). + */ + +static void __iomem *alloc_rfa(struct net_device *dev, void __iomem *ptr) +{ + struct rfd_struct __iomem *rfd = ptr; + struct rbd_struct __iomem *rbd; + int i; + struct priv *p = netdev_priv(dev); + + memset_io(rfd, 0, + sizeof(struct rfd_struct) * (p->num_recv_buffs + rfdadd)); + p->rfd_first = rfd; + + for (i = 0; i < (p->num_recv_buffs + rfdadd); i++) { + writew(make16(rfd + (i+1) % (p->num_recv_buffs+rfdadd)), + &rfd[i].next); + writew(0xffff, &rfd[i].rbd_offset); + } + /* RU suspend */ + writeb(RFD_SUSP, &rfd[p->num_recv_buffs-1+rfdadd].last); + + ptr = rfd + (p->num_recv_buffs + rfdadd); + + rbd = ptr; + ptr = rbd + p->num_recv_buffs; + + /* clr descriptors */ + memset_io(rbd, 0, sizeof(struct rbd_struct) * (p->num_recv_buffs)); + + for (i = 0; i < p->num_recv_buffs; i++) { + writew(make16(rbd + (i+1) % p->num_recv_buffs), &rbd[i].next); + writew(RECV_BUFF_SIZE, &rbd[i].size); + writel(make24(ptr), &rbd[i].buffer); + ptr = ptr + RECV_BUFF_SIZE; + } + p->rfd_top = p->rfd_first; + p->rfd_last = p->rfd_first + (p->num_recv_buffs - 1 + rfdadd); + + writew(make16(p->rfd_first), &p->scb->rfa_offset); + writew(make16(rbd), &p->rfd_first->rbd_offset); + + return ptr; +} + + +/************************************************** + * Interrupt Handler ... + */ + +static irqreturn_t ni52_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + unsigned int stat; + int cnt = 0; + struct priv *p; + + p = netdev_priv(dev); + + if (debuglevel > 1) + printk("I"); + + spin_lock(&p->spinlock); + + wait_for_scb_cmd(dev); /* wait for last command */ + + while ((stat = readb(&p->scb->cus) & STAT_MASK)) { + writeb(stat, &p->scb->cmd_cuc); + ni_attn586(); + + if (stat & STAT_FR) /* received a frame */ + ni52_rcv_int(dev); + + if (stat & STAT_RNR) { /* RU went 'not ready' */ + printk("(R)"); + if (readb(&p->scb->rus) & RU_SUSPEND) { + /* special case: RU_SUSPEND */ + wait_for_scb_cmd(dev); + writeb(RUC_RESUME, &p->scb->cmd_ruc); + ni_attn586(); + wait_for_scb_cmd_ruc(dev); + } else { + printk(KERN_ERR "%s: Receiver-Unit went 'NOT READY': %04x/%02x.\n", + dev->name, stat, readb(&p->scb->rus)); + ni52_rnr_int(dev); + } + } + + /* Command with I-bit set complete */ + if (stat & STAT_CX) + ni52_xmt_int(dev); + +#ifndef NO_NOPCOMMANDS + if (stat & STAT_CNA) { /* CU went 'not ready' */ + if (netif_running(dev)) + printk(KERN_ERR "%s: oops! CU has left active state. stat: %04x/%02x.\n", + dev->name, stat, readb(&p->scb->cus)); + } +#endif + + if (debuglevel > 1) + printk("%d", cnt++); + + /* Wait for ack. (ni52_xmt_int can be faster than ack!!) */ + wait_for_scb_cmd(dev); + if (readb(&p->scb->cmd_cuc)) { /* timed out? */ + printk(KERN_ERR "%s: Acknowledge timed out.\n", + dev->name); + ni_disint(); + break; + } + } + spin_unlock(&p->spinlock); + + if (debuglevel > 1) + printk("i"); + return IRQ_HANDLED; +} + +/******************************************************* + * receive-interrupt + */ + +static void ni52_rcv_int(struct net_device *dev) +{ + int status, cnt = 0; + unsigned short totlen; + struct sk_buff *skb; + struct rbd_struct __iomem *rbd; + struct priv *p = netdev_priv(dev); + + if (debuglevel > 0) + printk("R"); + + for (; (status = readb(&p->rfd_top->stat_high)) & RFD_COMPL;) { + rbd = make32(readw(&p->rfd_top->rbd_offset)); + if (status & RFD_OK) { /* frame received without error? */ + totlen = readw(&rbd->status); + if (totlen & RBD_LAST) { + /* the first and the last buffer? */ + totlen &= RBD_MASK; /* length of this frame */ + writew(0x00, &rbd->status); + skb = (struct sk_buff *)dev_alloc_skb(totlen+2); + if (skb != NULL) { + skb_reserve(skb, 2); + skb_put(skb, totlen); + memcpy_fromio(skb->data, p->base + readl(&rbd->buffer), totlen); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += totlen; + } else + dev->stats.rx_dropped++; + } else { + int rstat; + /* free all RBD's until RBD_LAST is set */ + totlen = 0; + while (!((rstat = readw(&rbd->status)) & RBD_LAST)) { + totlen += rstat & RBD_MASK; + if (!rstat) { + printk(KERN_ERR "%s: Whoops .. no end mark in RBD list\n", dev->name); + break; + } + writew(0, &rbd->status); + rbd = make32(readw(&rbd->next)); + } + totlen += rstat & RBD_MASK; + writew(0, &rbd->status); + printk(KERN_ERR "%s: received oversized frame! length: %d\n", + dev->name, totlen); + dev->stats.rx_dropped++; + } + } else {/* frame !(ok), only with 'save-bad-frames' */ + printk(KERN_ERR "%s: oops! rfd-error-status: %04x\n", + dev->name, status); + dev->stats.rx_errors++; + } + writeb(0, &p->rfd_top->stat_high); + writeb(RFD_SUSP, &p->rfd_top->last); /* maybe exchange by RFD_LAST */ + writew(0xffff, &p->rfd_top->rbd_offset); + writeb(0, &p->rfd_last->last); /* delete RFD_SUSP */ + p->rfd_last = p->rfd_top; + p->rfd_top = make32(readw(&p->rfd_top->next)); /* step to next RFD */ + writew(make16(p->rfd_top), &p->scb->rfa_offset); + + if (debuglevel > 0) + printk("%d", cnt++); + } + + if (automatic_resume) { + wait_for_scb_cmd(dev); + writeb(RUC_RESUME, &p->scb->cmd_ruc); + ni_attn586(); + wait_for_scb_cmd_ruc(dev); + } + +#ifdef WAIT_4_BUSY + { + int i; + for (i = 0; i < 1024; i++) { + if (p->rfd_top->status) + break; + udelay(16); + if (i == 1023) + printk(KERN_ERR "%s: RU hasn't fetched next RFD (not busy/complete)\n", dev->name); + } + } +#endif + if (debuglevel > 0) + printk("r"); +} + +/********************************************************** + * handle 'Receiver went not ready'. + */ + +static void ni52_rnr_int(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + dev->stats.rx_errors++; + + wait_for_scb_cmd(dev); /* wait for the last cmd, WAIT_4_FULLSTAT?? */ + writeb(RUC_ABORT, &p->scb->cmd_ruc); /* usually the RU is in the 'no resource'-state .. abort it now. */ + ni_attn586(); + wait_for_scb_cmd_ruc(dev); /* wait for accept cmd. */ + + alloc_rfa(dev, p->rfd_first); + /* maybe add a check here, before restarting the RU */ + startrecv586(dev); /* restart RU */ + + printk(KERN_ERR "%s: Receive-Unit restarted. Status: %04x\n", + dev->name, readb(&p->scb->rus)); + +} + +/********************************************************** + * handle xmit - interrupt + */ + +static void ni52_xmt_int(struct net_device *dev) +{ + int status; + struct priv *p = netdev_priv(dev); + + if (debuglevel > 0) + printk("X"); + + status = readw(&p->xmit_cmds[p->xmit_last]->cmd_status); + if (!(status & STAT_COMPL)) + printk(KERN_ERR "%s: strange .. xmit-int without a 'COMPLETE'\n", dev->name); + + if (status & STAT_OK) { + dev->stats.tx_packets++; + dev->stats.collisions += (status & TCMD_MAXCOLLMASK); + } else { + dev->stats.tx_errors++; + if (status & TCMD_LATECOLL) { + printk(KERN_ERR "%s: late collision detected.\n", + dev->name); + dev->stats.collisions++; + } else if (status & TCMD_NOCARRIER) { + dev->stats.tx_carrier_errors++; + printk(KERN_ERR "%s: no carrier detected.\n", + dev->name); + } else if (status & TCMD_LOSTCTS) + printk(KERN_ERR "%s: loss of CTS detected.\n", + dev->name); + else if (status & TCMD_UNDERRUN) { + dev->stats.tx_fifo_errors++; + printk(KERN_ERR "%s: DMA underrun detected.\n", + dev->name); + } else if (status & TCMD_MAXCOLL) { + printk(KERN_ERR "%s: Max. collisions exceeded.\n", + dev->name); + dev->stats.collisions += 16; + } + } +#if (NUM_XMIT_BUFFS > 1) + if ((++p->xmit_last) == NUM_XMIT_BUFFS) + p->xmit_last = 0; +#endif + netif_wake_queue(dev); +} + +/*********************************************************** + * (re)start the receiver + */ + +static void startrecv586(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + wait_for_scb_cmd(dev); + wait_for_scb_cmd_ruc(dev); + writew(make16(p->rfd_first), &p->scb->rfa_offset); + writeb(RUC_START, &p->scb->cmd_ruc); + ni_attn586(); /* start cmd. */ + wait_for_scb_cmd_ruc(dev); + /* wait for accept cmd. (no timeout!!) */ +} + +static void ni52_timeout(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); +#ifndef NO_NOPCOMMANDS + if (readb(&p->scb->cus) & CU_ACTIVE) { /* COMMAND-UNIT active? */ + netif_wake_queue(dev); +#ifdef DEBUG + printk(KERN_ERR "%s: strange ... timeout with CU active?!?\n", + dev->name); + printk(KERN_ERR "%s: X0: %04x N0: %04x N1: %04x %d\n", + dev->name, (int)p->xmit_cmds[0]->cmd_status, + readw(&p->nop_cmds[0]->cmd_status), + readw(&p->nop_cmds[1]->cmd_status), + p->nop_point); +#endif + writeb(CUC_ABORT, &p->scb->cmd_cuc); + ni_attn586(); + wait_for_scb_cmd(dev); + writew(make16(p->nop_cmds[p->nop_point]), &p->scb->cbl_offset); + writeb(CUC_START, &p->scb->cmd_cuc); + ni_attn586(); + wait_for_scb_cmd(dev); + dev->trans_start = jiffies; /* prevent tx timeout */ + return 0; + } +#endif + { +#ifdef DEBUG + printk(KERN_ERR "%s: xmitter timed out, try to restart! stat: %02x\n", + dev->name, readb(&p->scb->cus)); + printk(KERN_ERR "%s: command-stats: %04x %04x\n", + dev->name, + readw(&p->xmit_cmds[0]->cmd_status), + readw(&p->xmit_cmds[1]->cmd_status)); + printk(KERN_ERR "%s: check, whether you set the right interrupt number!\n", + dev->name); +#endif + ni52_close(dev); + ni52_open(dev); + } + dev->trans_start = jiffies; /* prevent tx timeout */ +} + +/****************************************************** + * send frame + */ + +static netdev_tx_t ni52_send_packet(struct sk_buff *skb, + struct net_device *dev) +{ + int len, i; +#ifndef NO_NOPCOMMANDS + int next_nop; +#endif + struct priv *p = netdev_priv(dev); + + if (skb->len > XMIT_BUFF_SIZE) { + printk(KERN_ERR "%s: Sorry, max. framelength is %d bytes. The length of your frame is %d bytes.\n", dev->name, XMIT_BUFF_SIZE, skb->len); + return NETDEV_TX_OK; + } + + netif_stop_queue(dev); + + memcpy_toio(p->xmit_cbuffs[p->xmit_count], skb->data, skb->len); + len = skb->len; + if (len < ETH_ZLEN) { + len = ETH_ZLEN; + memset_io(p->xmit_cbuffs[p->xmit_count]+skb->len, 0, + len - skb->len); + } + +#if (NUM_XMIT_BUFFS == 1) +# ifdef NO_NOPCOMMANDS + +#ifdef DEBUG + if (readb(&p->scb->cus) & CU_ACTIVE) { + printk(KERN_ERR "%s: Hmmm .. CU is still running and we wanna send a new packet.\n", dev->name); + printk(KERN_ERR "%s: stat: %04x %04x\n", + dev->name, readb(&p->scb->cus), + readw(&p->xmit_cmds[0]->cmd_status)); + } +#endif + writew(TBD_LAST | len, &p->xmit_buffs[0]->size); + for (i = 0; i < 16; i++) { + writew(0, &p->xmit_cmds[0]->cmd_status); + wait_for_scb_cmd(dev); + if ((readb(&p->scb->cus) & CU_STATUS) == CU_SUSPEND) + writeb(CUC_RESUME, &p->scb->cmd_cuc); + else { + writew(make16(p->xmit_cmds[0]), &p->scb->cbl_offset); + writeb(CUC_START, &p->scb->cmd_cuc); + } + ni_attn586(); + if (!i) + dev_kfree_skb(skb); + wait_for_scb_cmd(dev); + /* test it, because CU sometimes doesn't start immediately */ + if (readb(&p->scb->cus) & CU_ACTIVE) + break; + if (readw(&p->xmit_cmds[0]->cmd_status)) + break; + if (i == 15) + printk(KERN_WARNING "%s: Can't start transmit-command.\n", dev->name); + } +# else + next_nop = (p->nop_point + 1) & 0x1; + writew(TBD_LAST | len, &p->xmit_buffs[0]->size); + writew(make16(p->nop_cmds[next_nop]), &p->xmit_cmds[0]->cmd_link); + writew(make16(p->nop_cmds[next_nop]), + &p->nop_cmds[next_nop]->cmd_link); + writew(0, &p->xmit_cmds[0]->cmd_status); + writew(0, &p->nop_cmds[next_nop]->cmd_status); + + writew(make16(p->xmit_cmds[0]), &p->nop_cmds[p->nop_point]->cmd_link); + p->nop_point = next_nop; + dev_kfree_skb(skb); +# endif +#else + writew(TBD_LAST | len, &p->xmit_buffs[p->xmit_count]->size); + next_nop = p->xmit_count + 1 + if (next_nop == NUM_XMIT_BUFFS) + next_nop = 0; + writew(0, &p->xmit_cmds[p->xmit_count]->cmd_status); + /* linkpointer of xmit-command already points to next nop cmd */ + writew(make16(p->nop_cmds[next_nop]), + &p->nop_cmds[next_nop]->cmd_link); + writew(0, &p->nop_cmds[next_nop]->cmd_status); + writew(make16(p->xmit_cmds[p->xmit_count]), + &p->nop_cmds[p->xmit_count]->cmd_link); + p->xmit_count = next_nop; + { + unsigned long flags; + spin_lock_irqsave(&p->spinlock); + if (p->xmit_count != p->xmit_last) + netif_wake_queue(dev); + spin_unlock_irqrestore(&p->spinlock); + } + dev_kfree_skb(skb); +#endif + return NETDEV_TX_OK; +} + +/******************************************* + * Someone wanna have the statistics + */ + +static struct net_device_stats *ni52_get_stats(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + unsigned short crc, aln, rsc, ovrn; + + /* Get error-statistics from the ni82586 */ + crc = readw(&p->scb->crc_errs); + writew(0, &p->scb->crc_errs); + aln = readw(&p->scb->aln_errs); + writew(0, &p->scb->aln_errs); + rsc = readw(&p->scb->rsc_errs); + writew(0, &p->scb->rsc_errs); + ovrn = readw(&p->scb->ovrn_errs); + writew(0, &p->scb->ovrn_errs); + + dev->stats.rx_crc_errors += crc; + dev->stats.rx_fifo_errors += ovrn; + dev->stats.rx_frame_errors += aln; + dev->stats.rx_dropped += rsc; + + return &dev->stats; +} + +/******************************************************** + * Set MC list .. + */ + +static void set_multicast_list(struct net_device *dev) +{ + netif_stop_queue(dev); + ni_disint(); + alloc586(dev); + init586(dev); + startrecv586(dev); + ni_enaint(); + netif_wake_queue(dev); +} + +#ifdef MODULE +static struct net_device *dev_ni52; + +module_param(io, int, 0); +module_param(irq, int, 0); +module_param(memstart, long, 0); +module_param(memend, long, 0); +MODULE_PARM_DESC(io, "NI5210 I/O base address,required"); +MODULE_PARM_DESC(irq, "NI5210 IRQ number,required"); +MODULE_PARM_DESC(memstart, "NI5210 memory base address,required"); +MODULE_PARM_DESC(memend, "NI5210 memory end address,required"); + +int __init init_module(void) +{ + if (io <= 0x0 || !memend || !memstart || irq < 2) { + printk(KERN_ERR "ni52: Autoprobing not allowed for modules.\n"); + printk(KERN_ERR "ni52: Set symbols 'io' 'irq' 'memstart' and 'memend'\n"); + return -ENODEV; + } + dev_ni52 = ni52_probe(-1); + if (IS_ERR(dev_ni52)) + return PTR_ERR(dev_ni52); + return 0; +} + +void __exit cleanup_module(void) +{ + struct priv *p = netdev_priv(dev_ni52); + unregister_netdev(dev_ni52); + iounmap(p->mapped); + release_region(dev_ni52->base_addr, NI52_TOTAL_SIZE); + free_netdev(dev_ni52); +} +#endif /* MODULE */ + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/i825xx/ni52.h b/drivers/net/ethernet/i825xx/ni52.h new file mode 100644 index 000000000000..0a03b2883327 --- /dev/null +++ b/drivers/net/ethernet/i825xx/ni52.h @@ -0,0 +1,310 @@ +/* + * Intel i82586 Ethernet definitions + * + * This is an extension to the Linux operating system, and is covered by the + * same GNU General Public License that covers that work. + * + * copyrights (c) 1994 by Michael Hipp (hippm@informatik.uni-tuebingen.de) + * + * I have done a look in the following sources: + * crynwr-packet-driver by Russ Nelson + * Garret A. Wollman's i82586-driver for BSD + */ + + +#define NI52_RESET 0 /* writing to this address, resets the i82586 */ +#define NI52_ATTENTION 1 /* channel attention, kick the 586 */ +#define NI52_TENA 3 /* 2-5 possibly wrong, Xmit enable */ +#define NI52_TDIS 2 /* Xmit disable */ +#define NI52_INTENA 5 /* Interrupt enable */ +#define NI52_INTDIS 4 /* Interrupt disable */ +#define NI52_MAGIC1 6 /* dunno exact function */ +#define NI52_MAGIC2 7 /* dunno exact function */ + +#define NI52_MAGICVAL1 0x00 /* magic-values for ni5210 card */ +#define NI52_MAGICVAL2 0x55 + +/* + * where to find the System Configuration Pointer (SCP) + */ +#define SCP_DEFAULT_ADDRESS 0xfffff4 + + +/* + * System Configuration Pointer Struct + */ + +struct scp_struct +{ + u16 zero_dum0; /* has to be zero */ + u8 sysbus; /* 0=16Bit,1=8Bit */ + u8 zero_dum1; /* has to be zero for 586 */ + u16 zero_dum2; + u16 zero_dum3; + u32 iscp; /* pointer to the iscp-block */ +}; + + +/* + * Intermediate System Configuration Pointer (ISCP) + */ +struct iscp_struct +{ + u8 busy; /* 586 clears after successful init */ + u8 zero_dummy; /* has to be zero */ + u16 scb_offset; /* pointeroffset to the scb_base */ + u32 scb_base; /* base-address of all 16-bit offsets */ +}; + +/* + * System Control Block (SCB) + */ +struct scb_struct +{ + u8 rus; + u8 cus; + u8 cmd_ruc; /* command word: RU part */ + u8 cmd_cuc; /* command word: CU part & ACK */ + u16 cbl_offset; /* pointeroffset, command block list */ + u16 rfa_offset; /* pointeroffset, receive frame area */ + u16 crc_errs; /* CRC-Error counter */ + u16 aln_errs; /* alignmenterror counter */ + u16 rsc_errs; /* Resourceerror counter */ + u16 ovrn_errs; /* OVerrunerror counter */ +}; + +/* + * possible command values for the command word + */ +#define RUC_MASK 0x0070 /* mask for RU commands */ +#define RUC_NOP 0x0000 /* NOP-command */ +#define RUC_START 0x0010 /* start RU */ +#define RUC_RESUME 0x0020 /* resume RU after suspend */ +#define RUC_SUSPEND 0x0030 /* suspend RU */ +#define RUC_ABORT 0x0040 /* abort receiver operation immediately */ + +#define CUC_MASK 0x07 /* mask for CU command */ +#define CUC_NOP 0x00 /* NOP-command */ +#define CUC_START 0x01 /* start execution of 1. cmd on the CBL */ +#define CUC_RESUME 0x02 /* resume after suspend */ +#define CUC_SUSPEND 0x03 /* Suspend CU */ +#define CUC_ABORT 0x04 /* abort command operation immediately */ + +#define ACK_MASK 0xf0 /* mask for ACK command */ +#define ACK_CX 0x80 /* acknowledges STAT_CX */ +#define ACK_FR 0x40 /* ack. STAT_FR */ +#define ACK_CNA 0x20 /* ack. STAT_CNA */ +#define ACK_RNR 0x10 /* ack. STAT_RNR */ + +/* + * possible status values for the status word + */ +#define STAT_MASK 0xf0 /* mask for cause of interrupt */ +#define STAT_CX 0x80 /* CU finished cmd with its I bit set */ +#define STAT_FR 0x40 /* RU finished receiving a frame */ +#define STAT_CNA 0x20 /* CU left active state */ +#define STAT_RNR 0x10 /* RU left ready state */ + +#define CU_STATUS 0x7 /* CU status, 0=idle */ +#define CU_SUSPEND 0x1 /* CU is suspended */ +#define CU_ACTIVE 0x2 /* CU is active */ + +#define RU_STATUS 0x70 /* RU status, 0=idle */ +#define RU_SUSPEND 0x10 /* RU suspended */ +#define RU_NOSPACE 0x20 /* RU no resources */ +#define RU_READY 0x40 /* RU is ready */ + +/* + * Receive Frame Descriptor (RFD) + */ +struct rfd_struct +{ + u8 stat_low; /* status word */ + u8 stat_high; /* status word */ + u8 rfd_sf; /* 82596 mode only */ + u8 last; /* Bit15,Last Frame on List / Bit14,suspend */ + u16 next; /* linkoffset to next RFD */ + u16 rbd_offset; /* pointeroffset to RBD-buffer */ + u8 dest[6]; /* ethernet-address, destination */ + u8 source[6]; /* ethernet-address, source */ + u16 length; /* 802.3 frame-length */ + u16 zero_dummy; /* dummy */ +}; + +#define RFD_LAST 0x80 /* last: last rfd in the list */ +#define RFD_SUSP 0x40 /* last: suspend RU after */ +#define RFD_COMPL 0x80 +#define RFD_OK 0x20 +#define RFD_BUSY 0x40 +#define RFD_ERR_LEN 0x10 /* Length error (if enabled length-checking */ +#define RFD_ERR_CRC 0x08 /* CRC error */ +#define RFD_ERR_ALGN 0x04 /* Alignment error */ +#define RFD_ERR_RNR 0x02 /* status: receiver out of resources */ +#define RFD_ERR_OVR 0x01 /* DMA Overrun! */ + +#define RFD_ERR_FTS 0x0080 /* Frame to short */ +#define RFD_ERR_NEOP 0x0040 /* No EOP flag (for bitstuffing only) */ +#define RFD_ERR_TRUN 0x0020 /* (82596 only/SF mode) indicates truncated frame */ +#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA (only 82596) */ +#define RFD_COLLDET 0x0001 /* Detected collision during reception */ + +/* + * Receive Buffer Descriptor (RBD) + */ +struct rbd_struct +{ + u16 status; /* status word,number of used bytes in buff */ + u16 next; /* pointeroffset to next RBD */ + u32 buffer; /* receive buffer address pointer */ + u16 size; /* size of this buffer */ + u16 zero_dummy; /* dummy */ +}; + +#define RBD_LAST 0x8000 /* last buffer */ +#define RBD_USED 0x4000 /* this buffer has data */ +#define RBD_MASK 0x3fff /* size-mask for length */ + +/* + * Statusvalues for Commands/RFD + */ +#define STAT_COMPL 0x8000 /* status: frame/command is complete */ +#define STAT_BUSY 0x4000 /* status: frame/command is busy */ +#define STAT_OK 0x2000 /* status: frame/command is ok */ + +/* + * Action-Commands + */ +#define CMD_NOP 0x0000 /* NOP */ +#define CMD_IASETUP 0x0001 /* initial address setup command */ +#define CMD_CONFIGURE 0x0002 /* configure command */ +#define CMD_MCSETUP 0x0003 /* MC setup command */ +#define CMD_XMIT 0x0004 /* transmit command */ +#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */ +#define CMD_DUMP 0x0006 /* dump command */ +#define CMD_DIAGNOSE 0x0007 /* diagnose command */ + +/* + * Action command bits + */ +#define CMD_LAST 0x8000 /* indicates last command in the CBL */ +#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */ +#define CMD_INT 0x2000 /* generate interrupt after execution */ + +/* + * NOP - command + */ +struct nop_cmd_struct +{ + u16 cmd_status; /* status of this command */ + u16 cmd_cmd; /* the command itself (+bits) */ + u16 cmd_link; /* offsetpointer to next command */ +}; + +/* + * IA Setup command + */ +struct iasetup_cmd_struct +{ + u16 cmd_status; + u16 cmd_cmd; + u16 cmd_link; + u8 iaddr[6]; +}; + +/* + * Configure command + */ +struct configure_cmd_struct +{ + u16 cmd_status; + u16 cmd_cmd; + u16 cmd_link; + u8 byte_cnt; /* size of the config-cmd */ + u8 fifo; /* fifo/recv monitor */ + u8 sav_bf; /* save bad frames (bit7=1)*/ + u8 adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/ + u8 priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */ + u8 ifs; /* inter frame spacing */ + u8 time_low; /* slot time low */ + u8 time_high; /* slot time high(0-2) and max. retries(4-7) */ + u8 promisc; /* promisc-mode(0) , et al (1-7) */ + u8 carr_coll; /* carrier(0-3)/collision(4-7) stuff */ + u8 fram_len; /* minimal frame len */ + u8 dummy; /* dummy */ +}; + +/* + * Multicast Setup command + */ +struct mcsetup_cmd_struct +{ + u16 cmd_status; + u16 cmd_cmd; + u16 cmd_link; + u16 mc_cnt; /* number of bytes in the MC-List */ + u8 mc_list[0][6]; /* pointer to 6 bytes entries */ +}; + +/* + * DUMP command + */ +struct dump_cmd_struct +{ + u16 cmd_status; + u16 cmd_cmd; + u16 cmd_link; + u16 dump_offset; /* pointeroffset to DUMP space */ +}; + +/* + * transmit command + */ +struct transmit_cmd_struct +{ + u16 cmd_status; + u16 cmd_cmd; + u16 cmd_link; + u16 tbd_offset; /* pointeroffset to TBD */ + u8 dest[6]; /* destination address of the frame */ + u16 length; /* user defined: 802.3 length / Ether type */ +}; + +#define TCMD_ERRMASK 0x0fa0 +#define TCMD_MAXCOLLMASK 0x000f +#define TCMD_MAXCOLL 0x0020 +#define TCMD_HEARTBEAT 0x0040 +#define TCMD_DEFERRED 0x0080 +#define TCMD_UNDERRUN 0x0100 +#define TCMD_LOSTCTS 0x0200 +#define TCMD_NOCARRIER 0x0400 +#define TCMD_LATECOLL 0x0800 + +struct tdr_cmd_struct +{ + u16 cmd_status; + u16 cmd_cmd; + u16 cmd_link; + u16 status; +}; + +#define TDR_LNK_OK 0x8000 /* No link problem identified */ +#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */ +#define TDR_ET_OPN 0x2000 /* open, no correct termination */ +#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */ +#define TDR_TIMEMASK 0x07ff /* mask for the time field */ + +/* + * Transmit Buffer Descriptor (TBD) + */ +struct tbd_struct +{ + u16 size; /* size + EOF-Flag(15) */ + u16 next; /* pointeroffset to next TBD */ + u32 buffer; /* pointer to buffer */ +}; + +#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */ + + + + diff --git a/drivers/net/ethernet/i825xx/sni_82596.c b/drivers/net/ethernet/i825xx/sni_82596.c new file mode 100644 index 000000000000..6b2a88817473 --- /dev/null +++ b/drivers/net/ethernet/i825xx/sni_82596.c @@ -0,0 +1,186 @@ +/* + * sni_82596.c -- driver for intel 82596 ethernet controller, as + * used in older SNI RM machines + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/irq.h> + +#define SNI_82596_DRIVER_VERSION "SNI RM 82596 driver - Revision: 0.01" + +static const char sni_82596_string[] = "snirm_82596"; + +#define DMA_ALLOC dma_alloc_coherent +#define DMA_FREE dma_free_coherent +#define DMA_WBACK(priv, addr, len) do { } while (0) +#define DMA_INV(priv, addr, len) do { } while (0) +#define DMA_WBACK_INV(priv, addr, len) do { } while (0) + +#define SYSBUS 0x00004400 + +/* big endian CPU, 82596 little endian */ +#define SWAP32(x) cpu_to_le32((u32)(x)) +#define SWAP16(x) cpu_to_le16((u16)(x)) + +#define OPT_MPU_16BIT 0x01 + +#include "lib82596.c" + +MODULE_AUTHOR("Thomas Bogendoerfer"); +MODULE_DESCRIPTION("i82596 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:snirm_82596"); +module_param(i596_debug, int, 0); +MODULE_PARM_DESC(i596_debug, "82596 debug mask"); + +static inline void ca(struct net_device *dev) +{ + struct i596_private *lp = netdev_priv(dev); + + writel(0, lp->ca); +} + + +static void mpu_port(struct net_device *dev, int c, dma_addr_t x) +{ + struct i596_private *lp = netdev_priv(dev); + + u32 v = (u32) (c) | (u32) (x); + + if (lp->options & OPT_MPU_16BIT) { + writew(v & 0xffff, lp->mpu_port); + wmb(); /* order writes to MPU port */ + udelay(1); + writew(v >> 16, lp->mpu_port); + } else { + writel(v, lp->mpu_port); + wmb(); /* order writes to MPU port */ + udelay(1); + writel(v, lp->mpu_port); + } +} + + +static int __devinit sni_82596_probe(struct platform_device *dev) +{ + struct net_device *netdevice; + struct i596_private *lp; + struct resource *res, *ca, *idprom, *options; + int retval = -ENOMEM; + void __iomem *mpu_addr; + void __iomem *ca_addr; + u8 __iomem *eth_addr; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + ca = platform_get_resource(dev, IORESOURCE_MEM, 1); + options = platform_get_resource(dev, 0, 0); + idprom = platform_get_resource(dev, IORESOURCE_MEM, 2); + if (!res || !ca || !options || !idprom) + return -ENODEV; + mpu_addr = ioremap_nocache(res->start, 4); + if (!mpu_addr) + return -ENOMEM; + ca_addr = ioremap_nocache(ca->start, 4); + if (!ca_addr) + goto probe_failed_free_mpu; + + printk(KERN_INFO "Found i82596 at 0x%x\n", res->start); + + netdevice = alloc_etherdev(sizeof(struct i596_private)); + if (!netdevice) + goto probe_failed_free_ca; + + SET_NETDEV_DEV(netdevice, &dev->dev); + platform_set_drvdata (dev, netdevice); + + netdevice->base_addr = res->start; + netdevice->irq = platform_get_irq(dev, 0); + + eth_addr = ioremap_nocache(idprom->start, 0x10); + if (!eth_addr) + goto probe_failed; + + /* someone seems to like messed up stuff */ + netdevice->dev_addr[0] = readb(eth_addr + 0x0b); + netdevice->dev_addr[1] = readb(eth_addr + 0x0a); + netdevice->dev_addr[2] = readb(eth_addr + 0x09); + netdevice->dev_addr[3] = readb(eth_addr + 0x08); + netdevice->dev_addr[4] = readb(eth_addr + 0x07); + netdevice->dev_addr[5] = readb(eth_addr + 0x06); + iounmap(eth_addr); + + if (!netdevice->irq) { + printk(KERN_ERR "%s: IRQ not found for i82596 at 0x%lx\n", + __FILE__, netdevice->base_addr); + goto probe_failed; + } + + lp = netdev_priv(netdevice); + lp->options = options->flags & IORESOURCE_BITS; + lp->ca = ca_addr; + lp->mpu_port = mpu_addr; + + retval = i82596_probe(netdevice); + if (retval == 0) + return 0; + +probe_failed: + free_netdev(netdevice); +probe_failed_free_ca: + iounmap(ca_addr); +probe_failed_free_mpu: + iounmap(mpu_addr); + return retval; +} + +static int __devexit sni_82596_driver_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct i596_private *lp = netdev_priv(dev); + + unregister_netdev(dev); + DMA_FREE(dev->dev.parent, sizeof(struct i596_private), + lp->dma, lp->dma_addr); + iounmap(lp->ca); + iounmap(lp->mpu_port); + free_netdev (dev); + return 0; +} + +static struct platform_driver sni_82596_driver = { + .probe = sni_82596_probe, + .remove = __devexit_p(sni_82596_driver_remove), + .driver = { + .name = sni_82596_string, + .owner = THIS_MODULE, + }, +}; + +static int __devinit sni_82596_init(void) +{ + printk(KERN_INFO SNI_82596_DRIVER_VERSION "\n"); + return platform_driver_register(&sni_82596_driver); +} + + +static void __exit sni_82596_exit(void) +{ + platform_driver_unregister(&sni_82596_driver); +} + +module_init(sni_82596_init); +module_exit(sni_82596_exit); diff --git a/drivers/net/ethernet/i825xx/sun3_82586.c b/drivers/net/ethernet/i825xx/sun3_82586.c new file mode 100644 index 000000000000..b6ae53bada75 --- /dev/null +++ b/drivers/net/ethernet/i825xx/sun3_82586.c @@ -0,0 +1,1213 @@ +/* + * Sun3 i82586 Ethernet driver + * + * Cloned from ni52.c for the Sun3 by Sam Creasey (sammy@sammy.net) + * + * Original copyright follows: + * -------------------------- + * + * net-3-driver for the NI5210 card (i82586 Ethernet chip) + * + * This is an extension to the Linux operating system, and is covered by the + * same Gnu Public License that covers that work. + * + * Alphacode 0.82 (96/09/29) for Linux 2.0.0 (or later) + * Copyrights (c) 1994,1995,1996 by M.Hipp (hippm@informatik.uni-tuebingen.de) + * -------------------------- + * + * Consult ni52.c for further notes from the original driver. + * + * This incarnation currently supports the OBIO version of the i82586 chip + * used in certain sun3 models. It should be fairly doable to expand this + * to support VME if I should every acquire such a board. + * + */ + +static int debuglevel = 0; /* debug-printk 0: off 1: a few 2: more */ +static int automatic_resume = 0; /* experimental .. better should be zero */ +static int rfdadd = 0; /* rfdadd=1 may be better for 8K MEM cards */ +static int fifo=0x8; /* don't change */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <asm/io.h> +#include <asm/idprom.h> +#include <asm/machines.h> +#include <asm/sun3mmu.h> +#include <asm/dvma.h> +#include <asm/byteorder.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "sun3_82586.h" + +#define DRV_NAME "sun3_82586" + +#define DEBUG /* debug on */ +#define SYSBUSVAL 0 /* 16 Bit */ +#define SUN3_82586_TOTAL_SIZE PAGE_SIZE + +#define sun3_attn586() {*(volatile unsigned char *)(dev->base_addr) |= IEOB_ATTEN; *(volatile unsigned char *)(dev->base_addr) &= ~IEOB_ATTEN;} +#define sun3_reset586() {*(volatile unsigned char *)(dev->base_addr) = 0; udelay(100); *(volatile unsigned char *)(dev->base_addr) = IEOB_NORSET;} +#define sun3_disint() {*(volatile unsigned char *)(dev->base_addr) &= ~IEOB_IENAB;} +#define sun3_enaint() {*(volatile unsigned char *)(dev->base_addr) |= IEOB_IENAB;} +#define sun3_active() {*(volatile unsigned char *)(dev->base_addr) |= (IEOB_IENAB|IEOB_ONAIR|IEOB_NORSET);} + +#define make32(ptr16) (p->memtop + (swab16((unsigned short) (ptr16))) ) +#define make24(ptr32) (char *)swab32(( ((unsigned long) (ptr32)) - p->base)) +#define make16(ptr32) (swab16((unsigned short) ((unsigned long)(ptr32) - (unsigned long) p->memtop ))) + +/******************* how to calculate the buffers ***************************** + + * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works + * --------------- in a different (more stable?) mode. Only in this mode it's + * possible to configure the driver with 'NO_NOPCOMMANDS' + +sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8; +sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT +sizeof(rfd) = 24; sizeof(rbd) = 12; +sizeof(tbd) = 8; sizeof(transmit_cmd) = 16; +sizeof(nop_cmd) = 8; + + * if you don't know the driver, better do not change these values: */ + +#define RECV_BUFF_SIZE 1536 /* slightly oversized */ +#define XMIT_BUFF_SIZE 1536 /* slightly oversized */ +#define NUM_XMIT_BUFFS 1 /* config for 32K shmem */ +#define NUM_RECV_BUFFS_8 4 /* config for 32K shared mem */ +#define NUM_RECV_BUFFS_16 9 /* config for 32K shared mem */ +#define NUM_RECV_BUFFS_32 16 /* config for 32K shared mem */ +#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */ + +/**************************************************************************/ + +/* different DELAYs */ +#define DELAY(x) mdelay(32 * x); +#define DELAY_16(); { udelay(16); } +#define DELAY_18(); { udelay(4); } + +/* wait for command with timeout: */ +#define WAIT_4_SCB_CMD() \ +{ int i; \ + for(i=0;i<16384;i++) { \ + if(!p->scb->cmd_cuc) break; \ + DELAY_18(); \ + if(i == 16383) { \ + printk("%s: scb_cmd timed out: %04x,%04x .. disabling i82586!!\n",dev->name,p->scb->cmd_cuc,p->scb->cus); \ + if(!p->reseted) { p->reseted = 1; sun3_reset586(); } } } } + +#define WAIT_4_SCB_CMD_RUC() { int i; \ + for(i=0;i<16384;i++) { \ + if(!p->scb->cmd_ruc) break; \ + DELAY_18(); \ + if(i == 16383) { \ + printk("%s: scb_cmd (ruc) timed out: %04x,%04x .. disabling i82586!!\n",dev->name,p->scb->cmd_ruc,p->scb->rus); \ + if(!p->reseted) { p->reseted = 1; sun3_reset586(); } } } } + +#define WAIT_4_STAT_COMPL(addr) { int i; \ + for(i=0;i<32767;i++) { \ + if(swab16((addr)->cmd_status) & STAT_COMPL) break; \ + DELAY_16(); DELAY_16(); } } + +static int sun3_82586_probe1(struct net_device *dev,int ioaddr); +static irqreturn_t sun3_82586_interrupt(int irq,void *dev_id); +static int sun3_82586_open(struct net_device *dev); +static int sun3_82586_close(struct net_device *dev); +static int sun3_82586_send_packet(struct sk_buff *,struct net_device *); +static struct net_device_stats *sun3_82586_get_stats(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void sun3_82586_timeout(struct net_device *dev); +#if 0 +static void sun3_82586_dump(struct net_device *,void *); +#endif + +/* helper-functions */ +static int init586(struct net_device *dev); +static int check586(struct net_device *dev,char *where,unsigned size); +static void alloc586(struct net_device *dev); +static void startrecv586(struct net_device *dev); +static void *alloc_rfa(struct net_device *dev,void *ptr); +static void sun3_82586_rcv_int(struct net_device *dev); +static void sun3_82586_xmt_int(struct net_device *dev); +static void sun3_82586_rnr_int(struct net_device *dev); + +struct priv +{ + unsigned long base; + char *memtop; + long int lock; + int reseted; + volatile struct rfd_struct *rfd_last,*rfd_top,*rfd_first; + volatile struct scp_struct *scp; /* volatile is important */ + volatile struct iscp_struct *iscp; /* volatile is important */ + volatile struct scb_struct *scb; /* volatile is important */ + volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS]; + volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS]; +#if (NUM_XMIT_BUFFS == 1) + volatile struct nop_cmd_struct *nop_cmds[2]; +#else + volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS]; +#endif + volatile int nop_point,num_recv_buffs; + volatile char *xmit_cbuffs[NUM_XMIT_BUFFS]; + volatile int xmit_count,xmit_last; +}; + +/********************************************** + * close device + */ +static int sun3_82586_close(struct net_device *dev) +{ + free_irq(dev->irq, dev); + + sun3_reset586(); /* the hard way to stop the receiver */ + + netif_stop_queue(dev); + + return 0; +} + +/********************************************** + * open device + */ +static int sun3_82586_open(struct net_device *dev) +{ + int ret; + + sun3_disint(); + alloc586(dev); + init586(dev); + startrecv586(dev); + sun3_enaint(); + + ret = request_irq(dev->irq, sun3_82586_interrupt,0,dev->name,dev); + if (ret) + { + sun3_reset586(); + return ret; + } + + netif_start_queue(dev); + + return 0; /* most done by init */ +} + +/********************************************** + * Check to see if there's an 82586 out there. + */ +static int check586(struct net_device *dev,char *where,unsigned size) +{ + struct priv pb; + struct priv *p = &pb; + char *iscp_addr; + int i; + + p->base = (unsigned long) dvma_btov(0); + p->memtop = (char *)dvma_btov((unsigned long)where); + p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS); + memset((char *)p->scp,0, sizeof(struct scp_struct)); + for(i=0;i<sizeof(struct scp_struct);i++) /* memory was writeable? */ + if(((char *)p->scp)[i]) + return 0; + p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */ + if(p->scp->sysbus != SYSBUSVAL) + return 0; + + iscp_addr = (char *)dvma_btov((unsigned long)where); + + p->iscp = (struct iscp_struct *) iscp_addr; + memset((char *)p->iscp,0, sizeof(struct iscp_struct)); + + p->scp->iscp = make24(p->iscp); + p->iscp->busy = 1; + + sun3_reset586(); + sun3_attn586(); + DELAY(1); /* wait a while... */ + + if(p->iscp->busy) /* i82586 clears 'busy' after successful init */ + return 0; + + return 1; +} + +/****************************************************************** + * set iscp at the right place, called by sun3_82586_probe1 and open586. + */ +static void alloc586(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + sun3_reset586(); + DELAY(1); + + p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS); + p->iscp = (struct iscp_struct *) dvma_btov(dev->mem_start); + p->scb = (struct scb_struct *) ((char *)p->iscp + sizeof(struct iscp_struct)); + + memset((char *) p->iscp,0,sizeof(struct iscp_struct)); + memset((char *) p->scp ,0,sizeof(struct scp_struct)); + + p->scp->iscp = make24(p->iscp); + p->scp->sysbus = SYSBUSVAL; + p->iscp->scb_offset = make16(p->scb); + p->iscp->scb_base = make24(dvma_btov(dev->mem_start)); + + p->iscp->busy = 1; + sun3_reset586(); + sun3_attn586(); + + DELAY(1); + + if(p->iscp->busy) + printk("%s: Init-Problems (alloc).\n",dev->name); + + p->reseted = 0; + + memset((char *)p->scb,0,sizeof(struct scb_struct)); +} + +struct net_device * __init sun3_82586_probe(int unit) +{ + struct net_device *dev; + unsigned long ioaddr; + static int found = 0; + int err = -ENOMEM; + + /* check that this machine has an onboard 82586 */ + switch(idprom->id_machtype) { + case SM_SUN3|SM_3_160: + case SM_SUN3|SM_3_260: + /* these machines have 82586 */ + break; + + default: + return ERR_PTR(-ENODEV); + } + + if (found) + return ERR_PTR(-ENODEV); + + ioaddr = (unsigned long)ioremap(IE_OBIO, SUN3_82586_TOTAL_SIZE); + if (!ioaddr) + return ERR_PTR(-ENOMEM); + found = 1; + + dev = alloc_etherdev(sizeof(struct priv)); + if (!dev) + goto out; + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + } + + dev->irq = IE_IRQ; + dev->base_addr = ioaddr; + err = sun3_82586_probe1(dev, ioaddr); + if (err) + goto out1; + err = register_netdev(dev); + if (err) + goto out2; + return dev; + +out2: + release_region(ioaddr, SUN3_82586_TOTAL_SIZE); +out1: + free_netdev(dev); +out: + iounmap((void __iomem *)ioaddr); + return ERR_PTR(err); +} + +static const struct net_device_ops sun3_82586_netdev_ops = { + .ndo_open = sun3_82586_open, + .ndo_stop = sun3_82586_close, + .ndo_start_xmit = sun3_82586_send_packet, + .ndo_set_multicast_list = set_multicast_list, + .ndo_tx_timeout = sun3_82586_timeout, + .ndo_get_stats = sun3_82586_get_stats, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +static int __init sun3_82586_probe1(struct net_device *dev,int ioaddr) +{ + int i, size, retval; + + if (!request_region(ioaddr, SUN3_82586_TOTAL_SIZE, DRV_NAME)) + return -EBUSY; + + /* copy in the ethernet address from the prom */ + for(i = 0; i < 6 ; i++) + dev->dev_addr[i] = idprom->id_ethaddr[i]; + + printk("%s: SUN3 Intel 82586 found at %lx, ",dev->name,dev->base_addr); + + /* + * check (or search) IO-Memory, 32K + */ + size = 0x8000; + + dev->mem_start = (unsigned long)dvma_malloc_align(0x8000, 0x1000); + dev->mem_end = dev->mem_start + size; + + if(size != 0x2000 && size != 0x4000 && size != 0x8000) { + printk("\n%s: Illegal memory size %d. Allowed is 0x2000 or 0x4000 or 0x8000 bytes.\n",dev->name,size); + retval = -ENODEV; + goto out; + } + if(!check586(dev,(char *) dev->mem_start,size)) { + printk("?memcheck, Can't find memory at 0x%lx with size %d!\n",dev->mem_start,size); + retval = -ENODEV; + goto out; + } + + ((struct priv *)netdev_priv(dev))->memtop = + (char *)dvma_btov(dev->mem_start); + ((struct priv *)netdev_priv(dev))->base = (unsigned long) dvma_btov(0); + alloc586(dev); + + /* set number of receive-buffs according to memsize */ + if(size == 0x2000) + ((struct priv *)netdev_priv(dev))->num_recv_buffs = + NUM_RECV_BUFFS_8; + else if(size == 0x4000) + ((struct priv *)netdev_priv(dev))->num_recv_buffs = + NUM_RECV_BUFFS_16; + else + ((struct priv *)netdev_priv(dev))->num_recv_buffs = + NUM_RECV_BUFFS_32; + + printk("Memaddr: 0x%lx, Memsize: %d, IRQ %d\n",dev->mem_start,size, dev->irq); + + dev->netdev_ops = &sun3_82586_netdev_ops; + dev->watchdog_timeo = HZ/20; + + dev->if_port = 0; + return 0; +out: + release_region(ioaddr, SUN3_82586_TOTAL_SIZE); + return retval; +} + + +static int init586(struct net_device *dev) +{ + void *ptr; + int i,result=0; + struct priv *p = netdev_priv(dev); + volatile struct configure_cmd_struct *cfg_cmd; + volatile struct iasetup_cmd_struct *ias_cmd; + volatile struct tdr_cmd_struct *tdr_cmd; + volatile struct mcsetup_cmd_struct *mc_cmd; + struct netdev_hw_addr *ha; + int num_addrs=netdev_mc_count(dev); + + ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct)); + + cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */ + cfg_cmd->cmd_status = 0; + cfg_cmd->cmd_cmd = swab16(CMD_CONFIGURE | CMD_LAST); + cfg_cmd->cmd_link = 0xffff; + + cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */ + cfg_cmd->fifo = fifo; /* fifo-limit (8=tx:32/rx:64) */ + cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */ + cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */ + cfg_cmd->priority = 0x00; + cfg_cmd->ifs = 0x60; + cfg_cmd->time_low = 0x00; + cfg_cmd->time_high = 0xf2; + cfg_cmd->promisc = 0; + if(dev->flags & IFF_ALLMULTI) { + int len = ((char *) p->iscp - (char *) ptr - 8) / 6; + if(num_addrs > len) { + printk("%s: switching to promisc. mode\n",dev->name); + cfg_cmd->promisc = 1; + } + } + if(dev->flags&IFF_PROMISC) + cfg_cmd->promisc = 1; + cfg_cmd->carr_coll = 0x00; + + p->scb->cbl_offset = make16(cfg_cmd); + p->scb->cmd_ruc = 0; + + p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */ + sun3_attn586(); + + WAIT_4_STAT_COMPL(cfg_cmd); + + if((swab16(cfg_cmd->cmd_status) & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK)) + { + printk("%s: configure command failed: %x\n",dev->name,swab16(cfg_cmd->cmd_status)); + return 1; + } + + /* + * individual address setup + */ + + ias_cmd = (struct iasetup_cmd_struct *)ptr; + + ias_cmd->cmd_status = 0; + ias_cmd->cmd_cmd = swab16(CMD_IASETUP | CMD_LAST); + ias_cmd->cmd_link = 0xffff; + + memcpy((char *)&ias_cmd->iaddr,(char *) dev->dev_addr,ETH_ALEN); + + p->scb->cbl_offset = make16(ias_cmd); + + p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */ + sun3_attn586(); + + WAIT_4_STAT_COMPL(ias_cmd); + + if((swab16(ias_cmd->cmd_status) & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) { + printk("%s (82586): individual address setup command failed: %04x\n",dev->name,swab16(ias_cmd->cmd_status)); + return 1; + } + + /* + * TDR, wire check .. e.g. no resistor e.t.c + */ + + tdr_cmd = (struct tdr_cmd_struct *)ptr; + + tdr_cmd->cmd_status = 0; + tdr_cmd->cmd_cmd = swab16(CMD_TDR | CMD_LAST); + tdr_cmd->cmd_link = 0xffff; + tdr_cmd->status = 0; + + p->scb->cbl_offset = make16(tdr_cmd); + p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */ + sun3_attn586(); + + WAIT_4_STAT_COMPL(tdr_cmd); + + if(!(swab16(tdr_cmd->cmd_status) & STAT_COMPL)) + { + printk("%s: Problems while running the TDR.\n",dev->name); + } + else + { + DELAY_16(); /* wait for result */ + result = swab16(tdr_cmd->status); + + p->scb->cmd_cuc = p->scb->cus & STAT_MASK; + sun3_attn586(); /* ack the interrupts */ + + if(result & TDR_LNK_OK) + ; + else if(result & TDR_XCVR_PRB) + printk("%s: TDR: Transceiver problem. Check the cable(s)!\n",dev->name); + else if(result & TDR_ET_OPN) + printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK); + else if(result & TDR_ET_SRT) + { + if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */ + printk("%s: TDR: Detected a short circuit %d clocks away.\n",dev->name,result & TDR_TIMEMASK); + } + else + printk("%s: TDR: Unknown status %04x\n",dev->name,result); + } + + /* + * Multicast setup + */ + if(num_addrs && !(dev->flags & IFF_PROMISC) ) + { + mc_cmd = (struct mcsetup_cmd_struct *) ptr; + mc_cmd->cmd_status = 0; + mc_cmd->cmd_cmd = swab16(CMD_MCSETUP | CMD_LAST); + mc_cmd->cmd_link = 0xffff; + mc_cmd->mc_cnt = swab16(num_addrs * 6); + + i = 0; + netdev_for_each_mc_addr(ha, dev) + memcpy((char *) mc_cmd->mc_list[i++], + ha->addr, ETH_ALEN); + + p->scb->cbl_offset = make16(mc_cmd); + p->scb->cmd_cuc = CUC_START; + sun3_attn586(); + + WAIT_4_STAT_COMPL(mc_cmd); + + if( (swab16(mc_cmd->cmd_status) & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) ) + printk("%s: Can't apply multicast-address-list.\n",dev->name); + } + + /* + * alloc nop/xmit-cmds + */ +#if (NUM_XMIT_BUFFS == 1) + for(i=0;i<2;i++) + { + p->nop_cmds[i] = (struct nop_cmd_struct *)ptr; + p->nop_cmds[i]->cmd_cmd = swab16(CMD_NOP); + p->nop_cmds[i]->cmd_status = 0; + p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i])); + ptr = (char *) ptr + sizeof(struct nop_cmd_struct); + } +#else + for(i=0;i<NUM_XMIT_BUFFS;i++) + { + p->nop_cmds[i] = (struct nop_cmd_struct *)ptr; + p->nop_cmds[i]->cmd_cmd = swab16(CMD_NOP); + p->nop_cmds[i]->cmd_status = 0; + p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i])); + ptr = (char *) ptr + sizeof(struct nop_cmd_struct); + } +#endif + + ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */ + + /* + * alloc xmit-buffs / init xmit_cmds + */ + for(i=0;i<NUM_XMIT_BUFFS;i++) + { + p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/ + ptr = (char *) ptr + sizeof(struct transmit_cmd_struct); + p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */ + ptr = (char *) ptr + XMIT_BUFF_SIZE; + p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */ + ptr = (char *) ptr + sizeof(struct tbd_struct); + if((void *)ptr > (void *)dev->mem_end) + { + printk("%s: not enough shared-mem for your configuration!\n",dev->name); + return 1; + } + memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct)); + memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct)); + p->xmit_cmds[i]->cmd_link = make16(p->nop_cmds[(i+1)%NUM_XMIT_BUFFS]); + p->xmit_cmds[i]->cmd_status = swab16(STAT_COMPL); + p->xmit_cmds[i]->cmd_cmd = swab16(CMD_XMIT | CMD_INT); + p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i])); + p->xmit_buffs[i]->next = 0xffff; + p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i])); + } + + p->xmit_count = 0; + p->xmit_last = 0; +#ifndef NO_NOPCOMMANDS + p->nop_point = 0; +#endif + + /* + * 'start transmitter' + */ +#ifndef NO_NOPCOMMANDS + p->scb->cbl_offset = make16(p->nop_cmds[0]); + p->scb->cmd_cuc = CUC_START; + sun3_attn586(); + WAIT_4_SCB_CMD(); +#else + p->xmit_cmds[0]->cmd_link = make16(p->xmit_cmds[0]); + p->xmit_cmds[0]->cmd_cmd = swab16(CMD_XMIT | CMD_SUSPEND | CMD_INT); +#endif + + /* + * ack. interrupts + */ + p->scb->cmd_cuc = p->scb->cus & STAT_MASK; + sun3_attn586(); + DELAY_16(); + + sun3_enaint(); + sun3_active(); + + return 0; +} + +/****************************************************** + * This is a helper routine for sun3_82586_rnr_int() and init586(). + * It sets up the Receive Frame Area (RFA). + */ + +static void *alloc_rfa(struct net_device *dev,void *ptr) +{ + volatile struct rfd_struct *rfd = (struct rfd_struct *)ptr; + volatile struct rbd_struct *rbd; + int i; + struct priv *p = netdev_priv(dev); + + memset((char *) rfd,0,sizeof(struct rfd_struct)*(p->num_recv_buffs+rfdadd)); + p->rfd_first = rfd; + + for(i = 0; i < (p->num_recv_buffs+rfdadd); i++) { + rfd[i].next = make16(rfd + (i+1) % (p->num_recv_buffs+rfdadd) ); + rfd[i].rbd_offset = 0xffff; + } + rfd[p->num_recv_buffs-1+rfdadd].last = RFD_SUSP; /* RU suspend */ + + ptr = (void *) (rfd + (p->num_recv_buffs + rfdadd) ); + + rbd = (struct rbd_struct *) ptr; + ptr = (void *) (rbd + p->num_recv_buffs); + + /* clr descriptors */ + memset((char *) rbd,0,sizeof(struct rbd_struct)*(p->num_recv_buffs)); + + for(i=0;i<p->num_recv_buffs;i++) + { + rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs)); + rbd[i].size = swab16(RECV_BUFF_SIZE); + rbd[i].buffer = make24(ptr); + ptr = (char *) ptr + RECV_BUFF_SIZE; + } + + p->rfd_top = p->rfd_first; + p->rfd_last = p->rfd_first + (p->num_recv_buffs - 1 + rfdadd); + + p->scb->rfa_offset = make16(p->rfd_first); + p->rfd_first->rbd_offset = make16(rbd); + + return ptr; +} + + +/************************************************** + * Interrupt Handler ... + */ + +static irqreturn_t sun3_82586_interrupt(int irq,void *dev_id) +{ + struct net_device *dev = dev_id; + unsigned short stat; + int cnt=0; + struct priv *p; + + if (!dev) { + printk ("sun3_82586-interrupt: irq %d for unknown device.\n",irq); + return IRQ_NONE; + } + p = netdev_priv(dev); + + if(debuglevel > 1) + printk("I"); + + WAIT_4_SCB_CMD(); /* wait for last command */ + + while((stat=p->scb->cus & STAT_MASK)) + { + p->scb->cmd_cuc = stat; + sun3_attn586(); + + if(stat & STAT_FR) /* received a frame */ + sun3_82586_rcv_int(dev); + + if(stat & STAT_RNR) /* RU went 'not ready' */ + { + printk("(R)"); + if(p->scb->rus & RU_SUSPEND) /* special case: RU_SUSPEND */ + { + WAIT_4_SCB_CMD(); + p->scb->cmd_ruc = RUC_RESUME; + sun3_attn586(); + WAIT_4_SCB_CMD_RUC(); + } + else + { + printk("%s: Receiver-Unit went 'NOT READY': %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->rus); + sun3_82586_rnr_int(dev); + } + } + + if(stat & STAT_CX) /* command with I-bit set complete */ + sun3_82586_xmt_int(dev); + +#ifndef NO_NOPCOMMANDS + if(stat & STAT_CNA) /* CU went 'not ready' */ + { + if(netif_running(dev)) + printk("%s: oops! CU has left active state. stat: %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->cus); + } +#endif + + if(debuglevel > 1) + printk("%d",cnt++); + + WAIT_4_SCB_CMD(); /* wait for ack. (sun3_82586_xmt_int can be faster than ack!!) */ + if(p->scb->cmd_cuc) /* timed out? */ + { + printk("%s: Acknowledge timed out.\n",dev->name); + sun3_disint(); + break; + } + } + + if(debuglevel > 1) + printk("i"); + return IRQ_HANDLED; +} + +/******************************************************* + * receive-interrupt + */ + +static void sun3_82586_rcv_int(struct net_device *dev) +{ + int status,cnt=0; + unsigned short totlen; + struct sk_buff *skb; + struct rbd_struct *rbd; + struct priv *p = netdev_priv(dev); + + if(debuglevel > 0) + printk("R"); + + for(;(status = p->rfd_top->stat_high) & RFD_COMPL;) + { + rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset); + + if(status & RFD_OK) /* frame received without error? */ + { + if( (totlen = swab16(rbd->status)) & RBD_LAST) /* the first and the last buffer? */ + { + totlen &= RBD_MASK; /* length of this frame */ + rbd->status = 0; + skb = (struct sk_buff *) dev_alloc_skb(totlen+2); + if(skb != NULL) + { + skb_reserve(skb,2); + skb_put(skb,totlen); + skb_copy_to_linear_data(skb,(char *) p->base+swab32((unsigned long) rbd->buffer),totlen); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + } + else + dev->stats.rx_dropped++; + } + else + { + int rstat; + /* free all RBD's until RBD_LAST is set */ + totlen = 0; + while(!((rstat=swab16(rbd->status)) & RBD_LAST)) + { + totlen += rstat & RBD_MASK; + if(!rstat) + { + printk("%s: Whoops .. no end mark in RBD list\n",dev->name); + break; + } + rbd->status = 0; + rbd = (struct rbd_struct *) make32(rbd->next); + } + totlen += rstat & RBD_MASK; + rbd->status = 0; + printk("%s: received oversized frame! length: %d\n",dev->name,totlen); + dev->stats.rx_dropped++; + } + } + else /* frame !(ok), only with 'save-bad-frames' */ + { + printk("%s: oops! rfd-error-status: %04x\n",dev->name,status); + dev->stats.rx_errors++; + } + p->rfd_top->stat_high = 0; + p->rfd_top->last = RFD_SUSP; /* maybe exchange by RFD_LAST */ + p->rfd_top->rbd_offset = 0xffff; + p->rfd_last->last = 0; /* delete RFD_SUSP */ + p->rfd_last = p->rfd_top; + p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */ + p->scb->rfa_offset = make16(p->rfd_top); + + if(debuglevel > 0) + printk("%d",cnt++); + } + + if(automatic_resume) + { + WAIT_4_SCB_CMD(); + p->scb->cmd_ruc = RUC_RESUME; + sun3_attn586(); + WAIT_4_SCB_CMD_RUC(); + } + +#ifdef WAIT_4_BUSY + { + int i; + for(i=0;i<1024;i++) + { + if(p->rfd_top->status) + break; + DELAY_16(); + if(i == 1023) + printk("%s: RU hasn't fetched next RFD (not busy/complete)\n",dev->name); + } + } +#endif + +#if 0 + if(!at_least_one) + { + int i; + volatile struct rfd_struct *rfds=p->rfd_top; + volatile struct rbd_struct *rbds; + printk("%s: received a FC intr. without having a frame: %04x %d\n",dev->name,status,old_at_least); + for(i=0;i< (p->num_recv_buffs+4);i++) + { + rbds = (struct rbd_struct *) make32(rfds->rbd_offset); + printk("%04x:%04x ",rfds->status,rbds->status); + rfds = (struct rfd_struct *) make32(rfds->next); + } + printk("\nerrs: %04x %04x stat: %04x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->status); + printk("\nerrs: %04x %04x rus: %02x, cus: %02x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->rus,(int)p->scb->cus); + } + old_at_least = at_least_one; +#endif + + if(debuglevel > 0) + printk("r"); +} + +/********************************************************** + * handle 'Receiver went not ready'. + */ + +static void sun3_82586_rnr_int(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + dev->stats.rx_errors++; + + WAIT_4_SCB_CMD(); /* wait for the last cmd, WAIT_4_FULLSTAT?? */ + p->scb->cmd_ruc = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */ + sun3_attn586(); + WAIT_4_SCB_CMD_RUC(); /* wait for accept cmd. */ + + alloc_rfa(dev,(char *)p->rfd_first); +/* maybe add a check here, before restarting the RU */ + startrecv586(dev); /* restart RU */ + + printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->rus); + +} + +/********************************************************** + * handle xmit - interrupt + */ + +static void sun3_82586_xmt_int(struct net_device *dev) +{ + int status; + struct priv *p = netdev_priv(dev); + + if(debuglevel > 0) + printk("X"); + + status = swab16(p->xmit_cmds[p->xmit_last]->cmd_status); + if(!(status & STAT_COMPL)) + printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name); + + if(status & STAT_OK) + { + dev->stats.tx_packets++; + dev->stats.collisions += (status & TCMD_MAXCOLLMASK); + } + else + { + dev->stats.tx_errors++; + if(status & TCMD_LATECOLL) { + printk("%s: late collision detected.\n",dev->name); + dev->stats.collisions++; + } + else if(status & TCMD_NOCARRIER) { + dev->stats.tx_carrier_errors++; + printk("%s: no carrier detected.\n",dev->name); + } + else if(status & TCMD_LOSTCTS) + printk("%s: loss of CTS detected.\n",dev->name); + else if(status & TCMD_UNDERRUN) { + dev->stats.tx_fifo_errors++; + printk("%s: DMA underrun detected.\n",dev->name); + } + else if(status & TCMD_MAXCOLL) { + printk("%s: Max. collisions exceeded.\n",dev->name); + dev->stats.collisions += 16; + } + } + +#if (NUM_XMIT_BUFFS > 1) + if( (++p->xmit_last) == NUM_XMIT_BUFFS) + p->xmit_last = 0; +#endif + netif_wake_queue(dev); +} + +/*********************************************************** + * (re)start the receiver + */ + +static void startrecv586(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + + WAIT_4_SCB_CMD(); + WAIT_4_SCB_CMD_RUC(); + p->scb->rfa_offset = make16(p->rfd_first); + p->scb->cmd_ruc = RUC_START; + sun3_attn586(); /* start cmd. */ + WAIT_4_SCB_CMD_RUC(); /* wait for accept cmd. (no timeout!!) */ +} + +static void sun3_82586_timeout(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); +#ifndef NO_NOPCOMMANDS + if(p->scb->cus & CU_ACTIVE) /* COMMAND-UNIT active? */ + { + netif_wake_queue(dev); +#ifdef DEBUG + printk("%s: strange ... timeout with CU active?!?\n",dev->name); + printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)swab16(p->xmit_cmds[0]->cmd_status),(int)swab16(p->nop_cmds[0]->cmd_status),(int)swab16(p->nop_cmds[1]->cmd_status),(int)p->nop_point); +#endif + p->scb->cmd_cuc = CUC_ABORT; + sun3_attn586(); + WAIT_4_SCB_CMD(); + p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]); + p->scb->cmd_cuc = CUC_START; + sun3_attn586(); + WAIT_4_SCB_CMD(); + dev->trans_start = jiffies; /* prevent tx timeout */ + return 0; + } +#endif + { +#ifdef DEBUG + printk("%s: xmitter timed out, try to restart! stat: %02x\n",dev->name,p->scb->cus); + printk("%s: command-stats: %04x %04x\n",dev->name,swab16(p->xmit_cmds[0]->cmd_status),swab16(p->xmit_cmds[1]->cmd_status)); + printk("%s: check, whether you set the right interrupt number!\n",dev->name); +#endif + sun3_82586_close(dev); + sun3_82586_open(dev); + } + dev->trans_start = jiffies; /* prevent tx timeout */ +} + +/****************************************************** + * send frame + */ + +static int sun3_82586_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + int len,i; +#ifndef NO_NOPCOMMANDS + int next_nop; +#endif + struct priv *p = netdev_priv(dev); + + if(skb->len > XMIT_BUFF_SIZE) + { + printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %d bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len); + return NETDEV_TX_OK; + } + + netif_stop_queue(dev); + +#if(NUM_XMIT_BUFFS > 1) + if(test_and_set_bit(0,(void *) &p->lock)) { + printk("%s: Queue was locked\n",dev->name); + return NETDEV_TX_BUSY; + } + else +#endif + { + len = skb->len; + if (len < ETH_ZLEN) { + memset((void *)p->xmit_cbuffs[p->xmit_count], 0, + ETH_ZLEN); + len = ETH_ZLEN; + } + skb_copy_from_linear_data(skb, (void *)p->xmit_cbuffs[p->xmit_count], skb->len); + +#if (NUM_XMIT_BUFFS == 1) +# ifdef NO_NOPCOMMANDS + +#ifdef DEBUG + if(p->scb->cus & CU_ACTIVE) + { + printk("%s: Hmmm .. CU is still running and we wanna send a new packet.\n",dev->name); + printk("%s: stat: %04x %04x\n",dev->name,p->scb->cus,swab16(p->xmit_cmds[0]->cmd_status)); + } +#endif + + p->xmit_buffs[0]->size = swab16(TBD_LAST | len); + for(i=0;i<16;i++) + { + p->xmit_cmds[0]->cmd_status = 0; + WAIT_4_SCB_CMD(); + if( (p->scb->cus & CU_STATUS) == CU_SUSPEND) + p->scb->cmd_cuc = CUC_RESUME; + else + { + p->scb->cbl_offset = make16(p->xmit_cmds[0]); + p->scb->cmd_cuc = CUC_START; + } + + sun3_attn586(); + if(!i) + dev_kfree_skb(skb); + WAIT_4_SCB_CMD(); + if( (p->scb->cus & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */ + break; + if(p->xmit_cmds[0]->cmd_status) + break; + if(i==15) + printk("%s: Can't start transmit-command.\n",dev->name); + } +# else + next_nop = (p->nop_point + 1) & 0x1; + p->xmit_buffs[0]->size = swab16(TBD_LAST | len); + + p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link + = make16((p->nop_cmds[next_nop])); + p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0; + + p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0])); + p->nop_point = next_nop; + dev_kfree_skb(skb); +# endif +#else + p->xmit_buffs[p->xmit_count]->size = swab16(TBD_LAST | len); + if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS ) + next_nop = 0; + + p->xmit_cmds[p->xmit_count]->cmd_status = 0; + /* linkpointer of xmit-command already points to next nop cmd */ + p->nop_cmds[next_nop]->cmd_link = make16((p->nop_cmds[next_nop])); + p->nop_cmds[next_nop]->cmd_status = 0; + + p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count])); + p->xmit_count = next_nop; + + { + unsigned long flags; + local_irq_save(flags); + if(p->xmit_count != p->xmit_last) + netif_wake_queue(dev); + p->lock = 0; + local_irq_restore(flags); + } + dev_kfree_skb(skb); +#endif + } + return NETDEV_TX_OK; +} + +/******************************************* + * Someone wanna have the statistics + */ + +static struct net_device_stats *sun3_82586_get_stats(struct net_device *dev) +{ + struct priv *p = netdev_priv(dev); + unsigned short crc,aln,rsc,ovrn; + + crc = swab16(p->scb->crc_errs); /* get error-statistic from the ni82586 */ + p->scb->crc_errs = 0; + aln = swab16(p->scb->aln_errs); + p->scb->aln_errs = 0; + rsc = swab16(p->scb->rsc_errs); + p->scb->rsc_errs = 0; + ovrn = swab16(p->scb->ovrn_errs); + p->scb->ovrn_errs = 0; + + dev->stats.rx_crc_errors += crc; + dev->stats.rx_fifo_errors += ovrn; + dev->stats.rx_frame_errors += aln; + dev->stats.rx_dropped += rsc; + + return &dev->stats; +} + +/******************************************************** + * Set MC list .. + */ + +static void set_multicast_list(struct net_device *dev) +{ + netif_stop_queue(dev); + sun3_disint(); + alloc586(dev); + init586(dev); + startrecv586(dev); + sun3_enaint(); + netif_wake_queue(dev); +} + +#ifdef MODULE +#error This code is not currently supported as a module +static struct net_device *dev_sun3_82586; + +int init_module(void) +{ + dev_sun3_82586 = sun3_82586_probe(-1); + if (IS_ERR(dev_sun3_82586)) + return PTR_ERR(dev_sun3_82586); + return 0; +} + +void cleanup_module(void) +{ + unsigned long ioaddr = dev_sun3_82586->base_addr; + unregister_netdev(dev_sun3_82586); + release_region(ioaddr, SUN3_82586_TOTAL_SIZE); + iounmap((void *)ioaddr); + free_netdev(dev_sun3_82586); +} +#endif /* MODULE */ + +#if 0 +/* + * DUMP .. we expect a not running CMD unit and enough space + */ +void sun3_82586_dump(struct net_device *dev,void *ptr) +{ + struct priv *p = netdev_priv(dev); + struct dump_cmd_struct *dump_cmd = (struct dump_cmd_struct *) ptr; + int i; + + p->scb->cmd_cuc = CUC_ABORT; + sun3_attn586(); + WAIT_4_SCB_CMD(); + WAIT_4_SCB_CMD_RUC(); + + dump_cmd->cmd_status = 0; + dump_cmd->cmd_cmd = CMD_DUMP | CMD_LAST; + dump_cmd->dump_offset = make16((dump_cmd + 1)); + dump_cmd->cmd_link = 0xffff; + + p->scb->cbl_offset = make16(dump_cmd); + p->scb->cmd_cuc = CUC_START; + sun3_attn586(); + WAIT_4_STAT_COMPL(dump_cmd); + + if( (dump_cmd->cmd_status & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) ) + printk("%s: Can't get dump information.\n",dev->name); + + for(i=0;i<170;i++) { + printk("%02x ",(int) ((unsigned char *) (dump_cmd + 1))[i]); + if(i % 24 == 23) + printk("\n"); + } + printk("\n"); +} +#endif + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/i825xx/sun3_82586.h b/drivers/net/ethernet/i825xx/sun3_82586.h new file mode 100644 index 000000000000..93346f00486b --- /dev/null +++ b/drivers/net/ethernet/i825xx/sun3_82586.h @@ -0,0 +1,318 @@ +/* + * Intel i82586 Ethernet definitions + * + * This is an extension to the Linux operating system, and is covered by the + * same Gnu Public License that covers that work. + * + * copyrights (c) 1994 by Michael Hipp (hippm@informatik.uni-tuebingen.de) + * + * I have done a look in the following sources: + * crynwr-packet-driver by Russ Nelson + * Garret A. Wollman's i82586-driver for BSD + */ + +/* + * Cloned from ni52.h, copyright as above. + * + * Modified for Sun3 OBIO i82586 by Sam Creasey (sammy@sammy.net) + */ + + +/* defines for the obio chip (not vme) */ +#define IEOB_NORSET 0x80 /* don't reset the board */ +#define IEOB_ONAIR 0x40 /* put us on the air */ +#define IEOB_ATTEN 0x20 /* attention! */ +#define IEOB_IENAB 0x10 /* interrupt enable */ +#define IEOB_XXXXX 0x08 /* free bit */ +#define IEOB_XCVRL2 0x04 /* level 2 transceiver? */ +#define IEOB_BUSERR 0x02 /* bus error */ +#define IEOB_INT 0x01 /* interrupt */ + +/* where the obio one lives */ +#define IE_OBIO 0xc0000 +#define IE_IRQ 3 + +/* + * where to find the System Configuration Pointer (SCP) + */ +#define SCP_DEFAULT_ADDRESS 0xfffff4 + + +/* + * System Configuration Pointer Struct + */ + +struct scp_struct +{ + unsigned short zero_dum0; /* has to be zero */ + unsigned char sysbus; /* 0=16Bit,1=8Bit */ + unsigned char zero_dum1; /* has to be zero for 586 */ + unsigned short zero_dum2; + unsigned short zero_dum3; + char *iscp; /* pointer to the iscp-block */ +}; + + +/* + * Intermediate System Configuration Pointer (ISCP) + */ +struct iscp_struct +{ + unsigned char busy; /* 586 clears after successful init */ + unsigned char zero_dummy; /* has to be zero */ + unsigned short scb_offset; /* pointeroffset to the scb_base */ + char *scb_base; /* base-address of all 16-bit offsets */ +}; + +/* + * System Control Block (SCB) + */ +struct scb_struct +{ + unsigned char rus; + unsigned char cus; + unsigned char cmd_ruc; /* command word: RU part */ + unsigned char cmd_cuc; /* command word: CU part & ACK */ + unsigned short cbl_offset; /* pointeroffset, command block list */ + unsigned short rfa_offset; /* pointeroffset, receive frame area */ + unsigned short crc_errs; /* CRC-Error counter */ + unsigned short aln_errs; /* allignmenterror counter */ + unsigned short rsc_errs; /* Resourceerror counter */ + unsigned short ovrn_errs; /* OVerrunerror counter */ +}; + +/* + * possible command values for the command word + */ +#define RUC_MASK 0x0070 /* mask for RU commands */ +#define RUC_NOP 0x0000 /* NOP-command */ +#define RUC_START 0x0010 /* start RU */ +#define RUC_RESUME 0x0020 /* resume RU after suspend */ +#define RUC_SUSPEND 0x0030 /* suspend RU */ +#define RUC_ABORT 0x0040 /* abort receiver operation immediately */ + +#define CUC_MASK 0x07 /* mask for CU command */ +#define CUC_NOP 0x00 /* NOP-command */ +#define CUC_START 0x01 /* start execution of 1. cmd on the CBL */ +#define CUC_RESUME 0x02 /* resume after suspend */ +#define CUC_SUSPEND 0x03 /* Suspend CU */ +#define CUC_ABORT 0x04 /* abort command operation immediately */ + +#define ACK_MASK 0xf0 /* mask for ACK command */ +#define ACK_CX 0x80 /* acknowledges STAT_CX */ +#define ACK_FR 0x40 /* ack. STAT_FR */ +#define ACK_CNA 0x20 /* ack. STAT_CNA */ +#define ACK_RNR 0x10 /* ack. STAT_RNR */ + +/* + * possible status values for the status word + */ +#define STAT_MASK 0xf0 /* mask for cause of interrupt */ +#define STAT_CX 0x80 /* CU finished cmd with its I bit set */ +#define STAT_FR 0x40 /* RU finished receiving a frame */ +#define STAT_CNA 0x20 /* CU left active state */ +#define STAT_RNR 0x10 /* RU left ready state */ + +#define CU_STATUS 0x7 /* CU status, 0=idle */ +#define CU_SUSPEND 0x1 /* CU is suspended */ +#define CU_ACTIVE 0x2 /* CU is active */ + +#define RU_STATUS 0x70 /* RU status, 0=idle */ +#define RU_SUSPEND 0x10 /* RU suspended */ +#define RU_NOSPACE 0x20 /* RU no resources */ +#define RU_READY 0x40 /* RU is ready */ + +/* + * Receive Frame Descriptor (RFD) + */ +struct rfd_struct +{ + unsigned char stat_low; /* status word */ + unsigned char stat_high; /* status word */ + unsigned char rfd_sf; /* 82596 mode only */ + unsigned char last; /* Bit15,Last Frame on List / Bit14,suspend */ + unsigned short next; /* linkoffset to next RFD */ + unsigned short rbd_offset; /* pointeroffset to RBD-buffer */ + unsigned char dest[6]; /* ethernet-address, destination */ + unsigned char source[6]; /* ethernet-address, source */ + unsigned short length; /* 802.3 frame-length */ + unsigned short zero_dummy; /* dummy */ +}; + +#define RFD_LAST 0x80 /* last: last rfd in the list */ +#define RFD_SUSP 0x40 /* last: suspend RU after */ +#define RFD_COMPL 0x80 +#define RFD_OK 0x20 +#define RFD_BUSY 0x40 +#define RFD_ERR_LEN 0x10 /* Length error (if enabled length-checking */ +#define RFD_ERR_CRC 0x08 /* CRC error */ +#define RFD_ERR_ALGN 0x04 /* Alignment error */ +#define RFD_ERR_RNR 0x02 /* status: receiver out of resources */ +#define RFD_ERR_OVR 0x01 /* DMA Overrun! */ + +#define RFD_ERR_FTS 0x0080 /* Frame to short */ +#define RFD_ERR_NEOP 0x0040 /* No EOP flag (for bitstuffing only) */ +#define RFD_ERR_TRUN 0x0020 /* (82596 only/SF mode) indicates truncated frame */ +#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA (only 82596) */ +#define RFD_COLLDET 0x0001 /* Detected collision during reception */ + +/* + * Receive Buffer Descriptor (RBD) + */ +struct rbd_struct +{ + unsigned short status; /* status word,number of used bytes in buff */ + unsigned short next; /* pointeroffset to next RBD */ + char *buffer; /* receive buffer address pointer */ + unsigned short size; /* size of this buffer */ + unsigned short zero_dummy; /* dummy */ +}; + +#define RBD_LAST 0x8000 /* last buffer */ +#define RBD_USED 0x4000 /* this buffer has data */ +#define RBD_MASK 0x3fff /* size-mask for length */ + +/* + * Statusvalues for Commands/RFD + */ +#define STAT_COMPL 0x8000 /* status: frame/command is complete */ +#define STAT_BUSY 0x4000 /* status: frame/command is busy */ +#define STAT_OK 0x2000 /* status: frame/command is ok */ + +/* + * Action-Commands + */ +#define CMD_NOP 0x0000 /* NOP */ +#define CMD_IASETUP 0x0001 /* initial address setup command */ +#define CMD_CONFIGURE 0x0002 /* configure command */ +#define CMD_MCSETUP 0x0003 /* MC setup command */ +#define CMD_XMIT 0x0004 /* transmit command */ +#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */ +#define CMD_DUMP 0x0006 /* dump command */ +#define CMD_DIAGNOSE 0x0007 /* diagnose command */ + +/* + * Action command bits + */ +#define CMD_LAST 0x8000 /* indicates last command in the CBL */ +#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */ +#define CMD_INT 0x2000 /* generate interrupt after execution */ + +/* + * NOP - command + */ +struct nop_cmd_struct +{ + unsigned short cmd_status; /* status of this command */ + unsigned short cmd_cmd; /* the command itself (+bits) */ + unsigned short cmd_link; /* offsetpointer to next command */ +}; + +/* + * IA Setup command + */ +struct iasetup_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned char iaddr[6]; +}; + +/* + * Configure command + */ +struct configure_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned char byte_cnt; /* size of the config-cmd */ + unsigned char fifo; /* fifo/recv monitor */ + unsigned char sav_bf; /* save bad frames (bit7=1)*/ + unsigned char adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/ + unsigned char priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */ + unsigned char ifs; /* inter frame spacing */ + unsigned char time_low; /* slot time low */ + unsigned char time_high; /* slot time high(0-2) and max. retries(4-7) */ + unsigned char promisc; /* promisc-mode(0) , et al (1-7) */ + unsigned char carr_coll; /* carrier(0-3)/collision(4-7) stuff */ + unsigned char fram_len; /* minimal frame len */ + unsigned char dummy; /* dummy */ +}; + +/* + * Multicast Setup command + */ +struct mcsetup_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short mc_cnt; /* number of bytes in the MC-List */ + unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */ +}; + +/* + * DUMP command + */ +struct dump_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short dump_offset; /* pointeroffset to DUMP space */ +}; + +/* + * transmit command + */ +struct transmit_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short tbd_offset; /* pointeroffset to TBD */ + unsigned char dest[6]; /* destination address of the frame */ + unsigned short length; /* user defined: 802.3 length / Ether type */ +}; + +#define TCMD_ERRMASK 0x0fa0 +#define TCMD_MAXCOLLMASK 0x000f +#define TCMD_MAXCOLL 0x0020 +#define TCMD_HEARTBEAT 0x0040 +#define TCMD_DEFERRED 0x0080 +#define TCMD_UNDERRUN 0x0100 +#define TCMD_LOSTCTS 0x0200 +#define TCMD_NOCARRIER 0x0400 +#define TCMD_LATECOLL 0x0800 + +struct tdr_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short status; +}; + +#define TDR_LNK_OK 0x8000 /* No link problem identified */ +#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */ +#define TDR_ET_OPN 0x2000 /* open, no correct termination */ +#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */ +#define TDR_TIMEMASK 0x07ff /* mask for the time field */ + +/* + * Transmit Buffer Descriptor (TBD) + */ +struct tbd_struct +{ + unsigned short size; /* size + EOF-Flag(15) */ + unsigned short next; /* pointeroffset to next TBD */ + char *buffer; /* pointer to buffer */ +}; + +#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */ + + + + diff --git a/drivers/net/ethernet/i825xx/znet.c b/drivers/net/ethernet/i825xx/znet.c new file mode 100644 index 000000000000..8b8881718f5e --- /dev/null +++ b/drivers/net/ethernet/i825xx/znet.c @@ -0,0 +1,924 @@ +/* znet.c: An Zenith Z-Note ethernet driver for linux. */ + +/* + Written by Donald Becker. + + The author may be reached as becker@scyld.com. + This driver is based on the Linux skeleton driver. The copyright of the + skeleton driver is held by the United States Government, as represented + by DIRNSA, and it is released under the GPL. + + Thanks to Mike Hollick for alpha testing and suggestions. + + References: + The Crynwr packet driver. + + "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992 + Intel Microcommunications Databook, Vol. 1, 1990. + As usual with Intel, the documentation is incomplete and inaccurate. + I had to read the Crynwr packet driver to figure out how to actually + use the i82593, and guess at what register bits matched the loosely + related i82586. + + Theory of Operation + + The i82593 used in the Zenith Z-Note series operates using two(!) slave + DMA channels, one interrupt, and one 8-bit I/O port. + + While there several ways to configure '593 DMA system, I chose the one + that seemed commensurate with the highest system performance in the face + of moderate interrupt latency: Both DMA channels are configured as + recirculating ring buffers, with one channel (#0) dedicated to Rx and + the other channel (#1) to Tx and configuration. (Note that this is + different than the Crynwr driver, where the Tx DMA channel is initialized + before each operation. That approach simplifies operation and Tx error + recovery, but requires additional I/O in normal operation and precludes + transmit buffer chaining.) + + Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE. This provides + a reasonable ring size for Rx, while simplifying DMA buffer allocation -- + DMA buffers must not cross a 128K boundary. (In truth the size selection + was influenced by my lack of '593 documentation. I thus was constrained + to use the Crynwr '593 initialization table, which sets the Rx ring size + to 8K.) + + Despite my usual low opinion about Intel-designed parts, I must admit + that the bulk data handling of the i82593 is a good design for + an integrated system, like a laptop, where using two slave DMA channels + doesn't pose a problem. I still take issue with using only a single I/O + port. In the same controlled environment there are essentially no + limitations on I/O space, and using multiple locations would eliminate + the need for multiple operations when looking at status registers, + setting the Rx ring boundary, or switching to promiscuous mode. + + I also question Zenith's selection of the '593: one of the advertised + advantages of earlier Intel parts was that if you figured out the magic + initialization incantation you could use the same part on many different + network types. Zenith's use of the "FriendlyNet" (sic) connector rather + than an on-board transceiver leads me to believe that they were planning + to take advantage of this. But, uhmmm, the '593 omits all but ethernet + functionality from the serial subsystem. + */ + +/* 10/2002 + + o Resurected for Linux 2.5+ by Marc Zyngier <maz@wild-wind.fr.eu.org> : + + - Removed strange DMA snooping in znet_sent_packet, which lead to + TX buffer corruption on my laptop. + - Use init_etherdev stuff. + - Use kmalloc-ed DMA buffers. + - Use as few global variables as possible. + - Use proper resources management. + - Use wireless/i82593.h as much as possible (structure, constants) + - Compiles as module or build-in. + - Now survives unplugging/replugging cable. + + Some code was taken from wavelan_cs. + + Tested on a vintage Zenith Z-Note 433Lnp+. Probably broken on + anything else. Testers (and detailed bug reports) are welcome :-). + + o TODO : + + - Properly handle multicast + - Understand why some traffic patterns add a 1s latency... + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/bitops.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/i82593.h> + +static char version[] __initdata = "znet.c:v1.02 9/23/94 becker@scyld.com\n"; + +#ifndef ZNET_DEBUG +#define ZNET_DEBUG 1 +#endif +static unsigned int znet_debug = ZNET_DEBUG; +module_param (znet_debug, int, 0); +MODULE_PARM_DESC (znet_debug, "ZNet debug level"); +MODULE_LICENSE("GPL"); + +/* The DMA modes we need aren't in <dma.h>. */ +#define DMA_RX_MODE 0x14 /* Auto init, I/O to mem, ++, demand. */ +#define DMA_TX_MODE 0x18 /* Auto init, Mem to I/O, ++, demand. */ +#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17) +#define RX_BUF_SIZE 8192 +#define TX_BUF_SIZE 8192 +#define DMA_BUF_SIZE (RX_BUF_SIZE + 16) /* 8k + 16 bytes for trailers */ + +#define TX_TIMEOUT (HZ/10) + +struct znet_private { + int rx_dma, tx_dma; + spinlock_t lock; + short sia_base, sia_size, io_size; + struct i82593_conf_block i593_init; + /* The starting, current, and end pointers for the packet buffers. */ + ushort *rx_start, *rx_cur, *rx_end; + ushort *tx_start, *tx_cur, *tx_end; + ushort tx_buf_len; /* Tx buffer length, in words. */ +}; + +/* Only one can be built-in;-> */ +static struct net_device *znet_dev; + +struct netidblk { + char magic[8]; /* The magic number (string) "NETIDBLK" */ + unsigned char netid[8]; /* The physical station address */ + char nettype, globalopt; + char vendor[8]; /* The machine vendor and product name. */ + char product[8]; + char irq1, irq2; /* Interrupts, only one is currently used. */ + char dma1, dma2; + short dma_mem_misc[8]; /* DMA buffer locations (unused in Linux). */ + short iobase1, iosize1; + short iobase2, iosize2; /* Second iobase unused. */ + char driver_options; /* Misc. bits */ + char pad; +}; + +static int znet_open(struct net_device *dev); +static netdev_tx_t znet_send_packet(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t znet_interrupt(int irq, void *dev_id); +static void znet_rx(struct net_device *dev); +static int znet_close(struct net_device *dev); +static void hardware_init(struct net_device *dev); +static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset); +static void znet_tx_timeout (struct net_device *dev); + +/* Request needed resources */ +static int znet_request_resources (struct net_device *dev) +{ + struct znet_private *znet = netdev_priv(dev); + + if (request_irq (dev->irq, znet_interrupt, 0, "ZNet", dev)) + goto failed; + if (request_dma (znet->rx_dma, "ZNet rx")) + goto free_irq; + if (request_dma (znet->tx_dma, "ZNet tx")) + goto free_rx_dma; + if (!request_region (znet->sia_base, znet->sia_size, "ZNet SIA")) + goto free_tx_dma; + if (!request_region (dev->base_addr, znet->io_size, "ZNet I/O")) + goto free_sia; + + return 0; /* Happy ! */ + + free_sia: + release_region (znet->sia_base, znet->sia_size); + free_tx_dma: + free_dma (znet->tx_dma); + free_rx_dma: + free_dma (znet->rx_dma); + free_irq: + free_irq (dev->irq, dev); + failed: + return -1; +} + +static void znet_release_resources (struct net_device *dev) +{ + struct znet_private *znet = netdev_priv(dev); + + release_region (znet->sia_base, znet->sia_size); + release_region (dev->base_addr, znet->io_size); + free_dma (znet->tx_dma); + free_dma (znet->rx_dma); + free_irq (dev->irq, dev); +} + +/* Keep the magical SIA stuff in a single function... */ +static void znet_transceiver_power (struct net_device *dev, int on) +{ + struct znet_private *znet = netdev_priv(dev); + unsigned char v; + + /* Turn on/off the 82501 SIA, using zenith-specific magic. */ + /* Select LAN control register */ + outb(0x10, znet->sia_base); + + if (on) + v = inb(znet->sia_base + 1) | 0x84; + else + v = inb(znet->sia_base + 1) & ~0x84; + + outb(v, znet->sia_base+1); /* Turn on/off LAN power (bit 2). */ +} + +/* Init the i82593, with current promisc/mcast configuration. + Also used from hardware_init. */ +static void znet_set_multicast_list (struct net_device *dev) +{ + struct znet_private *znet = netdev_priv(dev); + short ioaddr = dev->base_addr; + struct i82593_conf_block *cfblk = &znet->i593_init; + + memset(cfblk, 0x00, sizeof(struct i82593_conf_block)); + + /* The configuration block. What an undocumented nightmare. + The first set of values are those suggested (without explanation) + for ethernet in the Intel 82586 databook. The rest appear to be + completely undocumented, except for cryptic notes in the Crynwr + packet driver. This driver uses the Crynwr values verbatim. */ + + /* maz : Rewritten to take advantage of the wanvelan includes. + At least we have names, not just blind values */ + + /* Byte 0 */ + cfblk->fifo_limit = 10; /* = 16 B rx and 80 B tx fifo thresholds */ + cfblk->forgnesi = 0; /* 0=82C501, 1=AMD7992B compatibility */ + cfblk->fifo_32 = 1; + cfblk->d6mod = 0; /* Run in i82593 advanced mode */ + cfblk->throttle_enb = 1; + + /* Byte 1 */ + cfblk->throttle = 8; /* Continuous w/interrupts, 128-clock DMA. */ + cfblk->cntrxint = 0; /* enable continuous mode receive interrupts */ + cfblk->contin = 1; /* enable continuous mode */ + + /* Byte 2 */ + cfblk->addr_len = ETH_ALEN; + cfblk->acloc = 1; /* Disable source addr insertion by i82593 */ + cfblk->preamb_len = 2; /* 8 bytes preamble */ + cfblk->loopback = 0; /* Loopback off */ + + /* Byte 3 */ + cfblk->lin_prio = 0; /* Default priorities & backoff methods. */ + cfblk->tbofstop = 0; + cfblk->exp_prio = 0; + cfblk->bof_met = 0; + + /* Byte 4 */ + cfblk->ifrm_spc = 6; /* 96 bit times interframe spacing */ + + /* Byte 5 */ + cfblk->slottim_low = 0; /* 512 bit times slot time (low) */ + + /* Byte 6 */ + cfblk->slottim_hi = 2; /* 512 bit times slot time (high) */ + cfblk->max_retr = 15; /* 15 collisions retries */ + + /* Byte 7 */ + cfblk->prmisc = ((dev->flags & IFF_PROMISC) ? 1 : 0); /* Promiscuous mode */ + cfblk->bc_dis = 0; /* Enable broadcast reception */ + cfblk->crs_1 = 0; /* Don't transmit without carrier sense */ + cfblk->nocrc_ins = 0; /* i82593 generates CRC */ + cfblk->crc_1632 = 0; /* 32-bit Autodin-II CRC */ + cfblk->crs_cdt = 0; /* CD not to be interpreted as CS */ + + /* Byte 8 */ + cfblk->cs_filter = 0; /* CS is recognized immediately */ + cfblk->crs_src = 0; /* External carrier sense */ + cfblk->cd_filter = 0; /* CD is recognized immediately */ + + /* Byte 9 */ + cfblk->min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length */ + + /* Byte A */ + cfblk->lng_typ = 1; /* Type/length checks OFF */ + cfblk->lng_fld = 1; /* Disable 802.3 length field check */ + cfblk->rxcrc_xf = 1; /* Don't transfer CRC to memory */ + cfblk->artx = 1; /* Disable automatic retransmission */ + cfblk->sarec = 1; /* Disable source addr trig of CD */ + cfblk->tx_jabber = 0; /* Disable jabber jam sequence */ + cfblk->hash_1 = 1; /* Use bits 0-5 in mc address hash */ + cfblk->lbpkpol = 0; /* Loopback pin active high */ + + /* Byte B */ + cfblk->fdx = 0; /* Disable full duplex operation */ + + /* Byte C */ + cfblk->dummy_6 = 0x3f; /* all ones, Default multicast addresses & backoff. */ + cfblk->mult_ia = 0; /* No multiple individual addresses */ + cfblk->dis_bof = 0; /* Disable the backoff algorithm ?! */ + + /* Byte D */ + cfblk->dummy_1 = 1; /* set to 1 */ + cfblk->tx_ifs_retrig = 3; /* Hmm... Disabled */ + cfblk->mc_all = (!netdev_mc_empty(dev) || + (dev->flags & IFF_ALLMULTI)); /* multicast all mode */ + cfblk->rcv_mon = 0; /* Monitor mode disabled */ + cfblk->frag_acpt = 0; /* Do not accept fragments */ + cfblk->tstrttrs = 0; /* No start transmission threshold */ + + /* Byte E */ + cfblk->fretx = 1; /* FIFO automatic retransmission */ + cfblk->runt_eop = 0; /* drop "runt" packets */ + cfblk->hw_sw_pin = 0; /* ?? */ + cfblk->big_endn = 0; /* Big Endian ? no... */ + cfblk->syncrqs = 1; /* Synchronous DRQ deassertion... */ + cfblk->sttlen = 1; /* 6 byte status registers */ + cfblk->rx_eop = 0; /* Signal EOP on packet reception */ + cfblk->tx_eop = 0; /* Signal EOP on packet transmission */ + + /* Byte F */ + cfblk->rbuf_size = RX_BUF_SIZE >> 12; /* Set receive buffer size */ + cfblk->rcvstop = 1; /* Enable Receive Stop Register */ + + if (znet_debug > 2) { + int i; + unsigned char *c; + + for (i = 0, c = (char *) cfblk; i < sizeof (*cfblk); i++) + printk ("%02X ", c[i]); + printk ("\n"); + } + + *znet->tx_cur++ = sizeof(struct i82593_conf_block); + memcpy(znet->tx_cur, cfblk, sizeof(struct i82593_conf_block)); + znet->tx_cur += sizeof(struct i82593_conf_block)/2; + outb(OP0_CONFIGURE | CR0_CHNL, ioaddr); + + /* XXX FIXME maz : Add multicast addresses here, so having a + * multicast address configured isn't equal to IFF_ALLMULTI */ +} + +static const struct net_device_ops znet_netdev_ops = { + .ndo_open = znet_open, + .ndo_stop = znet_close, + .ndo_start_xmit = znet_send_packet, + .ndo_set_multicast_list = znet_set_multicast_list, + .ndo_tx_timeout = znet_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +/* The Z-Note probe is pretty easy. The NETIDBLK exists in the safe-to-probe + BIOS area. We just scan for the signature, and pull the vital parameters + out of the structure. */ + +static int __init znet_probe (void) +{ + int i; + struct netidblk *netinfo; + struct znet_private *znet; + struct net_device *dev; + char *p; + int err = -ENOMEM; + + /* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */ + for(p = (char *)phys_to_virt(0xf0000); p < (char *)phys_to_virt(0x100000); p++) + if (*p == 'N' && strncmp(p, "NETIDBLK", 8) == 0) + break; + + if (p >= (char *)phys_to_virt(0x100000)) { + if (znet_debug > 1) + printk(KERN_INFO "No Z-Note ethernet adaptor found.\n"); + return -ENODEV; + } + + dev = alloc_etherdev(sizeof(struct znet_private)); + if (!dev) + return -ENOMEM; + + znet = netdev_priv(dev); + + netinfo = (struct netidblk *)p; + dev->base_addr = netinfo->iobase1; + dev->irq = netinfo->irq1; + + /* The station address is in the "netidblk" at 0x0f0000. */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = netinfo->netid[i]; + + printk(KERN_INFO "%s: ZNET at %#3lx, %pM" + ", using IRQ %d DMA %d and %d.\n", + dev->name, dev->base_addr, dev->dev_addr, + dev->irq, netinfo->dma1, netinfo->dma2); + + if (znet_debug > 1) { + printk(KERN_INFO "%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n", + dev->name, netinfo->vendor, + netinfo->irq1, netinfo->irq2, + netinfo->dma1, netinfo->dma2); + printk(KERN_INFO "%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n", + dev->name, netinfo->iobase1, netinfo->iosize1, + netinfo->iobase2, netinfo->iosize2, netinfo->nettype); + } + + if (znet_debug > 0) + printk(KERN_INFO "%s", version); + + znet->rx_dma = netinfo->dma1; + znet->tx_dma = netinfo->dma2; + spin_lock_init(&znet->lock); + znet->sia_base = 0xe6; /* Magic address for the 82501 SIA */ + znet->sia_size = 2; + /* maz: Despite the '593 being advertised above as using a + * single 8bits I/O port, this driver does many 16bits + * access. So set io_size accordingly */ + znet->io_size = 2; + + if (!(znet->rx_start = kmalloc (DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA))) + goto free_dev; + if (!(znet->tx_start = kmalloc (DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA))) + goto free_rx; + + if (!dma_page_eq (znet->rx_start, znet->rx_start + (RX_BUF_SIZE/2-1)) || + !dma_page_eq (znet->tx_start, znet->tx_start + (TX_BUF_SIZE/2-1))) { + printk (KERN_WARNING "tx/rx crossing DMA frontiers, giving up\n"); + goto free_tx; + } + + znet->rx_end = znet->rx_start + RX_BUF_SIZE/2; + znet->tx_buf_len = TX_BUF_SIZE/2; + znet->tx_end = znet->tx_start + znet->tx_buf_len; + + /* The ZNET-specific entries in the device structure. */ + dev->netdev_ops = &znet_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + err = register_netdev(dev); + if (err) + goto free_tx; + znet_dev = dev; + return 0; + + free_tx: + kfree(znet->tx_start); + free_rx: + kfree(znet->rx_start); + free_dev: + free_netdev(dev); + return err; +} + + +static int znet_open(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + + if (znet_debug > 2) + printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name); + + /* These should never fail. You can't add devices to a sealed box! */ + if (znet_request_resources (dev)) { + printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name); + return -EBUSY; + } + + znet_transceiver_power (dev, 1); + + /* According to the Crynwr driver we should wait 50 msec. for the + LAN clock to stabilize. My experiments indicates that the '593 can + be initialized immediately. The delay is probably needed for the + DC-to-DC converter to come up to full voltage, and for the oscillator + to be spot-on at 20Mhz before transmitting. + Until this proves to be a problem we rely on the higher layers for the + delay and save allocating a timer entry. */ + + /* maz : Well, I'm getting every time the following message + * without the delay on a 486@33. This machine is much too + * fast... :-) So maybe the Crynwr driver wasn't wrong after + * all, even if the message is completly harmless on my + * setup. */ + mdelay (50); + + /* This follows the packet driver's lead, and checks for success. */ + if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00) + printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n", + dev->name); + + hardware_init(dev); + netif_start_queue (dev); + + return 0; +} + + +static void znet_tx_timeout (struct net_device *dev) +{ + int ioaddr = dev->base_addr; + ushort event, tx_status, rx_offset, state; + + outb (CR0_STATUS_0, ioaddr); + event = inb (ioaddr); + outb (CR0_STATUS_1, ioaddr); + tx_status = inw (ioaddr); + outb (CR0_STATUS_2, ioaddr); + rx_offset = inw (ioaddr); + outb (CR0_STATUS_3, ioaddr); + state = inb (ioaddr); + printk (KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x," + " resetting.\n", dev->name, event, tx_status, rx_offset, state); + if (tx_status == TX_LOST_CRS) + printk (KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n", + dev->name); + outb (OP0_RESET, ioaddr); + hardware_init (dev); + netif_wake_queue (dev); +} + +static netdev_tx_t znet_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct znet_private *znet = netdev_priv(dev); + unsigned long flags; + short length = skb->len; + + if (znet_debug > 4) + printk(KERN_DEBUG "%s: ZNet_send_packet.\n", dev->name); + + if (length < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + length = ETH_ZLEN; + } + + netif_stop_queue (dev); + + /* Check that the part hasn't reset itself, probably from suspend. */ + outb(CR0_STATUS_0, ioaddr); + if (inw(ioaddr) == 0x0010 && + inw(ioaddr) == 0x0000 && + inw(ioaddr) == 0x0010) { + if (znet_debug > 1) + printk (KERN_WARNING "%s : waking up\n", dev->name); + hardware_init(dev); + znet_transceiver_power (dev, 1); + } + + if (1) { + unsigned char *buf = (void *)skb->data; + ushort *tx_link = znet->tx_cur - 1; + ushort rnd_len = (length + 1)>>1; + + dev->stats.tx_bytes+=length; + + if (znet->tx_cur >= znet->tx_end) + znet->tx_cur = znet->tx_start; + *znet->tx_cur++ = length; + if (znet->tx_cur + rnd_len + 1 > znet->tx_end) { + int semi_cnt = (znet->tx_end - znet->tx_cur)<<1; /* Cvrt to byte cnt. */ + memcpy(znet->tx_cur, buf, semi_cnt); + rnd_len -= semi_cnt>>1; + memcpy(znet->tx_start, buf + semi_cnt, length - semi_cnt); + znet->tx_cur = znet->tx_start + rnd_len; + } else { + memcpy(znet->tx_cur, buf, skb->len); + znet->tx_cur += rnd_len; + } + *znet->tx_cur++ = 0; + + spin_lock_irqsave(&znet->lock, flags); + { + *tx_link = OP0_TRANSMIT | CR0_CHNL; + /* Is this always safe to do? */ + outb(OP0_TRANSMIT | CR0_CHNL, ioaddr); + } + spin_unlock_irqrestore (&znet->lock, flags); + + netif_start_queue (dev); + + if (znet_debug > 4) + printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length); + } + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +/* The ZNET interrupt handler. */ +static irqreturn_t znet_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct znet_private *znet = netdev_priv(dev); + int ioaddr; + int boguscnt = 20; + int handled = 0; + + spin_lock (&znet->lock); + + ioaddr = dev->base_addr; + + outb(CR0_STATUS_0, ioaddr); + do { + ushort status = inb(ioaddr); + if (znet_debug > 5) { + ushort result, rx_ptr, running; + outb(CR0_STATUS_1, ioaddr); + result = inw(ioaddr); + outb(CR0_STATUS_2, ioaddr); + rx_ptr = inw(ioaddr); + outb(CR0_STATUS_3, ioaddr); + running = inb(ioaddr); + printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n", + dev->name, status, result, rx_ptr, running, boguscnt); + } + if ((status & SR0_INTERRUPT) == 0) + break; + + handled = 1; + + if ((status & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE || + (status & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE || + (status & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) { + int tx_status; + outb(CR0_STATUS_1, ioaddr); + tx_status = inw(ioaddr); + /* It's undocumented, but tx_status seems to match the i82586. */ + if (tx_status & TX_OK) { + dev->stats.tx_packets++; + dev->stats.collisions += tx_status & TX_NCOL_MASK; + } else { + if (tx_status & (TX_LOST_CTS | TX_LOST_CRS)) + dev->stats.tx_carrier_errors++; + if (tx_status & TX_UND_RUN) + dev->stats.tx_fifo_errors++; + if (!(tx_status & TX_HRT_BEAT)) + dev->stats.tx_heartbeat_errors++; + if (tx_status & TX_MAX_COL) + dev->stats.tx_aborted_errors++; + /* ...and the catch-all. */ + if ((tx_status | (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL)) != (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL)) + dev->stats.tx_errors++; + + /* Transceiver may be stuck if cable + * was removed while emitting a + * packet. Flip it off, then on to + * reset it. This is very empirical, + * but it seems to work. */ + + znet_transceiver_power (dev, 0); + znet_transceiver_power (dev, 1); + } + netif_wake_queue (dev); + } + + if ((status & SR0_RECEPTION) || + (status & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) { + znet_rx(dev); + } + /* Clear the interrupts we've handled. */ + outb(CR0_INT_ACK, ioaddr); + } while (boguscnt--); + + spin_unlock (&znet->lock); + + return IRQ_RETVAL(handled); +} + +static void znet_rx(struct net_device *dev) +{ + struct znet_private *znet = netdev_priv(dev); + int ioaddr = dev->base_addr; + int boguscount = 1; + short next_frame_end_offset = 0; /* Offset of next frame start. */ + short *cur_frame_end; + short cur_frame_end_offset; + + outb(CR0_STATUS_2, ioaddr); + cur_frame_end_offset = inw(ioaddr); + + if (cur_frame_end_offset == znet->rx_cur - znet->rx_start) { + printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n", + dev->name, cur_frame_end_offset); + return; + } + + /* Use same method as the Crynwr driver: construct a forward list in + the same area of the backwards links we now have. This allows us to + pass packets to the upper layers in the order they were received -- + important for fast-path sequential operations. */ + while (znet->rx_start + cur_frame_end_offset != znet->rx_cur && + ++boguscount < 5) { + unsigned short hi_cnt, lo_cnt, hi_status, lo_status; + int count, status; + + if (cur_frame_end_offset < 4) { + /* Oh no, we have a special case: the frame trailer wraps around + the end of the ring buffer. We've saved space at the end of + the ring buffer for just this problem. */ + memcpy(znet->rx_end, znet->rx_start, 8); + cur_frame_end_offset += (RX_BUF_SIZE/2); + } + cur_frame_end = znet->rx_start + cur_frame_end_offset - 4; + + lo_status = *cur_frame_end++; + hi_status = *cur_frame_end++; + status = ((hi_status & 0xff) << 8) + (lo_status & 0xff); + lo_cnt = *cur_frame_end++; + hi_cnt = *cur_frame_end++; + count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff); + + if (znet_debug > 5) + printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x" + " count %#x status %04x.\n", + cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt, + count, status); + cur_frame_end[-4] = status; + cur_frame_end[-3] = next_frame_end_offset; + cur_frame_end[-2] = count; + next_frame_end_offset = cur_frame_end_offset; + cur_frame_end_offset -= ((count + 1)>>1) + 3; + if (cur_frame_end_offset < 0) + cur_frame_end_offset += RX_BUF_SIZE/2; + } + + /* Now step forward through the list. */ + do { + ushort *this_rfp_ptr = znet->rx_start + next_frame_end_offset; + int status = this_rfp_ptr[-4]; + int pkt_len = this_rfp_ptr[-2]; + + if (znet_debug > 5) + printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x" + " next %04x.\n", next_frame_end_offset<<1, status, pkt_len, + this_rfp_ptr[-3]<<1); + /* Once again we must assume that the i82586 docs apply. */ + if ( ! (status & RX_RCV_OK)) { /* There was an error. */ + dev->stats.rx_errors++; + if (status & RX_CRC_ERR) dev->stats.rx_crc_errors++; + if (status & RX_ALG_ERR) dev->stats.rx_frame_errors++; +#if 0 + if (status & 0x0200) dev->stats.rx_over_errors++; /* Wrong. */ + if (status & 0x0100) dev->stats.rx_fifo_errors++; +#else + /* maz : Wild guess... */ + if (status & RX_OVRRUN) dev->stats.rx_over_errors++; +#endif + if (status & RX_SRT_FRM) dev->stats.rx_length_errors++; + } else if (pkt_len > 1536) { + dev->stats.rx_length_errors++; + } else { + /* Malloc up new buffer. */ + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_len); + if (skb == NULL) { + if (znet_debug) + printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); + dev->stats.rx_dropped++; + break; + } + + if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) { + int semi_cnt = (znet->rx_end - znet->rx_cur)<<1; + memcpy(skb_put(skb,semi_cnt), znet->rx_cur, semi_cnt); + memcpy(skb_put(skb,pkt_len-semi_cnt), znet->rx_start, + pkt_len - semi_cnt); + } else { + memcpy(skb_put(skb,pkt_len), znet->rx_cur, pkt_len); + if (znet_debug > 6) { + unsigned int *packet = (unsigned int *) skb->data; + printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0], + packet[1], packet[2], packet[3]); + } + } + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + znet->rx_cur = this_rfp_ptr; + if (znet->rx_cur >= znet->rx_end) + znet->rx_cur -= RX_BUF_SIZE/2; + update_stop_hit(ioaddr, (znet->rx_cur - znet->rx_start)<<1); + next_frame_end_offset = this_rfp_ptr[-3]; + if (next_frame_end_offset == 0) /* Read all the frames? */ + break; /* Done for now */ + this_rfp_ptr = znet->rx_start + next_frame_end_offset; + } while (--boguscount); + + /* If any worth-while packets have been received, dev_rint() + has done a mark_bh(INET_BH) for us and will work on them + when we get to the bottom-half routine. */ +} + +/* The inverse routine to znet_open(). */ +static int znet_close(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + + netif_stop_queue (dev); + + outb(OP0_RESET, ioaddr); /* CMD0_RESET */ + + if (znet_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); + /* Turn off transceiver power. */ + znet_transceiver_power (dev, 0); + + znet_release_resources (dev); + + return 0; +} + +static void show_dma(struct net_device *dev) +{ + short ioaddr = dev->base_addr; + unsigned char stat = inb (ioaddr); + struct znet_private *znet = netdev_priv(dev); + unsigned long flags; + short dma_port = ((znet->tx_dma&3)<<2) + IO_DMA2_BASE; + unsigned addr = inb(dma_port); + short residue; + + addr |= inb(dma_port) << 8; + residue = get_dma_residue(znet->tx_dma); + + if (znet_debug > 1) { + flags=claim_dma_lock(); + printk(KERN_DEBUG "Stat:%02x Addr: %04x cnt:%3x\n", + stat, addr<<1, residue); + release_dma_lock(flags); + } +} + +/* Initialize the hardware. We have to do this when the board is open()ed + or when we come out of suspend mode. */ +static void hardware_init(struct net_device *dev) +{ + unsigned long flags; + short ioaddr = dev->base_addr; + struct znet_private *znet = netdev_priv(dev); + + znet->rx_cur = znet->rx_start; + znet->tx_cur = znet->tx_start; + + /* Reset the chip, and start it up. */ + outb(OP0_RESET, ioaddr); + + flags=claim_dma_lock(); + disable_dma(znet->rx_dma); /* reset by an interrupting task. */ + clear_dma_ff(znet->rx_dma); + set_dma_mode(znet->rx_dma, DMA_RX_MODE); + set_dma_addr(znet->rx_dma, (unsigned int) znet->rx_start); + set_dma_count(znet->rx_dma, RX_BUF_SIZE); + enable_dma(znet->rx_dma); + /* Now set up the Tx channel. */ + disable_dma(znet->tx_dma); + clear_dma_ff(znet->tx_dma); + set_dma_mode(znet->tx_dma, DMA_TX_MODE); + set_dma_addr(znet->tx_dma, (unsigned int) znet->tx_start); + set_dma_count(znet->tx_dma, znet->tx_buf_len<<1); + enable_dma(znet->tx_dma); + release_dma_lock(flags); + + if (znet_debug > 1) + printk(KERN_DEBUG "%s: Initializing the i82593, rx buf %p tx buf %p\n", + dev->name, znet->rx_start,znet->tx_start); + /* Do an empty configure command, just like the Crynwr driver. This + resets to chip to its default values. */ + *znet->tx_cur++ = 0; + *znet->tx_cur++ = 0; + show_dma(dev); + outb(OP0_CONFIGURE | CR0_CHNL, ioaddr); + + znet_set_multicast_list (dev); + + *znet->tx_cur++ = 6; + memcpy(znet->tx_cur, dev->dev_addr, 6); + znet->tx_cur += 3; + show_dma(dev); + outb(OP0_IA_SETUP | CR0_CHNL, ioaddr); + show_dma(dev); + + update_stop_hit(ioaddr, 8192); + if (znet_debug > 1) printk(KERN_DEBUG "enabling Rx.\n"); + outb(OP0_RCV_ENABLE, ioaddr); + netif_start_queue (dev); +} + +static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset) +{ + outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, ioaddr); + if (znet_debug > 5) + printk(KERN_DEBUG "Updating stop hit with value %02x.\n", + (rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE); + outb((rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE, ioaddr); + outb(OP1_SWIT_TO_PORT_0, ioaddr); +} + +static __exit void znet_cleanup (void) +{ + if (znet_dev) { + struct znet_private *znet = netdev_priv(znet_dev); + + unregister_netdev (znet_dev); + kfree (znet->rx_start); + kfree (znet->tx_start); + free_netdev (znet_dev); + } +} + +module_init (znet_probe); +module_exit (znet_cleanup); |