diff options
Diffstat (limited to 'drivers/net/ethernet/dec')
22 files changed, 23601 insertions, 0 deletions
diff --git a/drivers/net/ethernet/dec/Kconfig b/drivers/net/ethernet/dec/Kconfig new file mode 100644 index 00000000000..37940279ded --- /dev/null +++ b/drivers/net/ethernet/dec/Kconfig @@ -0,0 +1,37 @@ +# +# Digital Equipment Inc network device configuration +# + +config NET_VENDOR_DEC + bool "Digital Equipment devices" + default y + depends on PCI || EISA || CARDBUS + ---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 doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about DEC cards. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_DEC + +config EWRK3 + tristate "EtherWORKS 3 (DE203, DE204, DE205) support" + depends on ISA + select CRC32 + ---help--- + This driver supports the DE203, DE204 and DE205 network (Ethernet) + cards. If this is for you, say Y and read + <file:Documentation/networking/ewrk3.txt> in the kernel source as + well as 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 ewrk3. + +source "drivers/net/ethernet/dec/tulip/Kconfig" + +endif # NET_VENDOR_DEC diff --git a/drivers/net/ethernet/dec/Makefile b/drivers/net/ethernet/dec/Makefile new file mode 100644 index 00000000000..1b01ed8d42c --- /dev/null +++ b/drivers/net/ethernet/dec/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Digital Equipment Inc. network device drivers. +# + +obj-$(CONFIG_EWRK3) += ewrk3.o +obj-$(CONFIG_NET_TULIP) += tulip/ diff --git a/drivers/net/ethernet/dec/ewrk3.c b/drivers/net/ethernet/dec/ewrk3.c new file mode 100644 index 00000000000..f9df5e4d034 --- /dev/null +++ b/drivers/net/ethernet/dec/ewrk3.c @@ -0,0 +1,1959 @@ +/* ewrk3.c: A DIGITAL EtherWORKS 3 ethernet driver for Linux. + + Written 1994 by David C. Davies. + + Copyright 1994 Digital Equipment Corporation. + + This software may be used and distributed according to the terms of + the GNU General Public License, incorporated herein by reference. + + This driver is written for the Digital Equipment Corporation series + of EtherWORKS ethernet cards: + + DE203 Turbo (BNC) + DE204 Turbo (TP) + DE205 Turbo (TP BNC) + + The driver has been tested on a relatively busy network using the DE205 + card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s + (7.8Mb/s) to a DECstation 5000/200. + + The author may be reached at davies@maniac.ultranet.com. + + ========================================================================= + This driver has been written substantially from scratch, although its + inheritance of style and stack interface from 'depca.c' and in turn from + Donald Becker's 'lance.c' should be obvious. + + The DE203/4/5 boards all use a new proprietary chip in place of the + LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422). + Use the depca.c driver in the standard distribution for the LANCE based + cards from DIGITAL; this driver will not work with them. + + The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O + only makes all the card accesses through I/O transactions and no high + (shared) memory is used. This mode provides a >48% performance penalty + and is deprecated in this driver, although allowed to provide initial + setup when hardstrapped. + + The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is + no point in using any mode other than the 2kB mode - their performances + are virtually identical, although the driver has been tested in the 2kB + and 32kB modes. I would suggest you uncomment the line: + + FORCE_2K_MODE; + + to allow the driver to configure the card as a 2kB card at your current + base address, thus leaving more room to clutter your system box with + other memory hungry boards. + + As many ISA and EISA cards can be supported under this driver as you + wish, limited primarily by the available IRQ lines, rather than by the + available I/O addresses (24 ISA, 16 EISA). I have checked different + configurations of multiple depca cards and ewrk3 cards and have not + found a problem yet (provided you have at least depca.c v0.38) ... + + The board IRQ setting must be at an unused IRQ which is auto-probed + using Donald Becker's autoprobe routines. All these cards are at + {5,10,11,15}. + + No 16MB memory limitation should exist with this driver as DMA is not + used and the common memory area is in low memory on the network card (my + current system has 20MB and I've not had problems yet). + + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long + reboot sequences). To utilise this ability, you have to do 8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) edit the source code near line 1898 to reflect the I/O address and + IRQ you're using. + 3) compile ewrk3.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the ewrk3 configuration turned off and reboot. + 5) insmod ewrk3.o + [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] + [Adam Kropelin: now accepts irq=x1,x2 io=y1,y2 for multiple cards] + 6) run the net startup bits for your new eth?? interface manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + Note that autoprobing is not allowed in loadable modules - the system is + already up and running and you're messing with interrupts. + + To unload a module, turn off the associated interface + 'ifconfig eth?? down' then 'rmmod ewrk3'. + + Promiscuous mode has been turned off in this driver, but all the + multicast address bits have been turned on. This improved the send + performance on a busy network by about 13%. + + Ioctl's have now been provided (primarily because I wanted to grab some + packet size statistics). They are patterned after 'plipconfig.c' from a + suggestion by Alan Cox. Using these ioctls, you can enable promiscuous + mode, add/delete multicast addresses, change the hardware address, get + packet size distribution statistics and muck around with the control and + status register. I'll add others if and when the need arises. + + TO DO: + ------ + + + Revision History + ---------------- + + Version Date Description + + 0.1 26-aug-94 Initial writing. ALPHA code release. + 0.11 31-aug-94 Fixed: 2k mode memory base calc., + LeMAC version calc., + IRQ vector assignments during autoprobe. + 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card. + Fixed up MCA hash table algorithm. + 0.20 4-sep-94 Added IOCTL functionality. + 0.21 14-sep-94 Added I/O mode. + 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0. + 0.22 16-sep-94 Added more IOCTLs & tidied up. + 0.23 21-sep-94 Added transmit cut through. + 0.24 31-oct-94 Added uid checks in some ioctls. + 0.30 1-nov-94 BETA code release. + 0.31 5-dec-94 Added check/allocate region code. + 0.32 16-jan-95 Broadcast packet fix. + 0.33 10-Feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>. + 0.40 27-Dec-95 Rationalise MODULE and autoprobe code. + Rewrite for portability & updated. + ALPHA support from <jestabro@amt.tay1.dec.com> + Added verify_area() calls in ewrk3_ioctl() from + suggestion by <heiko@colossus.escape.de>. + Add new multicasting code. + 0.41 20-Jan-96 Fix IRQ set up problem reported by + <kenneth@bbs.sas.ntu.ac.sg>. + 0.42 22-Apr-96 Fix alloc_device() bug <jari@markkus2.fimr.fi> + 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c + 0.44 08-Nov-01 use library crc32 functions <Matt_Domsch@dell.com> + 0.45 19-Jul-02 fix unaligned access on alpha <martin@bruli.net> + 0.46 10-Oct-02 Multiple NIC support when module <akropel1@rochester.rr.com> + 0.47 18-Oct-02 ethtool support <akropel1@rochester.rr.com> + 0.48 18-Oct-02 cli/sti removal for 2.5 <vda@port.imtp.ilyichevsk.odessa.ua> + ioctl locking, signature search cleanup <akropel1@rochester.rr.com> + + ========================================================================= + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/crc32.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/ethtool.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/unistd.h> +#include <linux/ctype.h> +#include <linux/bitops.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/uaccess.h> + +#include "ewrk3.h" + +#define DRV_NAME "ewrk3" +#define DRV_VERSION "0.48" + +static char version[] __initdata = +DRV_NAME ":v" DRV_VERSION " 2002/10/18 davies@maniac.ultranet.com\n"; + +#ifdef EWRK3_DEBUG +static int ewrk3_debug = EWRK3_DEBUG; +#else +static int ewrk3_debug = 1; +#endif + +#define EWRK3_NDA 0xffe0 /* No Device Address */ + +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +#ifndef EWRK3_SIGNATURE +#define EWRK3_SIGNATURE {"DE203","DE204","DE205",""} +#define EWRK3_STRLEN 8 +#endif + +#ifndef EWRK3_RAM_BASE_ADDRESSES +#define EWRK3_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0x00000} +#endif + +/* + ** Sets up the I/O area for the autoprobe. + */ +#define EWRK3_IO_BASE 0x100 /* Start address for probe search */ +#define EWRK3_IOP_INC 0x20 /* I/O address increment */ +#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */ + +#ifndef MAX_NUM_EWRK3S +#define MAX_NUM_EWRK3S 21 +#endif + +#ifndef EWRK3_EISA_IO_PORTS +#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ +#endif + +#ifndef MAX_EISA_SLOTS +#define MAX_EISA_SLOTS 16 +#define EISA_SLOT_INC 0x1000 +#endif + +#define QUEUE_PKT_TIMEOUT (1*HZ) /* Jiffies */ + +/* + ** EtherWORKS 3 shared memory window sizes + */ +#define IO_ONLY 0x00 +#define SHMEM_2K 0x800 +#define SHMEM_32K 0x8000 +#define SHMEM_64K 0x10000 + +/* + ** EtherWORKS 3 IRQ ENABLE/DISABLE + */ +#define ENABLE_IRQs { \ + icr |= lp->irq_mask;\ + outb(icr, EWRK3_ICR); /* Enable the IRQs */\ +} + +#define DISABLE_IRQs { \ + icr = inb(EWRK3_ICR);\ + icr &= ~lp->irq_mask;\ + outb(icr, EWRK3_ICR); /* Disable the IRQs */\ +} + +/* + ** EtherWORKS 3 START/STOP + */ +#define START_EWRK3 { \ + csr = inb(EWRK3_CSR);\ + csr &= ~(CSR_TXD|CSR_RXD);\ + outb(csr, EWRK3_CSR); /* Enable the TX and/or RX */\ +} + +#define STOP_EWRK3 { \ + csr = (CSR_TXD|CSR_RXD);\ + outb(csr, EWRK3_CSR); /* Disable the TX and/or RX */\ +} + +/* + ** The EtherWORKS 3 private structure + */ +#define EWRK3_PKT_STAT_SZ 16 +#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you + increase EWRK3_PKT_STAT_SZ */ + +struct ewrk3_stats { + u32 bins[EWRK3_PKT_STAT_SZ]; + u32 unicast; + u32 multicast; + u32 broadcast; + u32 excessive_collisions; + u32 tx_underruns; + u32 excessive_underruns; +}; + +struct ewrk3_private { + char adapter_name[80]; /* Name exported to /proc/ioports */ + u_long shmem_base; /* Shared memory start address */ + void __iomem *shmem; + u_long shmem_length; /* Shared memory window length */ + struct ewrk3_stats pktStats; /* Private stats counters */ + u_char irq_mask; /* Adapter IRQ mask bits */ + u_char mPage; /* Maximum 2kB Page number */ + u_char lemac; /* Chip rev. level */ + u_char hard_strapped; /* Don't allow a full open */ + u_char txc; /* Transmit cut through */ + void __iomem *mctbl; /* Pointer to the multicast table */ + u_char led_mask; /* Used to reserve LED access for ethtool */ + spinlock_t hw_lock; +}; + +/* + ** Force the EtherWORKS 3 card to be in 2kB MODE + */ +#define FORCE_2K_MODE { \ + shmem_length = SHMEM_2K;\ + outb(((mem_start - 0x80000) >> 11), EWRK3_MBR);\ +} + +/* + ** Public Functions + */ +static int ewrk3_open(struct net_device *dev); +static netdev_tx_t ewrk3_queue_pkt(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t ewrk3_interrupt(int irq, void *dev_id); +static int ewrk3_close(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static const struct ethtool_ops ethtool_ops_203; +static const struct ethtool_ops ethtool_ops; + +/* + ** Private functions + */ +static int ewrk3_hw_init(struct net_device *dev, u_long iobase); +static void ewrk3_init(struct net_device *dev); +static int ewrk3_rx(struct net_device *dev); +static int ewrk3_tx(struct net_device *dev); +static void ewrk3_timeout(struct net_device *dev); + +static void EthwrkSignature(char *name, char *eeprom_image); +static int DevicePresent(u_long iobase); +static void SetMulticastFilter(struct net_device *dev); +static int EISA_signature(char *name, s32 eisa_id); + +static int Read_EEPROM(u_long iobase, u_char eaddr); +static int Write_EEPROM(short data, u_long iobase, u_char eaddr); +static u_char get_hw_addr(struct net_device *dev, u_char * eeprom_image, char chipType); + +static int ewrk3_probe1(struct net_device *dev, u_long iobase, int irq); +static int isa_probe(struct net_device *dev, u_long iobase); +static int eisa_probe(struct net_device *dev, u_long iobase); + +static u_char irq[MAX_NUM_EWRK3S+1] = {5, 0, 10, 3, 11, 9, 15, 12}; + +static char name[EWRK3_STRLEN + 1]; +static int num_ewrks3s; + +/* + ** Miscellaneous defines... + */ +#define INIT_EWRK3 {\ + outb(EEPROM_INIT, EWRK3_IOPR);\ + mdelay(1);\ +} + +#ifndef MODULE +struct net_device * __init ewrk3_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct ewrk3_private)); + int err; + + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + } + + err = ewrk3_probe1(dev, dev->base_addr, dev->irq); + if (err) + goto out; + return dev; +out: + free_netdev(dev); + return ERR_PTR(err); + +} +#endif + +static int __init ewrk3_probe1(struct net_device *dev, u_long iobase, int irq) +{ + int err; + + dev->base_addr = iobase; + dev->irq = irq; + + /* Address PROM pattern */ + err = isa_probe(dev, iobase); + if (err != 0) + err = eisa_probe(dev, iobase); + + if (err) + return err; + + err = register_netdev(dev); + if (err) + release_region(dev->base_addr, EWRK3_TOTAL_SIZE); + + return err; +} + +static const struct net_device_ops ewrk3_netdev_ops = { + .ndo_open = ewrk3_open, + .ndo_start_xmit = ewrk3_queue_pkt, + .ndo_stop = ewrk3_close, + .ndo_set_rx_mode = set_multicast_list, + .ndo_do_ioctl = ewrk3_ioctl, + .ndo_tx_timeout = ewrk3_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __init +ewrk3_hw_init(struct net_device *dev, u_long iobase) +{ + struct ewrk3_private *lp; + int i, status = 0; + u_long mem_start, shmem_length; + u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0; + u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0; + + /* + ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot. + ** This also disables the EISA_ENABLE bit in the EISA Control Register. + */ + if (iobase > 0x400) + eisa_cr = inb(EISA_CR); + INIT_EWRK3; + + nicsr = inb(EWRK3_CSR); + + icr = inb(EWRK3_ICR); + icr &= 0x70; + outb(icr, EWRK3_ICR); /* Disable all the IRQs */ + + if (nicsr != (CSR_TXD | CSR_RXD)) + return -ENXIO; + + /* Check that the EEPROM is alive and well and not living on Pluto... */ + for (chksum = 0, i = 0; i < EEPROM_MAX; i += 2) { + union { + short val; + char c[2]; + } tmp; + + tmp.val = (short) Read_EEPROM(iobase, (i >> 1)); + eeprom_image[i] = tmp.c[0]; + eeprom_image[i + 1] = tmp.c[1]; + chksum += eeprom_image[i] + eeprom_image[i + 1]; + } + + if (chksum != 0) { /* Bad EEPROM Data! */ + printk("%s: Device has a bad on-board EEPROM.\n", dev->name); + return -ENXIO; + } + + EthwrkSignature(name, eeprom_image); + if (*name == '\0') + return -ENXIO; + + dev->base_addr = iobase; + + if (iobase > 0x400) { + outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */ + } + lemac = eeprom_image[EEPROM_CHIPVER]; + cmr = inb(EWRK3_CMR); + + if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) || + ((lemac == LeMAC2) && !(cmr & CMR_HS))) { + printk("%s: %s at %#4lx", dev->name, name, iobase); + hard_strapped = 1; + } else if ((iobase & 0x0fff) == EWRK3_EISA_IO_PORTS) { + /* EISA slot address */ + printk("%s: %s at %#4lx (EISA slot %ld)", + dev->name, name, iobase, ((iobase >> 12) & 0x0f)); + } else { /* ISA port address */ + printk("%s: %s at %#4lx", dev->name, name, iobase); + } + + printk(", h/w address "); + if (lemac != LeMAC2) + DevicePresent(iobase); /* need after EWRK3_INIT */ + status = get_hw_addr(dev, eeprom_image, lemac); + printk("%pM\n", dev->dev_addr); + + if (status) { + printk(" which has an EEPROM CRC error.\n"); + return -ENXIO; + } + + if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */ + cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS); + if (eeprom_image[EEPROM_MISC0] & READ_AHEAD) + cmr |= CMR_RA; + if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND) + cmr |= CMR_WB; + if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL) + cmr |= CMR_POLARITY; + if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) + cmr |= CMR_LINK; + if (eeprom_image[EEPROM_MISC0] & _0WS_ENA) + cmr |= CMR_0WS; + } + if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM) + cmr |= CMR_DRAM; + outb(cmr, EWRK3_CMR); + + cr = inb(EWRK3_CR); /* Set up the Control Register */ + cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD; + if (cr & SETUP_APD) + cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS; + cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS; + cr |= eeprom_image[EEPROM_MISC0] & ENA_16; + outb(cr, EWRK3_CR); + + /* + ** Determine the base address and window length for the EWRK3 + ** RAM from the memory base register. + */ + mem_start = inb(EWRK3_MBR); + shmem_length = 0; + if (mem_start != 0) { + if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) { + mem_start *= SHMEM_64K; + shmem_length = SHMEM_64K; + } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) { + mem_start *= SHMEM_32K; + shmem_length = SHMEM_32K; + } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) { + mem_start = mem_start * SHMEM_2K + 0x80000; + shmem_length = SHMEM_2K; + } else { + return -ENXIO; + } + } + /* + ** See the top of this source code for comments about + ** uncommenting this line. + */ +/* FORCE_2K_MODE; */ + + if (hard_strapped) { + printk(" is hard strapped.\n"); + } else if (mem_start) { + printk(" has a %dk RAM window", (int) (shmem_length >> 10)); + printk(" at 0x%.5lx", mem_start); + } else { + printk(" is in I/O only mode"); + } + + lp = netdev_priv(dev); + lp->shmem_base = mem_start; + lp->shmem = ioremap(mem_start, shmem_length); + if (!lp->shmem) + return -ENOMEM; + lp->shmem_length = shmem_length; + lp->lemac = lemac; + lp->hard_strapped = hard_strapped; + lp->led_mask = CR_LED; + spin_lock_init(&lp->hw_lock); + + lp->mPage = 64; + if (cmr & CMR_DRAM) + lp->mPage <<= 1; /* 2 DRAMS on module */ + + sprintf(lp->adapter_name, "%s (%s)", name, dev->name); + + lp->irq_mask = ICR_TNEM | ICR_TXDM | ICR_RNEM | ICR_RXDM; + + if (!hard_strapped) { + /* + ** Enable EWRK3 board interrupts for autoprobing + */ + icr |= ICR_IE; /* Enable interrupts */ + outb(icr, EWRK3_ICR); + + /* The DMA channel may be passed in on this parameter. */ + dev->dma = 0; + + /* To auto-IRQ we enable the initialization-done and DMA err, + interrupts. For now we will always get a DMA error. */ + if (dev->irq < 2) { +#ifndef MODULE + u_char irqnum; + unsigned long irq_mask; + + + irq_mask = probe_irq_on(); + + /* + ** Trigger a TNE interrupt. + */ + icr |= ICR_TNEM; + outb(1, EWRK3_TDQ); /* Write to the TX done queue */ + outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */ + + irqnum = irq[((icr & IRQ_SEL) >> 4)]; + + mdelay(20); + dev->irq = probe_irq_off(irq_mask); + if ((dev->irq) && (irqnum == dev->irq)) { + printk(" and uses IRQ%d.\n", dev->irq); + } else { + if (!dev->irq) { + printk(" and failed to detect IRQ line.\n"); + } else if ((irqnum == 1) && (lemac == LeMAC2)) { + printk(" and an illegal IRQ line detected.\n"); + } else { + printk(", but incorrect IRQ line detected.\n"); + } + iounmap(lp->shmem); + return -ENXIO; + } + + DISABLE_IRQs; /* Mask all interrupts */ + +#endif /* MODULE */ + } else { + printk(" and requires IRQ%d.\n", dev->irq); + } + } + + if (ewrk3_debug > 1) { + printk(version); + } + /* The EWRK3-specific entries in the device structure. */ + dev->netdev_ops = &ewrk3_netdev_ops; + if (lp->adapter_name[4] == '3') + SET_ETHTOOL_OPS(dev, ðtool_ops_203); + else + SET_ETHTOOL_OPS(dev, ðtool_ops); + dev->watchdog_timeo = QUEUE_PKT_TIMEOUT; + + dev->mem_start = 0; + + return 0; +} + + +static int ewrk3_open(struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int status = 0; + u_char icr, csr; + + /* + ** Stop the TX and RX... + */ + STOP_EWRK3; + + if (!lp->hard_strapped) { + if (request_irq(dev->irq, (void *) ewrk3_interrupt, 0, "ewrk3", dev)) { + printk("ewrk3_open(): Requested IRQ%d is busy\n", dev->irq); + status = -EAGAIN; + } else { + + /* + ** Re-initialize the EWRK3... + */ + ewrk3_init(dev); + + if (ewrk3_debug > 1) { + printk("%s: ewrk3 open with irq %d\n", dev->name, dev->irq); + printk(" physical address: %pM\n", dev->dev_addr); + if (lp->shmem_length == 0) { + printk(" no shared memory, I/O only mode\n"); + } else { + printk(" start of shared memory: 0x%08lx\n", lp->shmem_base); + printk(" window length: 0x%04lx\n", lp->shmem_length); + } + printk(" # of DRAMS: %d\n", ((inb(EWRK3_CMR) & 0x02) ? 2 : 1)); + printk(" csr: 0x%02x\n", inb(EWRK3_CSR)); + printk(" cr: 0x%02x\n", inb(EWRK3_CR)); + printk(" icr: 0x%02x\n", inb(EWRK3_ICR)); + printk(" cmr: 0x%02x\n", inb(EWRK3_CMR)); + printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC)); + } + netif_start_queue(dev); + /* + ** Unmask EWRK3 board interrupts + */ + icr = inb(EWRK3_ICR); + ENABLE_IRQs; + + } + } else { + printk(KERN_ERR "%s: ewrk3 available for hard strapped set up only.\n", dev->name); + printk(KERN_ERR " Run the 'ewrk3setup' utility or remove the hard straps.\n"); + return -EINVAL; + } + + return status; +} + +/* + ** Initialize the EtherWORKS 3 operating conditions + */ +static void ewrk3_init(struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + u_char csr, page; + u_long iobase = dev->base_addr; + int i; + + /* + ** Enable any multicasts + */ + set_multicast_list(dev); + + /* + ** Set hardware MAC address. Address is initialized from the EEPROM + ** during startup but may have since been changed by the user. + */ + for (i=0; i<ETH_ALEN; i++) + outb(dev->dev_addr[i], EWRK3_PAR0 + i); + + /* + ** Clean out any remaining entries in all the queues here + */ + while (inb(EWRK3_TQ)); + while (inb(EWRK3_TDQ)); + while (inb(EWRK3_RQ)); + while (inb(EWRK3_FMQ)); + + /* + ** Write a clean free memory queue + */ + for (page = 1; page < lp->mPage; page++) { /* Write the free page numbers */ + outb(page, EWRK3_FMQ); /* to the Free Memory Queue */ + } + + START_EWRK3; /* Enable the TX and/or RX */ +} + +/* + * Transmit timeout + */ + +static void ewrk3_timeout(struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + u_char icr, csr; + u_long iobase = dev->base_addr; + + if (!lp->hard_strapped) + { + printk(KERN_WARNING"%s: transmit timed/locked out, status %04x, resetting.\n", + dev->name, inb(EWRK3_CSR)); + + /* + ** Mask all board interrupts + */ + DISABLE_IRQs; + + /* + ** Stop the TX and RX... + */ + STOP_EWRK3; + + ewrk3_init(dev); + + /* + ** Unmask EWRK3 board interrupts + */ + ENABLE_IRQs; + + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue(dev); + } +} + +/* + ** Writes a socket buffer to the free page queue + */ +static netdev_tx_t ewrk3_queue_pkt(struct sk_buff *skb, struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + void __iomem *buf = NULL; + u_char icr; + u_char page; + + spin_lock_irq (&lp->hw_lock); + DISABLE_IRQs; + + /* if no resources available, exit, request packet be queued */ + if (inb (EWRK3_FMQC) == 0) { + printk (KERN_WARNING "%s: ewrk3_queue_pkt(): No free resources...\n", + dev->name); + printk (KERN_WARNING "%s: ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n", + dev->name, inb (EWRK3_CSR), inb (EWRK3_ICR), + inb (EWRK3_FMQC)); + goto err_out; + } + + /* + ** Get a free page from the FMQ + */ + if ((page = inb (EWRK3_FMQ)) >= lp->mPage) { + printk ("ewrk3_queue_pkt(): Invalid free memory page (%d).\n", + (u_char) page); + goto err_out; + } + + + /* + ** Set up shared memory window and pointer into the window + */ + if (lp->shmem_length == IO_ONLY) { + outb (page, EWRK3_IOPR); + } else if (lp->shmem_length == SHMEM_2K) { + buf = lp->shmem; + outb (page, EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_32K) { + buf = (((short) page << 11) & 0x7800) + lp->shmem; + outb ((page >> 4), EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_64K) { + buf = (((short) page << 11) & 0xf800) + lp->shmem; + outb ((page >> 5), EWRK3_MPR); + } else { + printk (KERN_ERR "%s: Oops - your private data area is hosed!\n", + dev->name); + BUG (); + } + + /* + ** Set up the buffer control structures and copy the data from + ** the socket buffer to the shared memory . + */ + if (lp->shmem_length == IO_ONLY) { + int i; + u_char *p = skb->data; + outb ((char) (TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA); + outb ((char) (skb->len & 0xff), EWRK3_DATA); + outb ((char) ((skb->len >> 8) & 0xff), EWRK3_DATA); + outb ((char) 0x04, EWRK3_DATA); + for (i = 0; i < skb->len; i++) { + outb (*p++, EWRK3_DATA); + } + outb (page, EWRK3_TQ); /* Start sending pkt */ + } else { + writeb ((char) (TCR_QMODE | TCR_PAD | TCR_IFC), buf); /* ctrl byte */ + buf += 1; + writeb ((char) (skb->len & 0xff), buf); /* length (16 bit xfer) */ + buf += 1; + if (lp->txc) { + writeb(((skb->len >> 8) & 0xff) | XCT, buf); + buf += 1; + writeb (0x04, buf); /* index byte */ + buf += 1; + writeb (0x00, (buf + skb->len)); /* Write the XCT flag */ + memcpy_toio (buf, skb->data, PRELOAD); /* Write PRELOAD bytes */ + outb (page, EWRK3_TQ); /* Start sending pkt */ + memcpy_toio (buf + PRELOAD, + skb->data + PRELOAD, + skb->len - PRELOAD); + writeb (0xff, (buf + skb->len)); /* Write the XCT flag */ + } else { + writeb ((skb->len >> 8) & 0xff, buf); + buf += 1; + writeb (0x04, buf); /* index byte */ + buf += 1; + memcpy_toio (buf, skb->data, skb->len); /* Write data bytes */ + outb (page, EWRK3_TQ); /* Start sending pkt */ + } + } + + ENABLE_IRQs; + spin_unlock_irq (&lp->hw_lock); + + dev->stats.tx_bytes += skb->len; + dev_kfree_skb (skb); + + /* Check for free resources: stop Tx queue if there are none */ + if (inb (EWRK3_FMQC) == 0) + netif_stop_queue (dev); + + return NETDEV_TX_OK; + +err_out: + ENABLE_IRQs; + spin_unlock_irq (&lp->hw_lock); + return NETDEV_TX_BUSY; +} + +/* + ** The EWRK3 interrupt handler. + */ +static irqreturn_t ewrk3_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct ewrk3_private *lp; + u_long iobase; + u_char icr, cr, csr; + + lp = netdev_priv(dev); + iobase = dev->base_addr; + + /* get the interrupt information */ + csr = inb(EWRK3_CSR); + + /* + * |