From a88394cfb58007cca945699545469017beb0d206 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Fri, 20 May 2011 07:15:19 -0700 Subject: ewrk3/tulip: Move the DEC - Tulip drivers Move the DEC - Tulip driver into drivers/net/ethernet/dec/tulip/ and make the necessary Kconfig and Makefile changes. The Digital Equioment (DEC) driver ewrk3 was moved into drivers/net/ethernet/dec/ and the remaining drivers (Tulip) were moved into drivers/net/ethernet/dec/tulip/ CC: Tobias Ringstrom CC: Grant Grundler CC: David Davies Signed-off-by: Jeff Kirsher Acked-by: Grant Grundler --- drivers/net/ethernet/dec/Kconfig | 36 + drivers/net/ethernet/dec/Makefile | 6 + drivers/net/ethernet/dec/ewrk3.c | 1959 +++++++++ drivers/net/ethernet/dec/ewrk3.h | 322 ++ drivers/net/ethernet/dec/tulip/21142.c | 260 ++ drivers/net/ethernet/dec/tulip/Kconfig | 171 + drivers/net/ethernet/dec/tulip/Makefile | 19 + drivers/net/ethernet/dec/tulip/de2104x.c | 2215 ++++++++++ drivers/net/ethernet/dec/tulip/de4x5.c | 5599 ++++++++++++++++++++++++++ drivers/net/ethernet/dec/tulip/de4x5.h | 1019 +++++ drivers/net/ethernet/dec/tulip/dmfe.c | 2253 +++++++++++ drivers/net/ethernet/dec/tulip/eeprom.c | 385 ++ drivers/net/ethernet/dec/tulip/interrupt.c | 811 ++++ drivers/net/ethernet/dec/tulip/media.c | 556 +++ drivers/net/ethernet/dec/tulip/pnic.c | 173 + drivers/net/ethernet/dec/tulip/pnic2.c | 406 ++ drivers/net/ethernet/dec/tulip/timer.c | 179 + drivers/net/ethernet/dec/tulip/tulip.h | 573 +++ drivers/net/ethernet/dec/tulip/tulip_core.c | 2011 +++++++++ drivers/net/ethernet/dec/tulip/uli526x.c | 1850 +++++++++ drivers/net/ethernet/dec/tulip/winbond-840.c | 1670 ++++++++ drivers/net/ethernet/dec/tulip/xircom_cb.c | 1154 ++++++ 22 files changed, 23627 insertions(+) create mode 100644 drivers/net/ethernet/dec/Kconfig create mode 100644 drivers/net/ethernet/dec/Makefile create mode 100644 drivers/net/ethernet/dec/ewrk3.c create mode 100644 drivers/net/ethernet/dec/ewrk3.h create mode 100644 drivers/net/ethernet/dec/tulip/21142.c create mode 100644 drivers/net/ethernet/dec/tulip/Kconfig create mode 100644 drivers/net/ethernet/dec/tulip/Makefile create mode 100644 drivers/net/ethernet/dec/tulip/de2104x.c create mode 100644 drivers/net/ethernet/dec/tulip/de4x5.c create mode 100644 drivers/net/ethernet/dec/tulip/de4x5.h create mode 100644 drivers/net/ethernet/dec/tulip/dmfe.c create mode 100644 drivers/net/ethernet/dec/tulip/eeprom.c create mode 100644 drivers/net/ethernet/dec/tulip/interrupt.c create mode 100644 drivers/net/ethernet/dec/tulip/media.c create mode 100644 drivers/net/ethernet/dec/tulip/pnic.c create mode 100644 drivers/net/ethernet/dec/tulip/pnic2.c create mode 100644 drivers/net/ethernet/dec/tulip/timer.c create mode 100644 drivers/net/ethernet/dec/tulip/tulip.h create mode 100644 drivers/net/ethernet/dec/tulip/tulip_core.c create mode 100644 drivers/net/ethernet/dec/tulip/uli526x.c create mode 100644 drivers/net/ethernet/dec/tulip/winbond-840.c create mode 100644 drivers/net/ethernet/dec/tulip/xircom_cb.c (limited to 'drivers/net/ethernet/dec') diff --git a/drivers/net/ethernet/dec/Kconfig b/drivers/net/ethernet/dec/Kconfig new file mode 100644 index 000000000000..40e8df9fde8d --- /dev/null +++ b/drivers/net/ethernet/dec/Kconfig @@ -0,0 +1,36 @@ +# +# Digital Equipment Inc network device configuration +# + +config NET_VENDOR_DEC + bool "Digital Equipment devices" + 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 + . + + 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 + in the kernel source as + well as the Ethernet-HOWTO, available from + . + + 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 000000000000..1b01ed8d42c8 --- /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 000000000000..05a5f71451a7 --- /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 . + 0.40 27-Dec-95 Rationalise MODULE and autoprobe code. + Rewrite for portability & updated. + ALPHA support from + Added verify_area() calls in ewrk3_ioctl() from + suggestion by . + Add new multicasting code. + 0.41 20-Jan-96 Fix IRQ set up problem reported by + . + 0.42 22-Apr-96 Fix alloc_device() bug + 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c + 0.44 08-Nov-01 use library crc32 functions + 0.45 19-Jul-02 fix unaligned access on alpha + 0.46 10-Oct-02 Multiple NIC support when module + 0.47 18-Oct-02 ethtool support + 0.48 18-Oct-02 cli/sti removal for 2.5 + ioctl locking, signature search cleanup + + ========================================================================= + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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_multicast_list = 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; idev_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); + + /* + ** Mask the EWRK3 board interrupts and turn on the LED + */ + spin_lock(&lp->hw_lock); + DISABLE_IRQs; + + cr = inb(EWRK3_CR); + cr |= lp->led_mask; + outb(cr, EWRK3_CR); + + if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */ + ewrk3_rx(dev); + + if (csr & CSR_TNE) /* Tx interrupt (packet sent) */ + ewrk3_tx(dev); + + /* + ** Now deal with the TX/RX disable flags. These are set when there + ** are no more resources. If resources free up then enable these + ** interrupts, otherwise mask them - failure to do this will result + ** in the system hanging in an interrupt loop. + */ + if (inb(EWRK3_FMQC)) { /* any resources available? */ + lp->irq_mask |= ICR_TXDM | ICR_RXDM; /* enable the interrupt source */ + csr &= ~(CSR_TXD | CSR_RXD); /* ensure restart of a stalled TX or RX */ + outb(csr, EWRK3_CSR); + netif_wake_queue(dev); + } else { + lp->irq_mask &= ~(ICR_TXDM | ICR_RXDM); /* disable the interrupt source */ + } + + /* Unmask the EWRK3 board interrupts and turn off the LED */ + cr &= ~(lp->led_mask); + outb(cr, EWRK3_CR); + ENABLE_IRQs; + spin_unlock(&lp->hw_lock); + return IRQ_HANDLED; +} + +/* Called with lp->hw_lock held */ +static int ewrk3_rx(struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int i, status = 0; + u_char page; + void __iomem *buf = NULL; + + while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */ + if ((page = inb(EWRK3_RQ)) < lp->mPage) { /* Get next entry's buffer page */ + /* + ** 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 { + status = -1; + printk("%s: Oops - your private data area is hosed!\n", dev->name); + } + + if (!status) { + char rx_status; + int pkt_len; + + if (lp->shmem_length == IO_ONLY) { + rx_status = inb(EWRK3_DATA); + pkt_len = inb(EWRK3_DATA); + pkt_len |= ((u_short) inb(EWRK3_DATA) << 8); + } else { + rx_status = readb(buf); + buf += 1; + pkt_len = readw(buf); + buf += 3; + } + + if (!(rx_status & R_ROK)) { /* There was an error. */ + dev->stats.rx_errors++; /* Update the error stats. */ + if (rx_status & R_DBE) + dev->stats.rx_frame_errors++; + if (rx_status & R_CRC) + dev->stats.rx_crc_errors++; + if (rx_status & R_PLL) + dev->stats.rx_fifo_errors++; + } else { + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + unsigned char *p; + skb_reserve(skb, 2); /* Align to 16 bytes */ + p = skb_put(skb, pkt_len); + + if (lp->shmem_length == IO_ONLY) { + *p = inb(EWRK3_DATA); /* dummy read */ + for (i = 0; i < pkt_len; i++) { + *p++ = inb(EWRK3_DATA); + } + } else { + memcpy_fromio(p, buf, pkt_len); + } + + for (i = 1; i < EWRK3_PKT_STAT_SZ - 1; i++) { + if (pkt_len < i * EWRK3_PKT_BIN_SZ) { + lp->pktStats.bins[i]++; + i = EWRK3_PKT_STAT_SZ; + } + } + p = skb->data; /* Look at the dest addr */ + if (is_multicast_ether_addr(p)) { + if (is_broadcast_ether_addr(p)) { + lp->pktStats.broadcast++; + } else { + lp->pktStats.multicast++; + } + } else if (compare_ether_addr(p, dev->dev_addr) == 0) { + lp->pktStats.unicast++; + } + lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ + if (lp->pktStats.bins[0] == 0) { /* Reset counters */ + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + } + /* + ** Notify the upper protocol layers that there is another + ** packet to handle + */ + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + + /* + ** Update stats + */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } else { + printk("%s: Insufficient memory; nuking packet.\n", dev->name); + dev->stats.rx_dropped++; /* Really, deferred. */ + break; + } + } + } + /* + ** Return the received buffer to the free memory queue + */ + outb(page, EWRK3_FMQ); + } else { + printk("ewrk3_rx(): Illegal page number, page %d\n", page); + printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n", inb(EWRK3_CSR), inb(EWRK3_ICR), inb(EWRK3_FMQC)); + } + } + return status; +} + +/* +** Buffer sent - check for TX buffer errors. +** Called with lp->hw_lock held +*/ +static int ewrk3_tx(struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + u_char tx_status; + + while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */ + if (tx_status & T_VSTS) { /* The status is valid */ + if (tx_status & T_TXE) { + dev->stats.tx_errors++; + if (tx_status & T_NCL) + dev->stats.tx_carrier_errors++; + if (tx_status & T_LCL) + dev->stats.tx_window_errors++; + if (tx_status & T_CTU) { + if ((tx_status & T_COLL) ^ T_XUR) { + lp->pktStats.tx_underruns++; + } else { + lp->pktStats.excessive_underruns++; + } + } else if (tx_status & T_COLL) { + if ((tx_status & T_COLL) ^ T_XCOLL) { + dev->stats.collisions++; + } else { + lp->pktStats.excessive_collisions++; + } + } + } else { + dev->stats.tx_packets++; + } + } + } + + return 0; +} + +static int ewrk3_close(struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + u_char icr, csr; + + netif_stop_queue(dev); + + if (ewrk3_debug > 1) { + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inb(EWRK3_CSR)); + } + /* + ** We stop the EWRK3 here... mask interrupts and stop TX & RX + */ + DISABLE_IRQs; + + STOP_EWRK3; + + /* + ** Clean out the TX and RX queues here (note that one entry + ** may get added to either the TXD or RX queues if the TX or RX + ** just starts processing a packet before the STOP_EWRK3 command + ** is received. This will be flushed in the ewrk3_open() call). + */ + while (inb(EWRK3_TQ)); + while (inb(EWRK3_TDQ)); + while (inb(EWRK3_RQ)); + + if (!lp->hard_strapped) { + free_irq(dev->irq, dev); + } + return 0; +} + +/* + ** Set or clear the multicast filter for this adapter. + */ +static void set_multicast_list(struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + u_char csr; + + csr = inb(EWRK3_CSR); + + if (lp->shmem_length == IO_ONLY) { + lp->mctbl = NULL; + } else { + lp->mctbl = lp->shmem + PAGE0_HTE; + } + + csr &= ~(CSR_PME | CSR_MCE); + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + csr |= CSR_PME; + outb(csr, EWRK3_CSR); + } else { + SetMulticastFilter(dev); + csr |= CSR_MCE; + outb(csr, EWRK3_CSR); + } +} + +/* + ** Calculate the hash code and update the logical address filter + ** from a list of ethernet multicast addresses. + ** Little endian crc one liner from Matt Thomas, DEC. + ** + ** Note that when clearing the table, the broadcast bit must remain asserted + ** to receive broadcast messages. + */ +static void SetMulticastFilter(struct net_device *dev) +{ + struct ewrk3_private *lp = netdev_priv(dev); + struct netdev_hw_addr *ha; + u_long iobase = dev->base_addr; + int i; + char bit, byte; + short __iomem *p = lp->mctbl; + u16 hashcode; + u32 crc; + + spin_lock_irq(&lp->hw_lock); + + if (lp->shmem_length == IO_ONLY) { + outb(0, EWRK3_IOPR); + outw(PAGE0_HTE, EWRK3_PIR1); + } else { + outb(0, EWRK3_MPR); + } + + if (dev->flags & IFF_ALLMULTI) { + for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { + if (lp->shmem_length == IO_ONLY) { + outb(0xff, EWRK3_DATA); + } else { /* memset didn't work here */ + writew(0xffff, p); + p++; + i++; + } + } + } else { + /* Clear table except for broadcast bit */ + if (lp->shmem_length == IO_ONLY) { + for (i = 0; i < (HASH_TABLE_LEN >> 4) - 1; i++) { + outb(0x00, EWRK3_DATA); + } + outb(0x80, EWRK3_DATA); + i++; /* insert the broadcast bit */ + for (; i < (HASH_TABLE_LEN >> 3); i++) { + outb(0x00, EWRK3_DATA); + } + } else { + memset_io(lp->mctbl, 0, HASH_TABLE_LEN >> 3); + writeb(0x80, lp->mctbl + (HASH_TABLE_LEN >> 4) - 1); + } + + /* Update table */ + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(ETH_ALEN, ha->addr); + hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */ + + byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ + bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */ + + if (lp->shmem_length == IO_ONLY) { + u_char tmp; + + outw(PAGE0_HTE + byte, EWRK3_PIR1); + tmp = inb(EWRK3_DATA); + tmp |= bit; + outw(PAGE0_HTE + byte, EWRK3_PIR1); + outb(tmp, EWRK3_DATA); + } else { + writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte); + } + } + } + + spin_unlock_irq(&lp->hw_lock); +} + +/* + ** ISA bus I/O device probe + */ +static int __init isa_probe(struct net_device *dev, u_long ioaddr) +{ + int i = num_ewrks3s, maxSlots; + int ret = -ENODEV; + + u_long iobase; + + if (ioaddr >= 0x400) + goto out; + + if (ioaddr == 0) { /* Autoprobing */ + iobase = EWRK3_IO_BASE; /* Get the first slot address */ + maxSlots = 24; + } else { /* Probe a specific location */ + iobase = ioaddr; + maxSlots = i + 1; + } + + for (; (i < maxSlots) && (dev != NULL); + iobase += EWRK3_IOP_INC, i++) + { + if (request_region(iobase, EWRK3_TOTAL_SIZE, DRV_NAME)) { + if (DevicePresent(iobase) == 0) { + int irq = dev->irq; + ret = ewrk3_hw_init(dev, iobase); + if (!ret) + break; + dev->irq = irq; + } + release_region(iobase, EWRK3_TOTAL_SIZE); + } + } + out: + + return ret; +} + +/* + ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually + ** the motherboard. + */ +static int __init eisa_probe(struct net_device *dev, u_long ioaddr) +{ + int i, maxSlots; + u_long iobase; + int ret = -ENODEV; + + if (ioaddr < 0x1000) + goto out; + + iobase = ioaddr; + i = (ioaddr >> 12); + maxSlots = i + 1; + + for (i = 1; (i < maxSlots) && (dev != NULL); i++, iobase += EISA_SLOT_INC) { + if (EISA_signature(name, EISA_ID) == 0) { + if (request_region(iobase, EWRK3_TOTAL_SIZE, DRV_NAME) && + DevicePresent(iobase) == 0) { + int irq = dev->irq; + ret = ewrk3_hw_init(dev, iobase); + if (!ret) + break; + dev->irq = irq; + } + release_region(iobase, EWRK3_TOTAL_SIZE); + } + } + + out: + return ret; +} + + +/* + ** Read the EWRK3 EEPROM using this routine + */ +static int Read_EEPROM(u_long iobase, u_char eaddr) +{ + int i; + + outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ + outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */ + for (i = 0; i < 5000; i++) + inb(EWRK3_CSR); /* wait 1msec */ + + return inw(EWRK3_EPROM1); /* 16 bits data return */ +} + +/* + ** Write the EWRK3 EEPROM using this routine + */ +static int Write_EEPROM(short data, u_long iobase, u_char eaddr) +{ + int i; + + outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */ + for (i = 0; i < 5000; i++) + inb(EWRK3_CSR); /* wait 1msec */ + outw(data, EWRK3_EPROM1); /* write data to register */ + outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ + outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */ + for (i = 0; i < 75000; i++) + inb(EWRK3_CSR); /* wait 15msec */ + outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */ + for (i = 0; i < 5000; i++) + inb(EWRK3_CSR); /* wait 1msec */ + + return 0; +} + +/* + ** Look for a particular board name in the on-board EEPROM. + */ +static void __init EthwrkSignature(char *name, char *eeprom_image) +{ + int i; + char *signatures[] = EWRK3_SIGNATURE; + + for (i=0; *signatures[i] != '\0'; i++) + if( !strncmp(eeprom_image+EEPROM_PNAME7, signatures[i], strlen(signatures[i])) ) + break; + + if (*signatures[i] != '\0') { + memcpy(name, eeprom_image+EEPROM_PNAME7, EWRK3_STRLEN); + name[EWRK3_STRLEN] = '\0'; + } else + name[0] = '\0'; +} + +/* + ** Look for a special sequence in the Ethernet station address PROM that + ** is common across all EWRK3 products. + ** + ** Search the Ethernet address ROM for the signature. Since the ROM address + ** counter can start at an arbitrary point, the search must include the entire + ** probe sequence length plus the (length_of_the_signature - 1). + ** Stop the search IMMEDIATELY after the signature is found so that the + ** PROM address counter is correctly positioned at the start of the + ** ethernet address for later read out. + */ + +static int __init DevicePresent(u_long iobase) +{ + union { + struct { + u32 a; + u32 b; + } llsig; + char Sig[sizeof(u32) << 1]; + } + dev; + short sigLength; + char data; + int i, j, status = 0; + + dev.llsig.a = ETH_PROM_SIG; + dev.llsig.b = ETH_PROM_SIG; + sigLength = sizeof(u32) << 1; + + for (i = 0, j = 0; j < sigLength && i < PROBE_LENGTH + sigLength - 1; i++) { + data = inb(EWRK3_APROM); + if (dev.Sig[j] == data) { /* track signature */ + j++; + } else { /* lost signature; begin search again */ + if (data == dev.Sig[0]) { + j = 1; + } else { + j = 0; + } + } + } + + if (j != sigLength) { + status = -ENODEV; /* search failed */ + } + return status; +} + +static u_char __init get_hw_addr(struct net_device *dev, u_char * eeprom_image, char chipType) +{ + int i, j, k; + u_short chksum; + u_char crc, lfsr, sd, status = 0; + u_long iobase = dev->base_addr; + u16 tmp; + + if (chipType == LeMAC2) { + for (crc = 0x6a, j = 0; j < ETH_ALEN; j++) { + sd = dev->dev_addr[j] = eeprom_image[EEPROM_PADDR0 + j]; + outb(dev->dev_addr[j], EWRK3_PAR0 + j); + for (k = 0; k < 8; k++, sd >>= 1) { + lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7; + crc = (crc >> 1) + lfsr; + } + } + if (crc != eeprom_image[EEPROM_PA_CRC]) + status = -1; + } else { + for (i = 0, k = 0; i < ETH_ALEN;) { + k <<= 1; + if (k > 0xffff) + k -= 0xffff; + + k += (u_char) (tmp = inb(EWRK3_APROM)); + dev->dev_addr[i] = (u_char) tmp; + outb(dev->dev_addr[i], EWRK3_PAR0 + i); + i++; + k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8); + dev->dev_addr[i] = (u_char) tmp; + outb(dev->dev_addr[i], EWRK3_PAR0 + i); + i++; + + if (k > 0xffff) + k -= 0xffff; + } + if (k == 0xffff) + k = 0; + chksum = inb(EWRK3_APROM); + chksum |= (inb(EWRK3_APROM) << 8); + if (k != chksum) + status = -1; + } + + return status; +} + +/* + ** Look for a particular board name in the EISA configuration space + */ +static int __init EISA_signature(char *name, s32 eisa_id) +{ + u_long i; + char *signatures[] = EWRK3_SIGNATURE; + char ManCode[EWRK3_STRLEN]; + union { + s32 ID; + char Id[4]; + } Eisa; + int status = 0; + + *name = '\0'; + for (i = 0; i < 4; i++) { + Eisa.Id[i] = inb(eisa_id + i); + } + + ManCode[0] = (((Eisa.Id[0] >> 2) & 0x1f) + 0x40); + ManCode[1] = (((Eisa.Id[1] & 0xe0) >> 5) + ((Eisa.Id[0] & 0x03) << 3) + 0x40); + ManCode[2] = (((Eisa.Id[2] >> 4) & 0x0f) + 0x30); + ManCode[3] = ((Eisa.Id[2] & 0x0f) + 0x30); + ManCode[4] = (((Eisa.Id[3] >> 4) & 0x0f) + 0x30); + ManCode[5] = '\0'; + + for (i = 0; (*signatures[i] != '\0') && (*name == '\0'); i++) { + if (strstr(ManCode, signatures[i]) != NULL) { + strcpy(name, ManCode); + status = 1; + } + } + + return status; /* return the device name string */ +} + +static void ewrk3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + int fwrev = Read_EEPROM(dev->base_addr, EEPROM_REVLVL); + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->fw_version, "%d", fwrev); + strcpy(info->bus_info, "N/A"); + info->eedump_len = EEPROM_MAX; +} + +static int ewrk3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct ewrk3_private *lp = netdev_priv(dev); + unsigned long iobase = dev->base_addr; + u8 cr = inb(EWRK3_CR); + + switch (lp->adapter_name[4]) { + case '3': /* DE203 */ + ecmd->supported = SUPPORTED_BNC; + ecmd->port = PORT_BNC; + break; + + case '4': /* DE204 */ + ecmd->supported = SUPPORTED_TP; + ecmd->port = PORT_TP; + break; + + case '5': /* DE205 */ + ecmd->supported = SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_AUI; + ecmd->autoneg = !(cr & CR_APD); + /* + ** Port is only valid if autoneg is disabled + ** and even then we don't know if AUI is jumpered. + */ + if (!ecmd->autoneg) + ecmd->port = (cr & CR_PSEL) ? PORT_BNC : PORT_TP; + break; + } + + ecmd->supported |= SUPPORTED_10baseT_Half; + ethtool_cmd_speed_set(ecmd, SPEED_10); + ecmd->duplex = DUPLEX_HALF; + return 0; +} + +static int ewrk3_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct ewrk3_private *lp = netdev_priv(dev); + unsigned long iobase = dev->base_addr; + unsigned long flags; + u8 cr; + + /* DE205 is the only card with anything to set */ + if (lp->adapter_name[4] != '5') + return -EOPNOTSUPP; + + /* Sanity-check parameters */ + if (ecmd->speed != SPEED_10) + return -EINVAL; + if (ecmd->port != PORT_TP && ecmd->port != PORT_BNC) + return -EINVAL; /* AUI is not software-selectable */ + if (ecmd->transceiver != XCVR_INTERNAL) + return -EINVAL; + if (ecmd->duplex != DUPLEX_HALF) + return -EINVAL; + if (ecmd->phy_address != 0) + return -EINVAL; + + spin_lock_irqsave(&lp->hw_lock, flags); + cr = inb(EWRK3_CR); + + /* If Autoneg is set, change to Auto Port mode */ + /* Otherwise, disable Auto Port and set port explicitly */ + if (ecmd->autoneg) { + cr &= ~CR_APD; + } else { + cr |= CR_APD; + if (ecmd->port == PORT_TP) + cr &= ~CR_PSEL; /* Force TP */ + else + cr |= CR_PSEL; /* Force BNC */ + } + + /* Commit the changes */ + outb(cr, EWRK3_CR); + spin_unlock_irqrestore(&lp->hw_lock, flags); + return 0; +} + +static u32 ewrk3_get_link(struct net_device *dev) +{ + unsigned long iobase = dev->base_addr; + u8 cmr = inb(EWRK3_CMR); + /* DE203 has BNC only and link status does not apply */ + /* On DE204 this is always valid since TP is the only port. */ + /* On DE205 this reflects TP status even if BNC or AUI is selected. */ + return !(cmr & CMR_LINK); +} + +static int ewrk3_set_phys_id(struct net_device *dev, + enum ethtool_phys_id_state state) +{ + struct ewrk3_private *lp = netdev_priv(dev); + unsigned long iobase = dev->base_addr; + u8 cr; + + spin_lock_irq(&lp->hw_lock); + + switch (state) { + case ETHTOOL_ID_ACTIVE: + /* Prevent ISR from twiddling the LED */ + lp->led_mask = 0; + spin_unlock_irq(&lp->hw_lock); + return 2; /* cycle on/off twice per second */ + + case ETHTOOL_ID_ON: + cr = inb(EWRK3_CR); + outb(cr | CR_LED, EWRK3_CR); + break; + + case ETHTOOL_ID_OFF: + cr = inb(EWRK3_CR); + outb(cr & ~CR_LED, EWRK3_CR); + break; + + case ETHTOOL_ID_INACTIVE: + lp->led_mask = CR_LED; + cr = inb(EWRK3_CR); + outb(cr & ~CR_LED, EWRK3_CR); + } + spin_unlock_irq(&lp->hw_lock); + + return 0; +} + +static const struct ethtool_ops ethtool_ops_203 = { + .get_drvinfo = ewrk3_get_drvinfo, + .get_settings = ewrk3_get_settings, + .set_settings = ewrk3_set_settings, + .set_phys_id = ewrk3_set_phys_id, +}; + +static const struct ethtool_ops ethtool_ops = { + .get_drvinfo = ewrk3_get_drvinfo, + .get_settings = ewrk3_get_settings, + .set_settings = ewrk3_set_settings, + .get_link = ewrk3_get_link, + .set_phys_id = ewrk3_set_phys_id, +}; + +/* + ** Perform IOCTL call functions here. Some are privileged operations and the + ** effective uid is checked in those cases. + */ +static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct ewrk3_private *lp = netdev_priv(dev); + struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_ifru; + u_long iobase = dev->base_addr; + int i, j, status = 0; + u_char csr; + unsigned long flags; + union ewrk3_addr { + u_char addr[HASH_TABLE_LEN * ETH_ALEN]; + u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; + }; + + union ewrk3_addr *tmp; + + /* All we handle are private IOCTLs */ + if (cmd != EWRK3IOCTL) + return -EOPNOTSUPP; + + tmp = kmalloc(sizeof(union ewrk3_addr), GFP_KERNEL); + if(tmp==NULL) + return -ENOMEM; + + switch (ioc->cmd) { + case EWRK3_GET_HWADDR: /* Get the hardware address */ + for (i = 0; i < ETH_ALEN; i++) { + tmp->addr[i] = dev->dev_addr[i]; + } + ioc->len = ETH_ALEN; + if (copy_to_user(ioc->data, tmp->addr, ioc->len)) + status = -EFAULT; + break; + + case EWRK3_SET_HWADDR: /* Set the hardware address */ + if (capable(CAP_NET_ADMIN)) { + spin_lock_irqsave(&lp->hw_lock, flags); + csr = inb(EWRK3_CSR); + csr |= (CSR_TXD | CSR_RXD); + outb(csr, EWRK3_CSR); /* Disable the TX and RX */ + spin_unlock_irqrestore(&lp->hw_lock, flags); + + if (copy_from_user(tmp->addr, ioc->data, ETH_ALEN)) { + status = -EFAULT; + break; + } + spin_lock_irqsave(&lp->hw_lock, flags); + for (i = 0; i < ETH_ALEN; i++) { + dev->dev_addr[i] = tmp->addr[i]; + outb(tmp->addr[i], EWRK3_PAR0 + i); + } + + csr = inb(EWRK3_CSR); + csr &= ~(CSR_TXD | CSR_RXD); /* Enable the TX and RX */ + outb(csr, EWRK3_CSR); + spin_unlock_irqrestore(&lp->hw_lock, flags); + } else { + status = -EPERM; + } + + break; + case EWRK3_SET_PROM: /* Set Promiscuous Mode */ + if (capable(CAP_NET_ADMIN)) { + spin_lock_irqsave(&lp->hw_lock, flags); + csr = inb(EWRK3_CSR); + csr |= CSR_PME; + csr &= ~CSR_MCE; + outb(csr, EWRK3_CSR); + spin_unlock_irqrestore(&lp->hw_lock, flags); + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */ + if (capable(CAP_NET_ADMIN)) { + spin_lock_irqsave(&lp->hw_lock, flags); + csr = inb(EWRK3_CSR); + csr &= ~CSR_PME; + outb(csr, EWRK3_CSR); + spin_unlock_irqrestore(&lp->hw_lock, flags); + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_MCA: /* Get the multicast address table */ + spin_lock_irqsave(&lp->hw_lock, flags); + if (lp->shmem_length == IO_ONLY) { + outb(0, EWRK3_IOPR); + outw(PAGE0_HTE, EWRK3_PIR1); + for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { + tmp->addr[i] = inb(EWRK3_DATA); + } + } else { + outb(0, EWRK3_MPR); + memcpy_fromio(tmp->addr, lp->shmem + PAGE0_HTE, (HASH_TABLE_LEN >> 3)); + } + spin_unlock_irqrestore(&lp->hw_lock, flags); + + ioc->len = (HASH_TABLE_LEN >> 3); + if (copy_to_user(ioc->data, tmp->addr, ioc->len)) + status = -EFAULT; + + break; + case EWRK3_SET_MCA: /* Set a multicast address */ + if (capable(CAP_NET_ADMIN)) { + if (ioc->len > HASH_TABLE_LEN) { + status = -EINVAL; + break; + } + if (copy_from_user(tmp->addr, ioc->data, ETH_ALEN * ioc->len)) { + status = -EFAULT; + break; + } + set_multicast_list(dev); + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_MCA: /* Clear all multicast addresses */ + if (capable(CAP_NET_ADMIN)) { + set_multicast_list(dev); + } else { + status = -EPERM; + } + + break; + case EWRK3_MCA_EN: /* Enable multicast addressing */ + if (capable(CAP_NET_ADMIN)) { + spin_lock_irqsave(&lp->hw_lock, flags); + csr = inb(EWRK3_CSR); + csr |= CSR_MCE; + csr &= ~CSR_PME; + outb(csr, EWRK3_CSR); + spin_unlock_irqrestore(&lp->hw_lock, flags); + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_STATS: { /* Get the driver statistics */ + struct ewrk3_stats *tmp_stats = + kmalloc(sizeof(lp->pktStats), GFP_KERNEL); + if (!tmp_stats) { + status = -ENOMEM; + break; + } + + spin_lock_irqsave(&lp->hw_lock, flags); + memcpy(tmp_stats, &lp->pktStats, sizeof(lp->pktStats)); + spin_unlock_irqrestore(&lp->hw_lock, flags); + + ioc->len = sizeof(lp->pktStats); + if (copy_to_user(ioc->data, tmp_stats, sizeof(lp->pktStats))) + status = -EFAULT; + kfree(tmp_stats); + break; + } + case EWRK3_CLR_STATS: /* Zero out the driver statistics */ + if (capable(CAP_NET_ADMIN)) { + spin_lock_irqsave(&lp->hw_lock, flags); + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + spin_unlock_irqrestore(&lp->hw_lock,flags); + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_CSR: /* Get the CSR Register contents */ + tmp->addr[0] = inb(EWRK3_CSR); + ioc->len = 1; + if (copy_to_user(ioc->data, tmp->addr, ioc->len)) + status = -EFAULT; + break; + case EWRK3_SET_CSR: /* Set the CSR Register contents */ + if (capable(CAP_NET_ADMIN)) { + if (copy_from_user(tmp->addr, ioc->data, 1)) { + status = -EFAULT; + break; + } + outb(tmp->addr[0], EWRK3_CSR); + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_EEPROM: /* Get the EEPROM contents */ + if (capable(CAP_NET_ADMIN)) { + for (i = 0; i < (EEPROM_MAX >> 1); i++) { + tmp->val[i] = (short) Read_EEPROM(iobase, i); + } + i = EEPROM_MAX; + tmp->addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */ + for (j = 0; j < ETH_ALEN; j++) { + tmp->addr[i++] = inb(EWRK3_PAR0 + j); + } + ioc->len = EEPROM_MAX + 1 + ETH_ALEN; + if (copy_to_user(ioc->data, tmp->addr, ioc->len)) + status = -EFAULT; + } else { + status = -EPERM; + } + + break; + case EWRK3_SET_EEPROM: /* Set the EEPROM contents */ + if (capable(CAP_NET_ADMIN)) { + if (copy_from_user(tmp->addr, ioc->data, EEPROM_MAX)) { + status = -EFAULT; + break; + } + for (i = 0; i < (EEPROM_MAX >> 1); i++) { + Write_EEPROM(tmp->val[i], iobase, i); + } + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_CMR: /* Get the CMR Register contents */ + tmp->addr[0] = inb(EWRK3_CMR); + ioc->len = 1; + if (copy_to_user(ioc->data, tmp->addr, ioc->len)) + status = -EFAULT; + break; + case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */ + if (capable(CAP_NET_ADMIN)) { + lp->txc = 1; + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */ + if (capable(CAP_NET_ADMIN)) { + lp->txc = 0; + } else { + status = -EPERM; + } + + break; + default: + status = -EOPNOTSUPP; + } + kfree(tmp); + return status; +} + +#ifdef MODULE +static struct net_device *ewrk3_devs[MAX_NUM_EWRK3S]; +static int ndevs; +static int io[MAX_NUM_EWRK3S+1] = { 0x300, 0, }; + +/* '21' below should really be 'MAX_NUM_EWRK3S' */ +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(io, "EtherWORKS 3 I/O base address(es)"); +MODULE_PARM_DESC(irq, "EtherWORKS 3 IRQ number(s)"); + +static __exit void ewrk3_exit_module(void) +{ + int i; + + for( i=0; ibase_addr, EWRK3_TOTAL_SIZE); + iounmap(lp->shmem); + free_netdev(dev); + } +} + +static __init int ewrk3_init_module(void) +{ + int i=0; + + while( io[i] && irq[i] ) { + struct net_device *dev + = alloc_etherdev(sizeof(struct ewrk3_private)); + + if (!dev) + break; + + if (ewrk3_probe1(dev, io[i], irq[i]) != 0) { + free_netdev(dev); + break; + } + + ewrk3_devs[ndevs++] = dev; + i++; + } + + return ndevs ? 0 : -EIO; +} + + +/* Hack for breakage in new module stuff */ +module_exit(ewrk3_exit_module); +module_init(ewrk3_init_module); +#endif /* MODULE */ +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/dec/ewrk3.h b/drivers/net/ethernet/dec/ewrk3.h new file mode 100644 index 000000000000..8e0ee906567b --- /dev/null +++ b/drivers/net/ethernet/dec/ewrk3.h @@ -0,0 +1,322 @@ +/* + 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. + + The author may be reached as davies@wanton.lkg.dec.com or Digital + Equipment Corporation, 550 King Street, Littleton MA 01460. + + ========================================================================= +*/ + +/* +** I/O Address Register Map +*/ +#define EWRK3_CSR iobase+0x00 /* Control and Status Register */ +#define EWRK3_CR iobase+0x01 /* Control Register */ +#define EWRK3_ICR iobase+0x02 /* Interrupt Control Register */ +#define EWRK3_TSR iobase+0x03 /* Transmit Status Register */ +#define EWRK3_RSVD1 iobase+0x04 /* RESERVED */ +#define EWRK3_RSVD2 iobase+0x05 /* RESERVED */ +#define EWRK3_FMQ iobase+0x06 /* Free Memory Queue */ +#define EWRK3_FMQC iobase+0x07 /* Free Memory Queue Counter */ +#define EWRK3_RQ iobase+0x08 /* Receive Queue */ +#define EWRK3_RQC iobase+0x09 /* Receive Queue Counter */ +#define EWRK3_TQ iobase+0x0a /* Transmit Queue */ +#define EWRK3_TQC iobase+0x0b /* Transmit Queue Counter */ +#define EWRK3_TDQ iobase+0x0c /* Transmit Done Queue */ +#define EWRK3_TDQC iobase+0x0d /* Transmit Done Queue Counter */ +#define EWRK3_PIR1 iobase+0x0e /* Page Index Register 1 */ +#define EWRK3_PIR2 iobase+0x0f /* Page Index Register 2 */ +#define EWRK3_DATA iobase+0x10 /* Data Register */ +#define EWRK3_IOPR iobase+0x11 /* I/O Page Register */ +#define EWRK3_IOBR iobase+0x12 /* I/O Base Register */ +#define EWRK3_MPR iobase+0x13 /* Memory Page Register */ +#define EWRK3_MBR iobase+0x14 /* Memory Base Register */ +#define EWRK3_APROM iobase+0x15 /* Address PROM */ +#define EWRK3_EPROM1 iobase+0x16 /* EEPROM Data Register 1 */ +#define EWRK3_EPROM2 iobase+0x17 /* EEPROM Data Register 2 */ +#define EWRK3_PAR0 iobase+0x18 /* Physical Address Register 0 */ +#define EWRK3_PAR1 iobase+0x19 /* Physical Address Register 1 */ +#define EWRK3_PAR2 iobase+0x1a /* Physical Address Register 2 */ +#define EWRK3_PAR3 iobase+0x1b /* Physical Address Register 3 */ +#define EWRK3_PAR4 iobase+0x1c /* Physical Address Register 4 */ +#define EWRK3_PAR5 iobase+0x1d /* Physical Address Register 5 */ +#define EWRK3_CMR iobase+0x1e /* Configuration/Management Register */ + +/* +** Control Page Map +*/ +#define PAGE0_FMQ 0x000 /* Free Memory Queue */ +#define PAGE0_RQ 0x080 /* Receive Queue */ +#define PAGE0_TQ 0x100 /* Transmit Queue */ +#define PAGE0_TDQ 0x180 /* Transmit Done Queue */ +#define PAGE0_HTE 0x200 /* Hash Table Entries */ +#define PAGE0_RSVD 0x240 /* RESERVED */ +#define PAGE0_USRD 0x600 /* User Data */ + +/* +** Control and Status Register bit definitions (EWRK3_CSR) +*/ +#define CSR_RA 0x80 /* Runt Accept */ +#define CSR_PME 0x40 /* Promiscuous Mode Enable */ +#define CSR_MCE 0x20 /* Multicast Enable */ +#define CSR_TNE 0x08 /* TX Done Queue Not Empty */ +#define CSR_RNE 0x04 /* RX Queue Not Empty */ +#define CSR_TXD 0x02 /* TX Disable */ +#define CSR_RXD 0x01 /* RX Disable */ + +/* +** Control Register bit definitions (EWRK3_CR) +*/ +#define CR_APD 0x80 /* Auto Port Disable */ +#define CR_PSEL 0x40 /* Port Select (0->TP port) */ +#define CR_LBCK 0x20 /* LoopBaCK enable */ +#define CR_FDUP 0x10 /* Full DUPlex enable */ +#define CR_FBUS 0x08 /* Fast BUS enable (ISA clk > 8.33MHz) */ +#define CR_EN_16 0x04 /* ENable 16 bit memory accesses */ +#define CR_LED 0x02 /* LED (1-> turn on) */ + +/* +** Interrupt Control Register bit definitions (EWRK3_ICR) +*/ +#define ICR_IE 0x80 /* Interrupt Enable */ +#define ICR_IS 0x60 /* Interrupt Selected */ +#define ICR_TNEM 0x08 /* TNE Mask (0->mask) */ +#define ICR_RNEM 0x04 /* RNE Mask (0->mask) */ +#define ICR_TXDM 0x02 /* TXD Mask (0->mask) */ +#define ICR_RXDM 0x01 /* RXD Mask (0->mask) */ + +/* +** Transmit Status Register bit definitions (EWRK3_TSR) +*/ +#define TSR_NCL 0x80 /* No Carrier Loopback */ +#define TSR_ID 0x40 /* Initially Deferred */ +#define TSR_LCL 0x20 /* Late CoLlision */ +#define TSR_ECL 0x10 /* Excessive CoLlisions */ +#define TSR_RCNTR 0x0f /* Retries CouNTeR */ + +/* +** I/O Page Register bit definitions (EWRK3_IOPR) +*/ +#define EEPROM_INIT 0xc0 /* EEPROM INIT command */ +#define EEPROM_WR_EN 0xc8 /* EEPROM WRITE ENABLE command */ +#define EEPROM_WR 0xd0 /* EEPROM WRITE command */ +#define EEPROM_WR_DIS 0xd8 /* EEPROM WRITE DISABLE command */ +#define EEPROM_RD 0xe0 /* EEPROM READ command */ + +/* +** I/O Base Register bit definitions (EWRK3_IOBR) +*/ +#define EISA_REGS_EN 0x20 /* Enable EISA ID and Control Registers */ +#define EISA_IOB 0x1f /* Compare bits for I/O Base Address */ + +/* +** I/O Configuration/Management Register bit definitions (EWRK3_CMR) +*/ +#define CMR_RA 0x80 /* Read Ahead */ +#define CMR_WB 0x40 /* Write Behind */ +#define CMR_LINK 0x20 /* 0->TP */ +#define CMR_POLARITY 0x10 /* Informational */ +#define CMR_NO_EEPROM 0x0c /* NO_EEPROM<1:0> pin status */ +#define CMR_HS 0x08 /* Hard Strapped pin status (LeMAC2) */ +#define CMR_PNP 0x04 /* Plug 'n Play */ +#define CMR_DRAM 0x02 /* 0-> 1DRAM, 1-> 2 DRAM on board */ +#define CMR_0WS 0x01 /* Zero Wait State */ + +/* +** MAC Receive Status Register bit definitions +*/ + +#define R_ROK 0x80 /* Receive OK summary */ +#define R_IAM 0x10 /* Individual Address Match */ +#define R_MCM 0x08 /* MultiCast Match */ +#define R_DBE 0x04 /* Dribble Bit Error */ +#define R_CRC 0x02 /* CRC error */ +#define R_PLL 0x01 /* Phase Lock Lost */ + +/* +** MAC Transmit Control Register bit definitions +*/ + +#define TCR_SQEE 0x40 /* SQE Enable - look for heartbeat */ +#define TCR_SED 0x20 /* Stop when Error Detected */ +#define TCR_QMODE 0x10 /* Q_MODE */ +#define TCR_LAB 0x08 /* Less Aggressive Backoff */ +#define TCR_PAD 0x04 /* PAD Runt Packets */ +#define TCR_IFC 0x02 /* Insert Frame Check */ +#define TCR_ISA 0x01 /* Insert Source Address */ + +/* +** MAC Transmit Status Register bit definitions +*/ + +#define T_VSTS 0x80 /* Valid STatuS */ +#define T_CTU 0x40 /* Cut Through Used */ +#define T_SQE 0x20 /* Signal Quality Error */ +#define T_NCL 0x10 /* No Carrier Loopback */ +#define T_LCL 0x08 /* Late Collision */ +#define T_ID 0x04 /* Initially Deferred */ +#define T_COLL 0x03 /* COLLision status */ +#define T_XCOLL 0x03 /* Excessive Collisions */ +#define T_MCOLL 0x02 /* Multiple Collisions */ +#define T_OCOLL 0x01 /* One Collision */ +#define T_NOCOLL 0x00 /* No Collisions */ +#define T_XUR 0x03 /* Excessive Underruns */ +#define T_TXE 0x7f /* TX Errors */ + +/* +** EISA Configuration Register bit definitions +*/ + +#define EISA_ID iobase + 0x0c80 /* EISA ID Registers */ +#define EISA_ID0 iobase + 0x0c80 /* EISA ID Register 0 */ +#define EISA_ID1 iobase + 0x0c81 /* EISA ID Register 1 */ +#define EISA_ID2 iobase + 0x0c82 /* EISA ID Register 2 */ +#define EISA_ID3 iobase + 0x0c83 /* EISA ID Register 3 */ +#define EISA_CR iobase + 0x0c84 /* EISA Control Register */ + +/* +** EEPROM BYTES +*/ +#define EEPROM_MEMB 0x00 +#define EEPROM_IOB 0x01 +#define EEPROM_EISA_ID0 0x02 +#define EEPROM_EISA_ID1 0x03 +#define EEPROM_EISA_ID2 0x04 +#define EEPROM_EISA_ID3 0x05 +#define EEPROM_MISC0 0x06 +#define EEPROM_MISC1 0x07 +#define EEPROM_PNAME7 0x08 +#define EEPROM_PNAME6 0x09 +#define EEPROM_PNAME5 0x0a +#define EEPROM_PNAME4 0x0b +#define EEPROM_PNAME3 0x0c +#define EEPROM_PNAME2 0x0d +#define EEPROM_PNAME1 0x0e +#define EEPROM_PNAME0 0x0f +#define EEPROM_SWFLAGS 0x10 +#define EEPROM_HWCAT 0x11 +#define EEPROM_NETMAN2 0x12 +#define EEPROM_REVLVL 0x13 +#define EEPROM_NETMAN0 0x14 +#define EEPROM_NETMAN1 0x15 +#define EEPROM_CHIPVER 0x16 +#define EEPROM_SETUP 0x17 +#define EEPROM_PADDR0 0x18 +#define EEPROM_PADDR1 0x19 +#define EEPROM_PADDR2 0x1a +#define EEPROM_PADDR3 0x1b +#define EEPROM_PADDR4 0x1c +#define EEPROM_PADDR5 0x1d +#define EEPROM_PA_CRC 0x1e +#define EEPROM_CHKSUM 0x1f + +/* +** EEPROM bytes for checksumming +*/ +#define EEPROM_MAX 32 /* bytes */ + +/* +** EEPROM MISCELLANEOUS FLAGS +*/ +#define RBE_SHADOW 0x0100 /* Remote Boot Enable Shadow */ +#define READ_AHEAD 0x0080 /* Read Ahead feature */ +#define IRQ_SEL2 0x0070 /* IRQ line selection (LeMAC2) */ +#define IRQ_SEL 0x0060 /* IRQ line selection */ +#define FAST_BUS 0x0008 /* ISA Bus speeds > 8.33MHz */ +#define ENA_16 0x0004 /* Enables 16 bit memory transfers */ +#define WRITE_BEHIND 0x0002 /* Write Behind feature */ +#define _0WS_ENA 0x0001 /* Zero Wait State Enable */ + +/* +** EEPROM NETWORK MANAGEMENT FLAGS +*/ +#define NETMAN_POL 0x04 /* Polarity defeat */ +#define NETMAN_LINK 0x02 /* Link defeat */ +#define NETMAN_CCE 0x01 /* Custom Counters Enable */ + +/* +** EEPROM SW FLAGS +*/ +#define SW_SQE 0x10 /* Signal Quality Error */ +#define SW_LAB 0x08 /* Less Aggressive Backoff */ +#define SW_INIT 0x04 /* Initialized */ +#define SW_TIMEOUT 0x02 /* 0:2.5 mins, 1: 30 secs */ +#define SW_REMOTE 0x01 /* Remote Boot Enable -> 1 */ + +/* +** EEPROM SETUP FLAGS +*/ +#define SETUP_APD 0x80 /* AutoPort Disable */ +#define SETUP_PS 0x40 /* Port Select */ +#define SETUP_MP 0x20 /* MultiPort */ +#define SETUP_1TP 0x10 /* 1 port, TP */ +#define SETUP_1COAX 0x00 /* 1 port, Coax */ +#define SETUP_DRAM 0x02 /* Number of DRAMS on board */ + +/* +** EEPROM MANAGEMENT FLAGS +*/ +#define MGMT_CCE 0x01 /* Custom Counters Enable */ + +/* +** EEPROM VERSIONS +*/ +#define LeMAC 0x11 +#define LeMAC2 0x12 + +/* +** Miscellaneous +*/ + +#define EEPROM_WAIT_TIME 1000 /* Number of microseconds */ +#define EISA_EN 0x0001 /* Enable EISA bus buffers */ + +#define HASH_TABLE_LEN 512 /* Bits */ + +#define XCT 0x80 /* Transmit Cut Through */ +#define PRELOAD 16 /* 4 long words */ + +#define MASK_INTERRUPTS 1 +#define UNMASK_INTERRUPTS 0 + +#define EEPROM_OFFSET(a) ((u_short)((u_long)(a))) + +/* +** Include the IOCTL stuff +*/ +#include + +#define EWRK3IOCTL SIOCDEVPRIVATE + +struct ewrk3_ioctl { + unsigned short cmd; /* Command to run */ + unsigned short len; /* Length of the data buffer */ + unsigned char __user *data; /* Pointer to the data buffer */ +}; + +/* +** Recognised commands for the driver +*/ +#define EWRK3_GET_HWADDR 0x01 /* Get the hardware address */ +#define EWRK3_SET_HWADDR 0x02 /* Get the hardware address */ +#define EWRK3_SET_PROM 0x03 /* Set Promiscuous Mode */ +#define EWRK3_CLR_PROM 0x04 /* Clear Promiscuous Mode */ +#define EWRK3_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ +#define EWRK3_GET_MCA 0x06 /* Get a multicast address */ +#define EWRK3_SET_MCA 0x07 /* Set a multicast address */ +#define EWRK3_CLR_MCA 0x08 /* Clear a multicast address */ +#define EWRK3_MCA_EN 0x09 /* Enable a multicast address group */ +#define EWRK3_GET_STATS 0x0a /* Get the driver statistics */ +#define EWRK3_CLR_STATS 0x0b /* Zero out the driver statistics */ +#define EWRK3_GET_CSR 0x0c /* Get the CSR Register contents */ +#define EWRK3_SET_CSR 0x0d /* Set the CSR Register contents */ +#define EWRK3_GET_EEPROM 0x0e /* Get the EEPROM contents */ +#define EWRK3_SET_EEPROM 0x0f /* Set the EEPROM contents */ +#define EWRK3_GET_CMR 0x10 /* Get the CMR Register contents */ +#define EWRK3_CLR_TX_CUT_THRU 0x11 /* Clear the TX cut through mode */ +#define EWRK3_SET_TX_CUT_THRU 0x12 /* Set the TX cut through mode */ diff --git a/drivers/net/ethernet/dec/tulip/21142.c b/drivers/net/ethernet/dec/tulip/21142.c new file mode 100644 index 000000000000..092c3faa882a --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/21142.c @@ -0,0 +1,260 @@ +/* + drivers/net/tulip/21142.c + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + + DC21143 manual "21143 PCI/CardBus 10/100Mb/s Ethernet LAN Controller + Hardware Reference Manual" is currently available at : + http://developer.intel.com/design/network/manuals/278074.htm + + Please submit bugs to http://bugzilla.kernel.org/ . +*/ + +#include +#include "tulip.h" + + +static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; +u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, }; +static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + + +/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list + of available transceivers. */ +void t21142_media_task(struct work_struct *work) +{ + struct tulip_private *tp = + container_of(work, struct tulip_private, media_work); + struct net_device *dev = tp->dev; + void __iomem *ioaddr = tp->base_addr; + int csr12 = ioread32(ioaddr + CSR12); + int next_tick = 60*HZ; + int new_csr6 = 0; + int csr14 = ioread32(ioaddr + CSR14); + + /* CSR12[LS10,LS100] are not reliable during autonegotiation */ + if ((csr14 & 0x80) && (csr12 & 0x7000) != 0x5000) + csr12 |= 6; + if (tulip_debug > 2) + dev_info(&dev->dev, "21143 negotiation status %08x, %s\n", + csr12, medianame[dev->if_port]); + if (tulip_media_cap[dev->if_port] & MediaIsMII) { + if (tulip_check_duplex(dev) < 0) { + netif_carrier_off(dev); + next_tick = 3*HZ; + } else { + netif_carrier_on(dev); + next_tick = 60*HZ; + } + } else if (tp->nwayset) { + /* Don't screw up a negotiated session! */ + if (tulip_debug > 1) + dev_info(&dev->dev, + "Using NWay-set %s media, csr12 %08x\n", + medianame[dev->if_port], csr12); + } else if (tp->medialock) { + ; + } else if (dev->if_port == 3) { + if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ + if (tulip_debug > 1) + dev_info(&dev->dev, + "No 21143 100baseTx link beat, %08x, trying NWay\n", + csr12); + t21142_start_nway(dev); + next_tick = 3*HZ; + } + } else if ((csr12 & 0x7000) != 0x5000) { + /* Negotiation failed. Search media types. */ + if (tulip_debug > 1) + dev_info(&dev->dev, + "21143 negotiation failed, status %08x\n", + csr12); + if (!(csr12 & 4)) { /* 10mbps link beat good. */ + new_csr6 = 0x82420000; + dev->if_port = 0; + iowrite32(0, ioaddr + CSR13); + iowrite32(0x0003FFFF, ioaddr + CSR14); + iowrite16(t21142_csr15[dev->if_port], ioaddr + CSR15); + iowrite32(t21142_csr13[dev->if_port], ioaddr + CSR13); + } else { + /* Select 100mbps port to check for link beat. */ + new_csr6 = 0x83860000; + dev->if_port = 3; + iowrite32(0, ioaddr + CSR13); + iowrite32(0x0003FFFF, ioaddr + CSR14); + iowrite16(8, ioaddr + CSR15); + iowrite32(1, ioaddr + CSR13); + } + if (tulip_debug > 1) + dev_info(&dev->dev, "Testing new 21143 media %s\n", + medianame[dev->if_port]); + if (new_csr6 != (tp->csr6 & ~0x00D5)) { + tp->csr6 &= 0x00D5; + tp->csr6 |= new_csr6; + iowrite32(0x0301, ioaddr + CSR12); + tulip_restart_rxtx(tp); + } + next_tick = 3*HZ; + } + + /* mod_timer synchronizes us with potential add_timer calls + * from interrupts. + */ + mod_timer(&tp->timer, RUN_AT(next_tick)); +} + + +void t21142_start_nway(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int csr14 = ((tp->sym_advertise & 0x0780) << 9) | + ((tp->sym_advertise & 0x0020) << 1) | 0xffbf; + + dev->if_port = 0; + tp->nway = tp->mediasense = 1; + tp->nwayset = tp->lpar = 0; + if (tulip_debug > 1) + netdev_dbg(dev, "Restarting 21143 autonegotiation, csr14=%08x\n", + csr14); + iowrite32(0x0001, ioaddr + CSR13); + udelay(100); + iowrite32(csr14, ioaddr + CSR14); + tp->csr6 = 0x82420000 | (tp->sym_advertise & 0x0040 ? FullDuplex : 0); + iowrite32(tp->csr6, ioaddr + CSR6); + if (tp->mtable && tp->mtable->csr15dir) { + iowrite32(tp->mtable->csr15dir, ioaddr + CSR15); + iowrite32(tp->mtable->csr15val, ioaddr + CSR15); + } else + iowrite16(0x0008, ioaddr + CSR15); + iowrite32(0x1301, ioaddr + CSR12); /* Trigger NWAY. */ +} + + + +void t21142_lnk_change(struct net_device *dev, int csr5) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int csr12 = ioread32(ioaddr + CSR12); + int csr14 = ioread32(ioaddr + CSR14); + + /* CSR12[LS10,LS100] are not reliable during autonegotiation */ + if ((csr14 & 0x80) && (csr12 & 0x7000) != 0x5000) + csr12 |= 6; + if (tulip_debug > 1) + dev_info(&dev->dev, + "21143 link status interrupt %08x, CSR5 %x, %08x\n", + csr12, csr5, csr14); + + /* If NWay finished and we have a negotiated partner capability. */ + if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) { + int setup_done = 0; + int negotiated = tp->sym_advertise & (csr12 >> 16); + tp->lpar = csr12 >> 16; + tp->nwayset = 1; + /* If partner cannot negotiate, it is 10Mbps Half Duplex */ + if (!(csr12 & 0x8000)) dev->if_port = 0; + else if (negotiated & 0x0100) dev->if_port = 5; + else if (negotiated & 0x0080) dev->if_port = 3; + else if (negotiated & 0x0040) dev->if_port = 4; + else if (negotiated & 0x0020) dev->if_port = 0; + else { + tp->nwayset = 0; + if ((csr12 & 2) == 0 && (tp->sym_advertise & 0x0180)) + dev->if_port = 3; + } + tp->full_duplex = (tulip_media_cap[dev->if_port] & MediaAlwaysFD) ? 1:0; + + if (tulip_debug > 1) { + if (tp->nwayset) + dev_info(&dev->dev, + "Switching to %s based on link negotiation %04x & %04x = %04x\n", + medianame[dev->if_port], + tp->sym_advertise, tp->lpar, + negotiated); + else + dev_info(&dev->dev, + "Autonegotiation failed, using %s, link beat status %04x\n", + medianame[dev->if_port], csr12); + } + + if (tp->mtable) { + int i; + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == dev->if_port) { + int startup = ! ((tp->chip_id == DC21143 && (tp->revision == 48 || tp->revision == 65))); + tp->cur_index = i; + tulip_select_media(dev, startup); + setup_done = 1; + break; + } + } + if ( ! setup_done) { + tp->csr6 = (dev->if_port & 1 ? 0x838E0000 : 0x82420000) | (tp->csr6 & 0x20ff); + if (tp->full_duplex) + tp->csr6 |= 0x0200; + iowrite32(1, ioaddr + CSR13); + } +#if 0 /* Restart shouldn't be needed. */ + iowrite32(tp->csr6 | RxOn, ioaddr + CSR6); + if (tulip_debug > 2) + netdev_dbg(dev, " Restarting Tx and Rx, CSR5 is %08x\n", + ioread32(ioaddr + CSR5)); +#endif + tulip_start_rxtx(tp); + if (tulip_debug > 2) + netdev_dbg(dev, " Setting CSR6 %08x/%x CSR12 %08x\n", + tp->csr6, ioread32(ioaddr + CSR6), + ioread32(ioaddr + CSR12)); + } else if ((tp->nwayset && (csr5 & 0x08000000) && + (dev->if_port == 3 || dev->if_port == 5) && + (csr12 & 2) == 2) || + (tp->nway && (csr5 & (TPLnkFail)))) { + /* Link blew? Maybe restart NWay. */ + del_timer_sync(&tp->timer); + t21142_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } else if (dev->if_port == 3 || dev->if_port == 5) { + if (tulip_debug > 1) + dev_info(&dev->dev, "21143 %s link beat %s\n", + medianame[dev->if_port], + (csr12 & 2) ? "failed" : "good"); + if ((csr12 & 2) && ! tp->medialock) { + del_timer_sync(&tp->timer); + t21142_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } else if (dev->if_port == 5) + iowrite32(csr14 & ~0x080, ioaddr + CSR14); + } else if (dev->if_port == 0 || dev->if_port == 4) { + if ((csr12 & 4) == 0) + dev_info(&dev->dev, "21143 10baseT link beat good\n"); + } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ + if (tulip_debug) + dev_info(&dev->dev, "21143 10mbps sensed media\n"); + dev->if_port = 0; + } else if (tp->nwayset) { + if (tulip_debug) + dev_info(&dev->dev, "21143 using NWay-set %s, csr6 %08x\n", + medianame[dev->if_port], tp->csr6); + } else { /* 100mbps link beat good. */ + if (tulip_debug) + dev_info(&dev->dev, "21143 100baseTx sensed media\n"); + dev->if_port = 3; + tp->csr6 = 0x838E0000 | (tp->csr6 & 0x20ff); + iowrite32(0x0003FF7F, ioaddr + CSR14); + iowrite32(0x0301, ioaddr + CSR12); + tulip_restart_rxtx(tp); + } +} + + diff --git a/drivers/net/ethernet/dec/tulip/Kconfig b/drivers/net/ethernet/dec/tulip/Kconfig new file mode 100644 index 000000000000..f6af772b12c9 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/Kconfig @@ -0,0 +1,171 @@ +# +# Tulip family network device configuration +# + +config NET_TULIP + bool "DEC - Tulip devices" + depends on (PCI || EISA || CARDBUS) + ---help--- + This selects the "Tulip" family of EISA/PCI network cards. + +if NET_TULIP + +config DE2104X + tristate "Early DECchip Tulip (dc2104x) PCI support" + depends on PCI + select CRC32 + ---help--- + This driver is developed for the SMC EtherPower series Ethernet + cards and also works with cards based on the DECchip + 21040 (Tulip series) chips. Some LinkSys PCI cards are + of this type. (If your card is NOT SMC EtherPower 10/100 PCI + (smc9332dst), you can also try the driver for "Generic DECchip" + cards, below. However, most people with a network card of this type + will say Y here.) Do read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module will + be called de2104x. + +config DE2104X_DSL + int "Descriptor Skip Length in 32 bit longwords" + depends on DE2104X + range 0 31 + default 0 + ---help--- + Setting this value allows to align ring buffer descriptors into their + own cache lines. Value of 4 corresponds to the typical 32 byte line + (the descriptor is 16 bytes). This is necessary on systems that lack + cache coherence, an example is PowerMac 5500. Otherwise 0 is safe. + Default is 0, and range is 0 to 31. + +config TULIP + tristate "DECchip Tulip (dc2114x) PCI support" + depends on PCI + select CRC32 + ---help--- + This driver is developed for the SMC EtherPower series Ethernet + cards and also works with cards based on the DECchip + 21140 (Tulip series) chips. Some LinkSys PCI cards are + of this type. (If your card is NOT SMC EtherPower 10/100 PCI + (smc9332dst), you can also try the driver for "Generic DECchip" + cards, above. However, most people with a network card of this type + will say Y here.) Do read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module will + be called tulip. + +config TULIP_MWI + bool "New bus configuration (EXPERIMENTAL)" + depends on TULIP && EXPERIMENTAL + ---help--- + This configures your Tulip card specifically for the card and + system cache line size type you are using. + + This is experimental code, not yet tested on many boards. + + If unsure, say N. + +config TULIP_MMIO + bool "Use PCI shared mem for NIC registers" + depends on TULIP + ---help--- + Use PCI shared memory for the NIC registers, rather than going through + the Tulip's PIO (programmed I/O ports). Faster, but could produce + obscure bugs if your mainboard has memory controller timing issues. + If in doubt, say N. + +config TULIP_NAPI + bool "Use RX polling (NAPI)" + depends on TULIP + ---help--- + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config TULIP_NAPI_HW_MITIGATION + bool "Use Interrupt Mitigation" + depends on TULIP_NAPI + ---help--- + Use HW to reduce RX interrupts. Not strictly necessary since NAPI + reduces RX interrupts by itself. Interrupt mitigation reduces RX + interrupts even at low levels of traffic at the cost of a small + latency. + + If in doubt, say Y. + +config TULIP_DM910X + def_bool y + depends on TULIP && SPARC + +config DE4X5 + tristate "Generic DECchip & DIGITAL EtherWORKS PCI/EISA" + depends on (PCI || EISA) + select CRC32 + ---help--- + This is support for the DIGITAL series of PCI/EISA Ethernet cards. + These include the DE425, DE434, DE435, DE450 and DE500 models. If + you have a network card of this type, say Y and read the + Ethernet-HOWTO, available from + . More specific + information is contained in + . + + To compile this driver as a module, choose M here. The module will + be called de4x5. + +config WINBOND_840 + tristate "Winbond W89c840 Ethernet support" + depends on PCI + select CRC32 + select MII + ---help--- + This driver is for the Winbond W89c840 chip. It also works with + the TX9882 chip on the Compex RL100-ATX board. + More specific information and updates are available from + . + +config DM9102 + tristate "Davicom DM910x/DM980x support" + depends on PCI + select CRC32 + ---help--- + This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from + Davicom (). If you have such a network + (Ethernet) card, say Y. Some information is contained in the file + . + + To compile this driver as a module, choose M here. The module will + be called dmfe. + +config ULI526X + tristate "ULi M526x controller support" + depends on PCI + select CRC32 + ---help--- + This driver is for ULi M5261/M5263 10/100M Ethernet Controller + (). + + To compile this driver as a module, choose M here. The module will + be called uli526x. + +config PCMCIA_XIRCOM + tristate "Xircom CardBus support" + depends on CARDBUS + ---help--- + This driver is for the Digital "Tulip" Ethernet CardBus adapters. + It should work with most DEC 21*4*-based chips/ethercards, as well + as with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and + ASIX. + + To compile this driver as a module, choose M here. The module will + be called xircom_cb. If unsure, say N. + +endif # NET_TULIP diff --git a/drivers/net/ethernet/dec/tulip/Makefile b/drivers/net/ethernet/dec/tulip/Makefile new file mode 100644 index 000000000000..5e8be38b45bb --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for the Linux "Tulip" family network device drivers. +# + +ccflags-$(CONFIG_NET_TULIP) := -DDEBUG + +obj-$(CONFIG_PCMCIA_XIRCOM) += xircom_cb.o +obj-$(CONFIG_DM9102) += dmfe.o +obj-$(CONFIG_WINBOND_840) += winbond-840.o +obj-$(CONFIG_DE2104X) += de2104x.o +obj-$(CONFIG_TULIP) += tulip.o +obj-$(CONFIG_DE4X5) += de4x5.o +obj-$(CONFIG_ULI526X) += uli526x.o + +# Declare multi-part drivers. + +tulip-objs := eeprom.o interrupt.o media.o \ + timer.o tulip_core.o \ + 21142.o pnic.o pnic2.o diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c new file mode 100644 index 000000000000..ce90efc6ba3c --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -0,0 +1,2215 @@ +/* de2104x.c: A Linux PCI Ethernet driver for Intel/Digital 21040/1 chips. */ +/* + Copyright 2001,2003 Jeff Garzik + + Copyright 1994, 1995 Digital Equipment Corporation. [de4x5.c] + Written/copyright 1994-2001 by Donald Becker. [tulip.c] + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + See the file COPYING in this distribution for more information. + + TODO, in rough priority order: + * Support forcing media type with a module parameter, + like dl2k.c/sundance.c + * Constants (module parms?) for Rx work limit + * Complete reset on PciErr + * Jumbo frames / dev->change_mtu + * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error + * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error + * Implement Tx software interrupt mitigation via + Tx descriptor bit + + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "de2104x" +#define DRV_VERSION "0.7" +#define DRV_RELDATE "Mar 17, 2004" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* These identify the driver base version and may not be removed. */ +static char version[] = +"PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")"; + +MODULE_AUTHOR("Jeff Garzik "); +MODULE_DESCRIPTION("Intel/Digital 21040/1 series PCI Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +static int debug = -1; +module_param (debug, int, 0); +MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number"); + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \ + defined(CONFIG_SPARC) || defined(__ia64__) || \ + defined(__sh__) || defined(__mips__) +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif +module_param (rx_copybreak, int, 0); +MODULE_PARM_DESC (rx_copybreak, "de2104x Breakpoint at which Rx packets are copied"); + +#define DE_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR) + +/* Descriptor skip length in 32 bit longwords. */ +#ifndef CONFIG_DE2104X_DSL +#define DSL 0 +#else +#define DSL CONFIG_DE2104X_DSL +#endif + +#define DE_RX_RING_SIZE 64 +#define DE_TX_RING_SIZE 64 +#define DE_RING_BYTES \ + ((sizeof(struct de_desc) * DE_RX_RING_SIZE) + \ + (sizeof(struct de_desc) * DE_TX_RING_SIZE)) +#define NEXT_TX(N) (((N) + 1) & (DE_TX_RING_SIZE - 1)) +#define NEXT_RX(N) (((N) + 1) & (DE_RX_RING_SIZE - 1)) +#define TX_BUFFS_AVAIL(CP) \ + (((CP)->tx_tail <= (CP)->tx_head) ? \ + (CP)->tx_tail + (DE_TX_RING_SIZE - 1) - (CP)->tx_head : \ + (CP)->tx_tail - (CP)->tx_head - 1) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +#define RX_OFFSET 2 + +#define DE_SETUP_SKB ((struct sk_buff *) 1) +#define DE_DUMMY_SKB ((struct sk_buff *) 2) +#define DE_SETUP_FRAME_WORDS 96 +#define DE_EEPROM_WORDS 256 +#define DE_EEPROM_SIZE (DE_EEPROM_WORDS * sizeof(u16)) +#define DE_MAX_MEDIA 5 + +#define DE_MEDIA_TP_AUTO 0 +#define DE_MEDIA_BNC 1 +#define DE_MEDIA_AUI 2 +#define DE_MEDIA_TP 3 +#define DE_MEDIA_TP_FD 4 +#define DE_MEDIA_INVALID DE_MAX_MEDIA +#define DE_MEDIA_FIRST 0 +#define DE_MEDIA_LAST (DE_MAX_MEDIA - 1) +#define DE_AUI_BNC (SUPPORTED_AUI | SUPPORTED_BNC) + +#define DE_TIMER_LINK (60 * HZ) +#define DE_TIMER_NO_LINK (5 * HZ) + +#define DE_NUM_REGS 16 +#define DE_REGS_SIZE (DE_NUM_REGS * sizeof(u32)) +#define DE_REGS_VER 1 + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 + +enum { + /* NIC registers */ + BusMode = 0x00, + TxPoll = 0x08, + RxPoll = 0x10, + RxRingAddr = 0x18, + TxRingAddr = 0x20, + MacStatus = 0x28, + MacMode = 0x30, + IntrMask = 0x38, + RxMissed = 0x40, + ROMCmd = 0x48, + CSR11 = 0x58, + SIAStatus = 0x60, + CSR13 = 0x68, + CSR14 = 0x70, + CSR15 = 0x78, + PCIPM = 0x40, + + /* BusMode bits */ + CmdReset = (1 << 0), + CacheAlign16 = 0x00008000, + BurstLen4 = 0x00000400, + DescSkipLen = (DSL << 2), + + /* Rx/TxPoll bits */ + NormalTxPoll = (1 << 0), + NormalRxPoll = (1 << 0), + + /* Tx/Rx descriptor status bits */ + DescOwn = (1 << 31), + RxError = (1 << 15), + RxErrLong = (1 << 7), + RxErrCRC = (1 << 1), + RxErrFIFO = (1 << 0), + RxErrRunt = (1 << 11), + RxErrFrame = (1 << 14), + RingEnd = (1 << 25), + FirstFrag = (1 << 29), + LastFrag = (1 << 30), + TxError = (1 << 15), + TxFIFOUnder = (1 << 1), + TxLinkFail = (1 << 2) | (1 << 10) | (1 << 11), + TxMaxCol = (1 << 8), + TxOWC = (1 << 9), + TxJabber = (1 << 14), + SetupFrame = (1 << 27), + TxSwInt = (1 << 31), + + /* MacStatus bits */ + IntrOK = (1 << 16), + IntrErr = (1 << 15), + RxIntr = (1 << 6), + RxEmpty = (1 << 7), + TxIntr = (1 << 0), + TxEmpty = (1 << 2), + PciErr = (1 << 13), + TxState = (1 << 22) | (1 << 21) | (1 << 20), + RxState = (1 << 19) | (1 << 18) | (1 << 17), + LinkFail = (1 << 12), + LinkPass = (1 << 4), + RxStopped = (1 << 8), + TxStopped = (1 << 1), + + /* MacMode bits */ + TxEnable = (1 << 13), + RxEnable = (1 << 1), + RxTx = TxEnable | RxEnable, + FullDuplex = (1 << 9), + AcceptAllMulticast = (1 << 7), + AcceptAllPhys = (1 << 6), + BOCnt = (1 << 5), + MacModeClear = (1<<12) | (1<<11) | (1<<10) | (1<<8) | (1<<3) | + RxTx | BOCnt | AcceptAllPhys | AcceptAllMulticast, + + /* ROMCmd bits */ + EE_SHIFT_CLK = 0x02, /* EEPROM shift clock. */ + EE_CS = 0x01, /* EEPROM chip select. */ + EE_DATA_WRITE = 0x04, /* Data from the Tulip to EEPROM. */ + EE_WRITE_0 = 0x01, + EE_WRITE_1 = 0x05, + EE_DATA_READ = 0x08, /* Data from the EEPROM chip. */ + EE_ENB = (0x4800 | EE_CS), + + /* The EEPROM commands include the alway-set leading bit. */ + EE_READ_CMD = 6, + + /* RxMissed bits */ + RxMissedOver = (1 << 16), + RxMissedMask = 0xffff, + + /* SROM-related bits */ + SROMC0InfoLeaf = 27, + MediaBlockMask = 0x3f, + MediaCustomCSRs = (1 << 6), + + /* PCIPM bits */ + PM_Sleep = (1 << 31), + PM_Snooze = (1 << 30), + PM_Mask = PM_Sleep | PM_Snooze, + + /* SIAStatus bits */ + NWayState = (1 << 14) | (1 << 13) | (1 << 12), + NWayRestart = (1 << 12), + NonselPortActive = (1 << 9), + SelPortActive = (1 << 8), + LinkFailStatus = (1 << 2), + NetCxnErr = (1 << 1), +}; + +static const u32 de_intr_mask = + IntrOK | IntrErr | RxIntr | RxEmpty | TxIntr | TxEmpty | + LinkPass | LinkFail | PciErr; + +/* + * Set the programmable burst length to 4 longwords for all: + * DMA errors result without these values. Cache align 16 long. + */ +static const u32 de_bus_mode = CacheAlign16 | BurstLen4 | DescSkipLen; + +struct de_srom_media_block { + u8 opts; + u16 csr13; + u16 csr14; + u16 csr15; +} __packed; + +struct de_srom_info_leaf { + u16 default_media; + u8 n_blocks; + u8 unused; +} __packed; + +struct de_desc { + __le32 opts1; + __le32 opts2; + __le32 addr1; + __le32 addr2; +#if DSL + __le32 skip[DSL]; +#endif +}; + +struct media_info { + u16 type; /* DE_MEDIA_xxx */ + u16 csr13; + u16 csr14; + u16 csr15; +}; + +struct ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct de_private { + unsigned tx_head; + unsigned tx_tail; + unsigned rx_tail; + + void __iomem *regs; + struct net_device *dev; + spinlock_t lock; + + struct de_desc *rx_ring; + struct de_desc *tx_ring; + struct ring_info tx_skb[DE_TX_RING_SIZE]; + struct ring_info rx_skb[DE_RX_RING_SIZE]; + unsigned rx_buf_sz; + dma_addr_t ring_dma; + + u32 msg_enable; + + struct net_device_stats net_stats; + + struct pci_dev *pdev; + + u16 setup_frame[DE_SETUP_FRAME_WORDS]; + + u32 media_type; + u32 media_supported; + u32 media_advertise; + struct media_info media[DE_MAX_MEDIA]; + struct timer_list media_timer; + + u8 *ee_data; + unsigned board_idx; + unsigned de21040 : 1; + unsigned media_lock : 1; +}; + + +static void de_set_rx_mode (struct net_device *dev); +static void de_tx (struct de_private *de); +static void de_clean_rings (struct de_private *de); +static void de_media_interrupt (struct de_private *de, u32 status); +static void de21040_media_timer (unsigned long data); +static void de21041_media_timer (unsigned long data); +static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media); + + +static DEFINE_PCI_DEVICE_TABLE(de_pci_tbl) = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { }, +}; +MODULE_DEVICE_TABLE(pci, de_pci_tbl); + +static const char * const media_name[DE_MAX_MEDIA] = { + "10baseT auto", + "BNC", + "AUI", + "10baseT-HD", + "10baseT-FD" +}; + +/* 21040 transceiver register settings: + * TP AUTO(unused), BNC(unused), AUI, TP, TP FD*/ +static u16 t21040_csr13[] = { 0, 0, 0x8F09, 0x8F01, 0x8F01, }; +static u16 t21040_csr14[] = { 0, 0, 0x0705, 0xFFFF, 0xFFFD, }; +static u16 t21040_csr15[] = { 0, 0, 0x0006, 0x0000, 0x0000, }; + +/* 21041 transceiver register settings: TP AUTO, BNC, AUI, TP, TP FD*/ +static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; +/* If on-chip autonegotiation is broken, use half-duplex (FF3F) instead */ +static u16 t21041_csr14_brk[] = { 0xFF3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + + +#define dr32(reg) ioread32(de->regs + (reg)) +#define dw32(reg, val) iowrite32((val), de->regs + (reg)) + + +static void de_rx_err_acct (struct de_private *de, unsigned rx_tail, + u32 status, u32 len) +{ + netif_dbg(de, rx_err, de->dev, + "rx err, slot %d status 0x%x len %d\n", + rx_tail, status, len); + + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + netif_warn(de, rx_err, de->dev, + "Oversized Ethernet frame spanned multiple buffers, status %08x!\n", + status); + de->net_stats.rx_length_errors++; + } + } else if (status & RxError) { + /* There was a fatal error. */ + de->net_stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) de->net_stats.rx_length_errors++; + if (status & RxErrCRC) de->net_stats.rx_crc_errors++; + if (status & RxErrFIFO) de->net_stats.rx_fifo_errors++; + } +} + +static void de_rx (struct de_private *de) +{ + unsigned rx_tail = de->rx_tail; + unsigned rx_work = DE_RX_RING_SIZE; + unsigned drop = 0; + int rc; + + while (--rx_work) { + u32 status, len; + dma_addr_t mapping; + struct sk_buff *skb, *copy_skb; + unsigned copying_skb, buflen; + + skb = de->rx_skb[rx_tail].skb; + BUG_ON(!skb); + rmb(); + status = le32_to_cpu(de->rx_ring[rx_tail].opts1); + if (status & DescOwn) + break; + + len = ((status >> 16) & 0x7ff) - 4; + mapping = de->rx_skb[rx_tail].mapping; + + if (unlikely(drop)) { + de->net_stats.rx_dropped++; + goto rx_next; + } + + if (unlikely((status & 0x38008300) != 0x0300)) { + de_rx_err_acct(de, rx_tail, status, len); + goto rx_next; + } + + copying_skb = (len <= rx_copybreak); + + netif_dbg(de, rx_status, de->dev, + "rx slot %d status 0x%x len %d copying? %d\n", + rx_tail, status, len, copying_skb); + + buflen = copying_skb ? (len + RX_OFFSET) : de->rx_buf_sz; + copy_skb = dev_alloc_skb (buflen); + if (unlikely(!copy_skb)) { + de->net_stats.rx_dropped++; + drop = 1; + rx_work = 100; + goto rx_next; + } + + if (!copying_skb) { + pci_unmap_single(de->pdev, mapping, + buflen, PCI_DMA_FROMDEVICE); + skb_put(skb, len); + + mapping = + de->rx_skb[rx_tail].mapping = + pci_map_single(de->pdev, copy_skb->data, + buflen, PCI_DMA_FROMDEVICE); + de->rx_skb[rx_tail].skb = copy_skb; + } else { + pci_dma_sync_single_for_cpu(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); + skb_reserve(copy_skb, RX_OFFSET); + skb_copy_from_linear_data(skb, skb_put(copy_skb, len), + len); + pci_dma_sync_single_for_device(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); + + /* We'll reuse the original ring buffer. */ + skb = copy_skb; + } + + skb->protocol = eth_type_trans (skb, de->dev); + + de->net_stats.rx_packets++; + de->net_stats.rx_bytes += skb->len; + rc = netif_rx (skb); + if (rc == NET_RX_DROP) + drop = 1; + +rx_next: + if (rx_tail == (DE_RX_RING_SIZE - 1)) + de->rx_ring[rx_tail].opts2 = + cpu_to_le32(RingEnd | de->rx_buf_sz); + else + de->rx_ring[rx_tail].opts2 = cpu_to_le32(de->rx_buf_sz); + de->rx_ring[rx_tail].addr1 = cpu_to_le32(mapping); + wmb(); + de->rx_ring[rx_tail].opts1 = cpu_to_le32(DescOwn); + rx_tail = NEXT_RX(rx_tail); + } + + if (!rx_work) + netdev_warn(de->dev, "rx work limit reached\n"); + + de->rx_tail = rx_tail; +} + +static irqreturn_t de_interrupt (int irq, void *dev_instance) +{ + struct net_device *dev = dev_instance; + struct de_private *de = netdev_priv(dev); + u32 status; + + status = dr32(MacStatus); + if ((!(status & (IntrOK|IntrErr))) || (status == 0xFFFF)) + return IRQ_NONE; + + netif_dbg(de, intr, dev, "intr, status %08x mode %08x desc %u/%u/%u\n", + status, dr32(MacMode), + de->rx_tail, de->tx_head, de->tx_tail); + + dw32(MacStatus, status); + + if (status & (RxIntr | RxEmpty)) { + de_rx(de); + if (status & RxEmpty) + dw32(RxPoll, NormalRxPoll); + } + + spin_lock(&de->lock); + + if (status & (TxIntr | TxEmpty)) + de_tx(de); + + if (status & (LinkPass | LinkFail)) + de_media_interrupt(de, status); + + spin_unlock(&de->lock); + + if (status & PciErr) { + u16 pci_status; + + pci_read_config_word(de->pdev, PCI_STATUS, &pci_status); + pci_write_config_word(de->pdev, PCI_STATUS, pci_status); + netdev_err(de->dev, + "PCI bus error, status=%08x, PCI status=%04x\n", + status, pci_status); + } + + return IRQ_HANDLED; +} + +static void de_tx (struct de_private *de) +{ + unsigned tx_head = de->tx_head; + unsigned tx_tail = de->tx_tail; + + while (tx_tail != tx_head) { + struct sk_buff *skb; + u32 status; + + rmb(); + status = le32_to_cpu(de->tx_ring[tx_tail].opts1); + if (status & DescOwn) + break; + + skb = de->tx_skb[tx_tail].skb; + BUG_ON(!skb); + if (unlikely(skb == DE_DUMMY_SKB)) + goto next; + + if (unlikely(skb == DE_SETUP_SKB)) { + pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, + sizeof(de->setup_frame), PCI_DMA_TODEVICE); + goto next; + } + + pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, + skb->len, PCI_DMA_TODEVICE); + + if (status & LastFrag) { + if (status & TxError) { + netif_dbg(de, tx_err, de->dev, + "tx err, status 0x%x\n", + status); + de->net_stats.tx_errors++; + if (status & TxOWC) + de->net_stats.tx_window_errors++; + if (status & TxMaxCol) + de->net_stats.tx_aborted_errors++; + if (status & TxLinkFail) + de->net_stats.tx_carrier_errors++; + if (status & TxFIFOUnder) + de->net_stats.tx_fifo_errors++; + } else { + de->net_stats.tx_packets++; + de->net_stats.tx_bytes += skb->len; + netif_dbg(de, tx_done, de->dev, + "tx done, slot %d\n", tx_tail); + } + dev_kfree_skb_irq(skb); + } + +next: + de->tx_skb[tx_tail].skb = NULL; + + tx_tail = NEXT_TX(tx_tail); + } + + de->tx_tail = tx_tail; + + if (netif_queue_stopped(de->dev) && (TX_BUFFS_AVAIL(de) > (DE_TX_RING_SIZE / 4))) + netif_wake_queue(de->dev); +} + +static netdev_tx_t de_start_xmit (struct sk_buff *skb, + struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + unsigned int entry, tx_free; + u32 mapping, len, flags = FirstFrag | LastFrag; + struct de_desc *txd; + + spin_lock_irq(&de->lock); + + tx_free = TX_BUFFS_AVAIL(de); + if (tx_free == 0) { + netif_stop_queue(dev); + spin_unlock_irq(&de->lock); + return NETDEV_TX_BUSY; + } + tx_free--; + + entry = de->tx_head; + + txd = &de->tx_ring[entry]; + + len = skb->len; + mapping = pci_map_single(de->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (entry == (DE_TX_RING_SIZE - 1)) + flags |= RingEnd; + if (!tx_free || (tx_free == (DE_TX_RING_SIZE / 2))) + flags |= TxSwInt; + flags |= len; + txd->opts2 = cpu_to_le32(flags); + txd->addr1 = cpu_to_le32(mapping); + + de->tx_skb[entry].skb = skb; + de->tx_skb[entry].mapping = mapping; + wmb(); + + txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + + de->tx_head = NEXT_TX(entry); + netif_dbg(de, tx_queued, dev, "tx queued, slot %d, skblen %d\n", + entry, skb->len); + + if (tx_free == 0) + netif_stop_queue(dev); + + spin_unlock_irq(&de->lock); + + /* Trigger an immediate transmit demand. */ + dw32(TxPoll, NormalTxPoll); + + return NETDEV_TX_OK; +} + +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling de->setup_frame. This is non-deterministic + when re-entered but still correct. */ + +#undef set_bit_le +#define set_bit_le(i,p) do { ((char *)(p))[(i)/8] |= (1<<((i)%8)); } while(0) + +static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + u16 hash_table[32]; + struct netdev_hw_addr *ha; + int i; + u16 *eaddrs; + + memset(hash_table, 0, sizeof(hash_table)); + set_bit_le(255, hash_table); /* Broadcast entry */ + /* This should work on big-endian machines as well. */ + netdev_for_each_mc_addr(ha, dev) { + int index = ether_crc_le(ETH_ALEN, ha->addr) & 0x1ff; + + set_bit_le(index, hash_table); + } + + for (i = 0; i < 32; i++) { + *setup_frm++ = hash_table[i]; + *setup_frm++ = hash_table[i]; + } + setup_frm = &de->setup_frame[13*6]; + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +} + +static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + struct netdev_hw_addr *ha; + u16 *eaddrs; + + /* We have <= 14 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + netdev_for_each_mc_addr(ha, dev) { + eaddrs = (u16 *) ha->addr; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + } + /* Fill the unused entries with the broadcast address. */ + memset(setup_frm, 0xff, (15 - netdev_mc_count(dev)) * 12); + setup_frm = &de->setup_frame[15*6]; + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +} + + +static void __de_set_rx_mode (struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + u32 macmode; + unsigned int entry; + u32 mapping; + struct de_desc *txd; + struct de_desc *dummy_txd = NULL; + + macmode = dr32(MacMode) & ~(AcceptAllMulticast | AcceptAllPhys); + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + macmode |= AcceptAllMulticast | AcceptAllPhys; + goto out; + } + + if ((netdev_mc_count(dev) > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well -- accept all multicasts. */ + macmode |= AcceptAllMulticast; + goto out; + } + + /* Note that only the low-address shortword of setup_frame is valid! + The values are doubled for big-endian architectures. */ + if (netdev_mc_count(dev) > 14) /* Must use a multicast hash table. */ + build_setup_frame_hash (de->setup_frame, dev); + else + build_setup_frame_perfect (de->setup_frame, dev); + + /* + * Now add this frame to the Tx list. + */ + + entry = de->tx_head; + + /* Avoid a chip errata by prefixing a dummy entry. */ + if (entry != 0) { + de->tx_skb[entry].skb = DE_DUMMY_SKB; + + dummy_txd = &de->tx_ring[entry]; + dummy_txd->opts2 = (entry == (DE_TX_RING_SIZE - 1)) ? + cpu_to_le32(RingEnd) : 0; + dummy_txd->addr1 = 0; + + /* Must set DescOwned later to avoid race with chip */ + + entry = NEXT_TX(entry); + } + + de->tx_skb[entry].skb = DE_SETUP_SKB; + de->tx_skb[entry].mapping = mapping = + pci_map_single (de->pdev, de->setup_frame, + sizeof (de->setup_frame), PCI_DMA_TODEVICE); + + /* Put the setup frame on the Tx list. */ + txd = &de->tx_ring[entry]; + if (entry == (DE_TX_RING_SIZE - 1)) + txd->opts2 = cpu_to_le32(SetupFrame | RingEnd | sizeof (de->setup_frame)); + else + txd->opts2 = cpu_to_le32(SetupFrame | sizeof (de->setup_frame)); + txd->addr1 = cpu_to_le32(mapping); + wmb(); + + txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + + if (dummy_txd) { + dummy_txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + } + + de->tx_head = NEXT_TX(entry); + + if (TX_BUFFS_AVAIL(de) == 0) + netif_stop_queue(dev); + + /* Trigger an immediate transmit demand. */ + dw32(TxPoll, NormalTxPoll); + +out: + if (macmode != dr32(MacMode)) + dw32(MacMode, macmode); +} + +static void de_set_rx_mode (struct net_device *dev) +{ + unsigned long flags; + struct de_private *de = netdev_priv(dev); + + spin_lock_irqsave (&de->lock, flags); + __de_set_rx_mode(dev); + spin_unlock_irqrestore (&de->lock, flags); +} + +static inline void de_rx_missed(struct de_private *de, u32 rx_missed) +{ + if (unlikely(rx_missed & RxMissedOver)) + de->net_stats.rx_missed_errors += RxMissedMask; + else + de->net_stats.rx_missed_errors += (rx_missed & RxMissedMask); +} + +static void __de_get_stats(struct de_private *de) +{ + u32 tmp = dr32(RxMissed); /* self-clearing */ + + de_rx_missed(de, tmp); +} + +static struct net_device_stats *de_get_stats(struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + + /* The chip only need report frame silently dropped. */ + spin_lock_irq(&de->lock); + if (netif_running(dev) && netif_device_present(dev)) + __de_get_stats(de); + spin_unlock_irq(&de->lock); + + return &de->net_stats; +} + +static inline int de_is_running (struct de_private *de) +{ + return (dr32(MacStatus) & (RxState | TxState)) ? 1 : 0; +} + +static void de_stop_rxtx (struct de_private *de) +{ + u32 macmode; + unsigned int i = 1300/100; + + macmode = dr32(MacMode); + if (macmode & RxTx) { + dw32(MacMode, macmode & ~RxTx); + dr32(MacMode); + } + + /* wait until in-flight frame completes. + * Max time @ 10BT: 1500*8b/10Mbps == 1200us (+ 100us margin) + * Typically expect this loop to end in < 50 us on 100BT. + */ + while (--i) { + if (!de_is_running(de)) + return; + udelay(100); + } + + netdev_warn(de->dev, "timeout expired, stopping DMA\n"); +} + +static inline void de_start_rxtx (struct de_private *de) +{ + u32 macmode; + + macmode = dr32(MacMode); + if ((macmode & RxTx) != RxTx) { + dw32(MacMode, macmode | RxTx); + dr32(MacMode); + } +} + +static void de_stop_hw (struct de_private *de) +{ + + udelay(5); + dw32(IntrMask, 0); + + de_stop_rxtx(de); + + dw32(MacStatus, dr32(MacStatus)); + + udelay(10); + + de->rx_tail = 0; + de->tx_head = de->tx_tail = 0; +} + +static void de_link_up(struct de_private *de) +{ + if (!netif_carrier_ok(de->dev)) { + netif_carrier_on(de->dev); + netif_info(de, link, de->dev, "link up, media %s\n", + media_name[de->media_type]); + } +} + +static void de_link_down(struct de_private *de) +{ + if (netif_carrier_ok(de->dev)) { + netif_carrier_off(de->dev); + netif_info(de, link, de->dev, "link down\n"); + } +} + +static void de_set_media (struct de_private *de) +{ + unsigned media = de->media_type; + u32 macmode = dr32(MacMode); + + if (de_is_running(de)) + netdev_warn(de->dev, "chip is running while changing media!\n"); + + if (de->de21040) + dw32(CSR11, FULL_DUPLEX_MAGIC); + dw32(CSR13, 0); /* Reset phy */ + dw32(CSR14, de->media[media].csr14); + dw32(CSR15, de->media[media].csr15); + dw32(CSR13, de->media[media].csr13); + + /* must delay 10ms before writing to other registers, + * especially CSR6 + */ + mdelay(10); + + if (media == DE_MEDIA_TP_FD) + macmode |= FullDuplex; + else + macmode &= ~FullDuplex; + + netif_info(de, link, de->dev, "set link %s\n", media_name[media]); + netif_info(de, hw, de->dev, "mode 0x%x, sia 0x%x,0x%x,0x%x,0x%x\n", + dr32(MacMode), dr32(SIAStatus), + dr32(CSR13), dr32(CSR14), dr32(CSR15)); + netif_info(de, hw, de->dev, "set mode 0x%x, set sia 0x%x,0x%x,0x%x\n", + macmode, de->media[media].csr13, + de->media[media].csr14, de->media[media].csr15); + if (macmode != dr32(MacMode)) + dw32(MacMode, macmode); +} + +static void de_next_media (struct de_private *de, const u32 *media, + unsigned int n_media) +{ + unsigned int i; + + for (i = 0; i < n_media; i++) { + if (de_ok_to_advertise(de, media[i])) { + de->media_type = media[i]; + return; + } + } +} + +static void de21040_media_timer (unsigned long data) +{ + struct de_private *de = (struct de_private *) data; + struct net_device *dev = de->dev; + u32 status = dr32(SIAStatus); + unsigned int carrier; + unsigned long flags; + + carrier = (status & NetCxnErr) ? 0 : 1; + + if (carrier) { + if (de->media_type != DE_MEDIA_AUI && (status & LinkFailStatus)) + goto no_link_yet; + + de->media_timer.expires = jiffies + DE_TIMER_LINK; + add_timer(&de->media_timer); + if (!netif_carrier_ok(dev)) + de_link_up(de); + else + netif_info(de, timer, dev, "%s link ok, status %x\n", + media_name[de->media_type], status); + return; + } + + de_link_down(de); + + if (de->media_lock) + return; + + if (de->media_type == DE_MEDIA_AUI) { + static const u32 next_state = DE_MEDIA_TP; + de_next_media(de, &next_state, 1); + } else { + static const u32 next_state = DE_MEDIA_AUI; + de_next_media(de, &next_state, 1); + } + + spin_lock_irqsave(&de->lock, flags); + de_stop_rxtx(de); + spin_unlock_irqrestore(&de->lock, flags); + de_set_media(de); + de_start_rxtx(de); + +no_link_yet: + de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; + add_timer(&de->media_timer); + + netif_info(de, timer, dev, "no link, trying media %s, status %x\n", + media_name[de->media_type], status); +} + +static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media) +{ + switch (new_media) { + case DE_MEDIA_TP_AUTO: + if (!(de->media_advertise & ADVERTISED_Autoneg)) + return 0; + if (!(de->media_advertise & (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full))) + return 0; + break; + case DE_MEDIA_BNC: + if (!(de->media_advertise & ADVERTISED_BNC)) + return 0; + break; + case DE_MEDIA_AUI: + if (!(de->media_advertise & ADVERTISED_AUI)) + return 0; + break; + case DE_MEDIA_TP: + if (!(de->media_advertise & ADVERTISED_10baseT_Half)) + return 0; + break; + case DE_MEDIA_TP_FD: + if (!(de->media_advertise & ADVERTISED_10baseT_Full)) + return 0; + break; + } + + return 1; +} + +static void de21041_media_timer (unsigned long data) +{ + struct de_private *de = (struct de_private *) data; + struct net_device *dev = de->dev; + u32 status = dr32(SIAStatus); + unsigned int carrier; + unsigned long flags; + + /* clear port active bits */ + dw32(SIAStatus, NonselPortActive | SelPortActive); + + carrier = (status & NetCxnErr) ? 0 : 1; + + if (carrier) { + if ((de->media_type == DE_MEDIA_TP_AUTO || + de->media_type == DE_MEDIA_TP || + de->media_type == DE_MEDIA_TP_FD) && + (status & LinkFailStatus)) + goto no_link_yet; + + de->media_timer.expires = jiffies + DE_TIMER_LINK; + add_timer(&de->media_timer); + if (!netif_carrier_ok(dev)) + de_link_up(de); + else + netif_info(de, timer, dev, + "%s link ok, mode %x status %x\n", + media_name[de->media_type], + dr32(MacMode), status); + return; + } + + de_link_down(de); + + /* if media type locked, don't switch media */ + if (de->media_lock) + goto set_media; + + /* if activity detected, use that as hint for new media type */ + if (status & NonselPortActive) { + unsigned int have_media = 1; + + /* if AUI/BNC selected, then activity is on TP port */ + if (de->media_type == DE_MEDIA_AUI || + de->media_type == DE_MEDIA_BNC) { + if (de_ok_to_advertise(de, DE_MEDIA_TP_AUTO)) + de->media_type = DE_MEDIA_TP_AUTO; + else + have_media = 0; + } + + /* TP selected. If there is only TP and BNC, then it's BNC */ + else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_BNC) && + de_ok_to_advertise(de, DE_MEDIA_BNC)) + de->media_type = DE_MEDIA_BNC; + + /* TP selected. If there is only TP and AUI, then it's AUI */ + else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_AUI) && + de_ok_to_advertise(de, DE_MEDIA_AUI)) + de->media_type = DE_MEDIA_AUI; + + /* otherwise, ignore the hint */ + else + have_media = 0; + + if (have_media) + goto set_media; + } + + /* + * Absent or ambiguous activity hint, move to next advertised + * media state. If de->media_type is left unchanged, this + * simply resets the PHY and reloads the current media settings. + */ + if (de->media_type == DE_MEDIA_AUI) { + static const u32 next_states[] = { + DE_MEDIA_BNC, DE_MEDIA_TP_AUTO + }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } else if (de->media_type == DE_MEDIA_BNC) { + static const u32 next_states[] = { + DE_MEDIA_TP_AUTO, DE_MEDIA_AUI + }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } else { + static const u32 next_states[] = { + DE_MEDIA_AUI, DE_MEDIA_BNC, DE_MEDIA_TP_AUTO + }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } + +set_media: + spin_lock_irqsave(&de->lock, flags); + de_stop_rxtx(de); + spin_unlock_irqrestore(&de->lock, flags); + de_set_media(de); + de_start_rxtx(de); + +no_link_yet: + de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; + add_timer(&de->media_timer); + + netif_info(de, timer, dev, "no link, trying media %s, status %x\n", + media_name[de->media_type], status); +} + +static void de_media_interrupt (struct de_private *de, u32 status) +{ + if (status & LinkPass) { + /* Ignore if current media is AUI or BNC and we can't use TP */ + if ((de->media_type == DE_MEDIA_AUI || + de->media_type == DE_MEDIA_BNC) && + (de->media_lock || + !de_ok_to_advertise(de, DE_MEDIA_TP_AUTO))) + return; + /* If current media is not TP, change it to TP */ + if ((de->media_type == DE_MEDIA_AUI || + de->media_type == DE_MEDIA_BNC)) { + de->media_type = DE_MEDIA_TP_AUTO; + de_stop_rxtx(de); + de_set_media(de); + de_start_rxtx(de); + } + de_link_up(de); + mod_timer(&de->media_timer, jiffies + DE_TIMER_LINK); + return; + } + + BUG_ON(!(status & LinkFail)); + /* Mark the link as down only if current media is TP */ + if (netif_carrier_ok(de->dev) && de->media_type != DE_MEDIA_AUI && + de->media_type != DE_MEDIA_BNC) { + de_link_down(de); + mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); + } +} + +static int de_reset_mac (struct de_private *de) +{ + u32 status, tmp; + + /* + * Reset MAC. de4x5.c and tulip.c examined for "advice" + * in this area. + */ + + if (dr32(BusMode) == 0xffffffff) + return -EBUSY; + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + dw32 (BusMode, CmdReset); + mdelay (1); + + dw32 (BusMode, de_bus_mode); + mdelay (1); + + for (tmp = 0; tmp < 5; tmp++) { + dr32 (BusMode); + mdelay (1); + } + + mdelay (1); + + status = dr32(MacStatus); + if (status & (RxState | TxState)) + return -EBUSY; + if (status == 0xffffffff) + return -ENODEV; + return 0; +} + +static void de_adapter_wake (struct de_private *de) +{ + u32 pmctl; + + if (de->de21040) + return; + + pci_read_config_dword(de->pdev, PCIPM, &pmctl); + if (pmctl & PM_Mask) { + pmctl &= ~PM_Mask; + pci_write_config_dword(de->pdev, PCIPM, pmctl); + + /* de4x5.c delays, so we do too */ + msleep(10); + } +} + +static void de_adapter_sleep (struct de_private *de) +{ + u32 pmctl; + + if (de->de21040) + return; + + dw32(CSR13, 0); /* Reset phy */ + pci_read_config_dword(de->pdev, PCIPM, &pmctl); + pmctl |= PM_Sleep; + pci_write_config_dword(de->pdev, PCIPM, pmctl); +} + +static int de_init_hw (struct de_private *de) +{ + struct net_device *dev = de->dev; + u32 macmode; + int rc; + + de_adapter_wake(de); + + macmode = dr32(MacMode) & ~MacModeClear; + + rc = de_reset_mac(de); + if (rc) + return rc; + + de_set_media(de); /* reset phy */ + + dw32(RxRingAddr, de->ring_dma); + dw32(TxRingAddr, de->ring_dma + (sizeof(struct de_desc) * DE_RX_RING_SIZE)); + + dw32(MacMode, RxTx | macmode); + + dr32(RxMissed); /* self-clearing */ + + dw32(IntrMask, de_intr_mask); + + de_set_rx_mode(dev); + + return 0; +} + +static int de_refill_rx (struct de_private *de) +{ + unsigned i; + + for (i = 0; i < DE_RX_RING_SIZE; i++) { + struct sk_buff *skb; + + skb = dev_alloc_skb(de->rx_buf_sz); + if (!skb) + goto err_out; + + skb->dev = de->dev; + + de->rx_skb[i].mapping = pci_map_single(de->pdev, + skb->data, de->rx_buf_sz, PCI_DMA_FROMDEVICE); + de->rx_skb[i].skb = skb; + + de->rx_ring[i].opts1 = cpu_to_le32(DescOwn); + if (i == (DE_RX_RING_SIZE - 1)) + de->rx_ring[i].opts2 = + cpu_to_le32(RingEnd | de->rx_buf_sz); + else + de->rx_ring[i].opts2 = cpu_to_le32(de->rx_buf_sz); + de->rx_ring[i].addr1 = cpu_to_le32(de->rx_skb[i].mapping); + de->rx_ring[i].addr2 = 0; + } + + return 0; + +err_out: + de_clean_rings(de); + return -ENOMEM; +} + +static int de_init_rings (struct de_private *de) +{ + memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); + de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + + de->rx_tail = 0; + de->tx_head = de->tx_tail = 0; + + return de_refill_rx (de); +} + +static int de_alloc_rings (struct de_private *de) +{ + de->rx_ring = pci_alloc_consistent(de->pdev, DE_RING_BYTES, &de->ring_dma); + if (!de->rx_ring) + return -ENOMEM; + de->tx_ring = &de->rx_ring[DE_RX_RING_SIZE]; + return de_init_rings(de); +} + +static void de_clean_rings (struct de_private *de) +{ + unsigned i; + + memset(de->rx_ring, 0, sizeof(struct de_desc) * DE_RX_RING_SIZE); + de->rx_ring[DE_RX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + wmb(); + memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); + de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + wmb(); + + for (i = 0; i < DE_RX_RING_SIZE; i++) { + if (de->rx_skb[i].skb) { + pci_unmap_single(de->pdev, de->rx_skb[i].mapping, + de->rx_buf_sz, PCI_DMA_FROMDEVICE); + dev_kfree_skb(de->rx_skb[i].skb); + } + } + + for (i = 0; i < DE_TX_RING_SIZE; i++) { + struct sk_buff *skb = de->tx_skb[i].skb; + if ((skb) && (skb != DE_DUMMY_SKB)) { + if (skb != DE_SETUP_SKB) { + de->net_stats.tx_dropped++; + pci_unmap_single(de->pdev, + de->tx_skb[i].mapping, + skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb(skb); + } else { + pci_unmap_single(de->pdev, + de->tx_skb[i].mapping, + sizeof(de->setup_frame), + PCI_DMA_TODEVICE); + } + } + } + + memset(&de->rx_skb, 0, sizeof(struct ring_info) * DE_RX_RING_SIZE); + memset(&de->tx_skb, 0, sizeof(struct ring_info) * DE_TX_RING_SIZE); +} + +static void de_free_rings (struct de_private *de) +{ + de_clean_rings(de); + pci_free_consistent(de->pdev, DE_RING_BYTES, de->rx_ring, de->ring_dma); + de->rx_ring = NULL; + de->tx_ring = NULL; +} + +static int de_open (struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + int rc; + + netif_dbg(de, ifup, dev, "enabling interface\n"); + + de->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + + rc = de_alloc_rings(de); + if (rc) { + netdev_err(dev, "ring allocation failure, err=%d\n", rc); + return rc; + } + + dw32(IntrMask, 0); + + rc = request_irq(dev->irq, de_interrupt, IRQF_SHARED, dev->name, dev); + if (rc) { + netdev_err(dev, "IRQ %d request failure, err=%d\n", + dev->irq, rc); + goto err_out_free; + } + + rc = de_init_hw(de); + if (rc) { + netdev_err(dev, "h/w init failure, err=%d\n", rc); + goto err_out_free_irq; + } + + netif_start_queue(dev); + mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); + + return 0; + +err_out_free_irq: + free_irq(dev->irq, dev); +err_out_free: + de_free_rings(de); + return rc; +} + +static int de_close (struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + unsigned long flags; + + netif_dbg(de, ifdown, dev, "disabling interface\n"); + + del_timer_sync(&de->media_timer); + + spin_lock_irqsave(&de->lock, flags); + de_stop_hw(de); + netif_stop_queue(dev); + netif_carrier_off(dev); + spin_unlock_irqrestore(&de->lock, flags); + + free_irq(dev->irq, dev); + + de_free_rings(de); + de_adapter_sleep(de); + return 0; +} + +static void de_tx_timeout (struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + + netdev_dbg(dev, "NIC status %08x mode %08x sia %08x desc %u/%u/%u\n", + dr32(MacStatus), dr32(MacMode), dr32(SIAStatus), + de->rx_tail, de->tx_head, de->tx_tail); + + del_timer_sync(&de->media_timer); + + disable_irq(dev->irq); + spin_lock_irq(&de->lock); + + de_stop_hw(de); + netif_stop_queue(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&de->lock); + enable_irq(dev->irq); + + /* Update the error counts. */ + __de_get_stats(de); + + synchronize_irq(dev->irq); + de_clean_rings(de); + + de_init_rings(de); + + de_init_hw(de); + + netif_wake_queue(dev); +} + +static void __de_get_regs(struct de_private *de, u8 *buf) +{ + int i; + u32 *rbuf = (u32 *)buf; + + /* read all CSRs */ + for (i = 0; i < DE_NUM_REGS; i++) + rbuf[i] = dr32(i * 8); + + /* handle self-clearing RxMissed counter, CSR8 */ + de_rx_missed(de, rbuf[8]); +} + +static int __de_get_settings(struct de_private *de, struct ethtool_cmd *ecmd) +{ + ecmd->supported = de->media_supported; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = 0; + ecmd->advertising = de->media_advertise; + + switch (de->media_type) { + case DE_MEDIA_AUI: + ecmd->port = PORT_AUI; + break; + case DE_MEDIA_BNC: + ecmd->port = PORT_BNC; + break; + default: + ecmd->port = PORT_TP; + break; + } + + ethtool_cmd_speed_set(ecmd, 10); + + if (dr32(MacMode) & FullDuplex) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + + if (de->media_lock) + ecmd->autoneg = AUTONEG_DISABLE; + else + ecmd->autoneg = AUTONEG_ENABLE; + + /* ignore maxtxpkt, maxrxpkt for now */ + + return 0; +} + +static int __de_set_settings(struct de_private *de, struct ethtool_cmd *ecmd) +{ + u32 new_media; + unsigned int media_lock; + + if (ethtool_cmd_speed(ecmd) != 10) + return -EINVAL; + if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) + return -EINVAL; + if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI && ecmd->port != PORT_BNC) + return -EINVAL; + if (de->de21040 && ecmd->port == PORT_BNC) + return -EINVAL; + if (ecmd->transceiver != XCVR_INTERNAL) + return -EINVAL; + if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) + return -EINVAL; + if (ecmd->advertising & ~de->media_supported) + return -EINVAL; + if (ecmd->autoneg == AUTONEG_ENABLE && + (!(ecmd->advertising & ADVERTISED_Autoneg))) + return -EINVAL; + + switch (ecmd->port) { + case PORT_AUI: + new_media = DE_MEDIA_AUI; + if (!(ecmd->advertising & ADVERTISED_AUI)) + return -EINVAL; + break; + case PORT_BNC: + new_media = DE_MEDIA_BNC; + if (!(ecmd->advertising & ADVERTISED_BNC)) + return -EINVAL; + break; + default: + if (ecmd->autoneg == AUTONEG_ENABLE) + new_media = DE_MEDIA_TP_AUTO; + else if (ecmd->duplex == DUPLEX_FULL) + new_media = DE_MEDIA_TP_FD; + else + new_media = DE_MEDIA_TP; + if (!(ecmd->advertising & ADVERTISED_TP)) + return -EINVAL; + if (!(ecmd->advertising & (ADVERTISED_10baseT_Full | ADVERTISED_10baseT_Half))) + return -EINVAL; + break; + } + + media_lock = (ecmd->autoneg == AUTONEG_ENABLE) ? 0 : 1; + + if ((new_media == de->media_type) && + (media_lock == de->media_lock) && + (ecmd->advertising == de->media_advertise)) + return 0; /* nothing to change */ + + de_link_down(de); + mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); + de_stop_rxtx(de); + + de->media_type = new_media; + de->media_lock = media_lock; + de->media_advertise = ecmd->advertising; + de_set_media(de); + if (netif_running(de->dev)) + de_start_rxtx(de); + + return 0; +} + +static void de_get_drvinfo (struct net_device *dev,struct ethtool_drvinfo *info) +{ + struct de_private *de = netdev_priv(dev); + + strcpy (info->driver, DRV_NAME); + strcpy (info->version, DRV_VERSION); + strcpy (info->bus_info, pci_name(de->pdev)); + info->eedump_len = DE_EEPROM_SIZE; +} + +static int de_get_regs_len(struct net_device *dev) +{ + return DE_REGS_SIZE; +} + +static int de_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct de_private *de = netdev_priv(dev); + int rc; + + spin_lock_irq(&de->lock); + rc = __de_get_settings(de, ecmd); + spin_unlock_irq(&de->lock); + + return rc; +} + +static int de_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct de_private *de = netdev_priv(dev); + int rc; + + spin_lock_irq(&de->lock); + rc = __de_set_settings(de, ecmd); + spin_unlock_irq(&de->lock); + + return rc; +} + +static u32 de_get_msglevel(struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + + return de->msg_enable; +} + +static void de_set_msglevel(struct net_device *dev, u32 msglvl) +{ + struct de_private *de = netdev_priv(dev); + + de->msg_enable = msglvl; +} + +static int de_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct de_private *de = netdev_priv(dev); + + if (!de->ee_data) + return -EOPNOTSUPP; + if ((eeprom->offset != 0) || (eeprom->magic != 0) || + (eeprom->len != DE_EEPROM_SIZE)) + return -EINVAL; + memcpy(data, de->ee_data, eeprom->len); + + return 0; +} + +static int de_nway_reset(struct net_device *dev) +{ + struct de_private *de = netdev_priv(dev); + u32 status; + + if (de->media_type != DE_MEDIA_TP_AUTO) + return -EINVAL; + if (netif_carrier_ok(de->dev)) + de_link_down(de); + + status = dr32(SIAStatus); + dw32(SIAStatus, (status & ~NWayState) | NWayRestart); + netif_info(de, link, dev, "link nway restart, status %x,%x\n", + status, dr32(SIAStatus)); + return 0; +} + +static void de_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *data) +{ + struct de_private *de = netdev_priv(dev); + + regs->version = (DE_REGS_VER << 2) | de->de21040; + + spin_lock_irq(&de->lock); + __de_get_regs(de, data); + spin_unlock_irq(&de->lock); +} + +static const struct ethtool_ops de_ethtool_ops = { + .get_link = ethtool_op_get_link, + .get_drvinfo = de_get_drvinfo, + .get_regs_len = de_get_regs_len, + .get_settings = de_get_settings, + .set_settings = de_set_settings, + .get_msglevel = de_get_msglevel, + .set_msglevel = de_set_msglevel, + .get_eeprom = de_get_eeprom, + .nway_reset = de_nway_reset, + .get_regs = de_get_regs, +}; + +static void __devinit de21040_get_mac_address (struct de_private *de) +{ + unsigned i; + + dw32 (ROMCmd, 0); /* Reset the pointer with a dummy write. */ + udelay(5); + + for (i = 0; i < 6; i++) { + int value, boguscnt = 100000; + do { + value = dr32(ROMCmd); + rmb(); + } while (value < 0 && --boguscnt > 0); + de->dev->dev_addr[i] = value; + udelay(1); + if (boguscnt <= 0) + pr_warn("timeout reading 21040 MAC address byte %u\n", + i); + } +} + +static void __devinit de21040_get_media_info(struct de_private *de) +{ + unsigned int i; + + de->media_type = DE_MEDIA_TP; + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full | + SUPPORTED_10baseT_Half | SUPPORTED_AUI; + de->media_advertise = de->media_supported; + + for (i = 0; i < DE_MAX_MEDIA; i++) { + switch (i) { + case DE_MEDIA_AUI: + case DE_MEDIA_TP: + case DE_MEDIA_TP_FD: + de->media[i].type = i; + de->media[i].csr13 = t21040_csr13[i]; + de->media[i].csr14 = t21040_csr14[i]; + de->media[i].csr15 = t21040_csr15[i]; + break; + default: + de->media[i].type = DE_MEDIA_INVALID; + break; + } + } +} + +/* Note: this routine returns extra data bits for size detection. */ +static unsigned __devinit tulip_read_eeprom(void __iomem *regs, int location, int addr_len) +{ + int i; + unsigned retval = 0; + void __iomem *ee_addr = regs + ROMCmd; + int read_cmd = location | (EE_READ_CMD << addr_len); + + writel(EE_ENB & ~EE_CS, ee_addr); + writel(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + writel(EE_ENB | dataval, ee_addr); + readl(ee_addr); + writel(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + readl(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); + } + writel(EE_ENB, ee_addr); + readl(ee_addr); + + for (i = 16; i > 0; i--) { + writel(EE_ENB | EE_SHIFT_CLK, ee_addr); + readl(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); + writel(EE_ENB, ee_addr); + readl(ee_addr); + } + + /* Terminate the EEPROM access. */ + writel(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +static void __devinit de21041_get_srom_info (struct de_private *de) +{ + unsigned i, sa_offset = 0, ofs; + u8 ee_data[DE_EEPROM_SIZE + 6] = {}; + unsigned ee_addr_size = tulip_read_eeprom(de->regs, 0xff, 8) & 0x40000 ? 8 : 6; + struct de_srom_info_leaf *il; + void *bufp; + + /* download entire eeprom */ + for (i = 0; i < DE_EEPROM_WORDS; i++) + ((__le16 *)ee_data)[i] = + cpu_to_le16(tulip_read_eeprom(de->regs, i, ee_addr_size)); + + /* DEC now has a specification but early board makers + just put the address in the first EEPROM locations. */ + /* This does memcmp(eedata, eedata+16, 8) */ + +#ifndef CONFIG_MIPS_COBALT + + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + +#endif + + /* store MAC address */ + for (i = 0; i < 6; i ++) + de->dev->dev_addr[i] = ee_data[i + sa_offset]; + + /* get offset of controller 0 info leaf. ignore 2nd byte. */ + ofs = ee_data[SROMC0InfoLeaf]; + if (ofs >= (sizeof(ee_data) - sizeof(struct de_srom_info_leaf) - sizeof(struct de_srom_media_block))) + goto bad_srom; + + /* get pointer to info leaf */ + il = (struct de_srom_info_leaf *) &ee_data[ofs]; + + /* paranoia checks */ + if (il->n_blocks == 0) + goto bad_srom; + if ((sizeof(ee_data) - ofs) < + (sizeof(struct de_srom_info_leaf) + (sizeof(struct de_srom_media_block) * il->n_blocks))) + goto bad_srom; + + /* get default media type */ + switch (get_unaligned(&il->default_media)) { + case 0x0001: de->media_type = DE_MEDIA_BNC; break; + case 0x0002: de->media_type = DE_MEDIA_AUI; break; + case 0x0204: de->media_type = DE_MEDIA_TP_FD; break; + default: de->media_type = DE_MEDIA_TP_AUTO; break; + } + + if (netif_msg_probe(de)) + pr_info("de%d: SROM leaf offset %u, default media %s\n", + de->board_idx, ofs, media_name[de->media_type]); + + /* init SIA register values to defaults */ + for (i = 0; i < DE_MAX_MEDIA; i++) { + de->media[i].type = DE_MEDIA_INVALID; + de->media[i].csr13 = 0xffff; + de->media[i].csr14 = 0xffff; + de->media[i].csr15 = 0xffff; + } + + /* parse media blocks to see what medias are supported, + * and if any custom CSR values are provided + */ + bufp = ((void *)il) + sizeof(*il); + for (i = 0; i < il->n_blocks; i++) { + struct de_srom_media_block *ib = bufp; + unsigned idx; + + /* index based on media type in media block */ + switch(ib->opts & MediaBlockMask) { + case 0: /* 10baseT */ + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half + | SUPPORTED_Autoneg; + idx = DE_MEDIA_TP; + de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; + break; + case 1: /* BNC */ + de->media_supported |= SUPPORTED_BNC; + idx = DE_MEDIA_BNC; + break; + case 2: /* AUI */ + de->media_supported |= SUPPORTED_AUI; + idx = DE_MEDIA_AUI; + break; + case 4: /* 10baseT-FD */ + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full + | SUPPORTED_Autoneg; + idx = DE_MEDIA_TP_FD; + de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; + break; + default: + goto bad_srom; + } + + de->media[idx].type = idx; + + if (netif_msg_probe(de)) + pr_info("de%d: media block #%u: %s", + de->board_idx, i, + media_name[de->media[idx].type]); + + bufp += sizeof (ib->opts); + + if (ib->opts & MediaCustomCSRs) { + de->media[idx].csr13 = get_unaligned(&ib->csr13); + de->media[idx].csr14 = get_unaligned(&ib->csr14); + de->media[idx].csr15 = get_unaligned(&ib->csr15); + bufp += sizeof(ib->csr13) + sizeof(ib->csr14) + + sizeof(ib->csr15); + + if (netif_msg_probe(de)) + pr_cont(" (%x,%x,%x)\n", + de->media[idx].csr13, + de->media[idx].csr14, + de->media[idx].csr15); + + } else { + if (netif_msg_probe(de)) + pr_cont("\n"); + } + + if (bufp > ((void *)&ee_data[DE_EEPROM_SIZE - 3])) + break; + } + + de->media_advertise = de->media_supported; + +fill_defaults: + /* fill in defaults, for cases where custom CSRs not used */ + for (i = 0; i < DE_MAX_MEDIA; i++) { + if (de->media[i].csr13 == 0xffff) + de->media[i].csr13 = t21041_csr13[i]; + if (de->media[i].csr14 == 0xffff) { + /* autonegotiation is broken at least on some chip + revisions - rev. 0x21 works, 0x11 does not */ + if (de->pdev->revision < 0x20) + de->media[i].csr14 = t21041_csr14_brk[i]; + else + de->media[i].csr14 = t21041_csr14[i]; + } + if (de->media[i].csr15 == 0xffff) + de->media[i].csr15 = t21041_csr15[i]; + } + + de->ee_data = kmemdup(&ee_data[0], DE_EEPROM_SIZE, GFP_KERNEL); + + return; + +bad_srom: + /* for error cases, it's ok to assume we support all these */ + for (i = 0; i < DE_MAX_MEDIA; i++) + de->media[i].type = i; + de->media_supported = + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | + SUPPORTED_AUI | + SUPPORTED_BNC; + goto fill_defaults; +} + +static const struct net_device_ops de_netdev_ops = { + .ndo_open = de_open, + .ndo_stop = de_close, + .ndo_set_multicast_list = de_set_rx_mode, + .ndo_start_xmit = de_start_xmit, + .ndo_get_stats = de_get_stats, + .ndo_tx_timeout = de_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __devinit de_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + struct de_private *de; + int rc; + void __iomem *regs; + unsigned long pciaddr; + static int board_idx = -1; + + board_idx++; + +#ifndef MODULE + if (board_idx == 0) + pr_info("%s\n", version); +#endif + + /* allocate a new ethernet device structure, and fill in defaults */ + dev = alloc_etherdev(sizeof(struct de_private)); + if (!dev) + return -ENOMEM; + + dev->netdev_ops = &de_netdev_ops; + SET_NETDEV_DEV(dev, &pdev->dev); + dev->ethtool_ops = &de_ethtool_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + de = netdev_priv(dev); + de->de21040 = ent->driver_data == 0 ? 1 : 0; + de->pdev = pdev; + de->dev = dev; + de->msg_enable = (debug < 0 ? DE_DEF_MSG_ENABLE : debug); + de->board_idx = board_idx; + spin_lock_init (&de->lock); + init_timer(&de->media_timer); + if (de->de21040) + de->media_timer.function = de21040_media_timer; + else + de->media_timer.function = de21041_media_timer; + de->media_timer.data = (unsigned long) de; + + netif_carrier_off(dev); + + /* wake up device, assign resources */ + rc = pci_enable_device(pdev); + if (rc) + goto err_out_free; + + /* reserve PCI resources to ensure driver atomicity */ + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out_disable; + + /* check for invalid IRQ value */ + if (pdev->irq < 2) { + rc = -EIO; + pr_err("invalid irq (%d) for pci dev %s\n", + pdev->irq, pci_name(pdev)); + goto err_out_res; + } + + dev->irq = pdev->irq; + + /* obtain and check validity of PCI I/O address */ + pciaddr = pci_resource_start(pdev, 1); + if (!pciaddr) { + rc = -EIO; + pr_err("no MMIO resource for pci dev %s\n", pci_name(pdev)); + goto err_out_res; + } + if (pci_resource_len(pdev, 1) < DE_REGS_SIZE) { + rc = -EIO; + pr_err("MMIO resource (%llx) too small on pci dev %s\n", + (unsigned long long)pci_resource_len(pdev, 1), + pci_name(pdev)); + goto err_out_res; + } + + /* remap CSR registers */ + regs = ioremap_nocache(pciaddr, DE_REGS_SIZE); + if (!regs) { + rc = -EIO; + pr_err("Cannot map PCI MMIO (%llx@%lx) on pci dev %s\n", + (unsigned long long)pci_resource_len(pdev, 1), + pciaddr, pci_name(pdev)); + goto err_out_res; + } + dev->base_addr = (unsigned long) regs; + de->regs = regs; + + de_adapter_wake(de); + + /* make sure hardware is not running */ + rc = de_reset_mac(de); + if (rc) { + pr_err("Cannot reset MAC, pci dev %s\n", pci_name(pdev)); + goto err_out_iomap; + } + + /* get MAC address, initialize default media type and + * get list of supported media + */ + if (de->de21040) { + de21040_get_mac_address(de); + de21040_get_media_info(de); + } else { + de21041_get_srom_info(de); + } + + /* register new network interface with kernel */ + rc = register_netdev(dev); + if (rc) + goto err_out_iomap; + + /* print info about board and interface just registered */ + netdev_info(dev, "%s at 0x%lx, %pM, IRQ %d\n", + de->de21040 ? "21040" : "21041", + dev->base_addr, + dev->dev_addr, + dev->irq); + + pci_set_drvdata(pdev, dev); + + /* enable busmastering */ + pci_set_master(pdev); + + /* put adapter to sleep */ + de_adapter_sleep(de); + + return 0; + +err_out_iomap: + kfree(de->ee_data); + iounmap(regs); +err_out_res: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_free: + free_netdev(dev); + return rc; +} + +static void __devexit de_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct de_private *de = netdev_priv(dev); + + BUG_ON(!dev); + unregister_netdev(dev); + kfree(de->ee_data); + iounmap(de->regs); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + free_netdev(dev); +} + +#ifdef CONFIG_PM + +static int de_suspend (struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct de_private *de = netdev_priv(dev); + + rtnl_lock(); + if (netif_running (dev)) { + del_timer_sync(&de->media_timer); + + disable_irq(dev->irq); + spin_lock_irq(&de->lock); + + de_stop_hw(de); + netif_stop_queue(dev); + netif_device_detach(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&de->lock); + enable_irq(dev->irq); + + /* Update the error counts. */ + __de_get_stats(de); + + synchronize_irq(dev->irq); + de_clean_rings(de); + + de_adapter_sleep(de); + pci_disable_device(pdev); + } else { + netif_device_detach(dev); + } + rtnl_unlock(); + return 0; +} + +static int de_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct de_private *de = netdev_priv(dev); + int retval = 0; + + rtnl_lock(); + if (netif_device_present(dev)) + goto out; + if (!netif_running(dev)) + goto out_attach; + if ((retval = pci_enable_device(pdev))) { + netdev_err(dev, "pci_enable_device failed in resume\n"); + goto out; + } + pci_set_master(pdev); + de_init_rings(de); + de_init_hw(de); +out_attach: + netif_device_attach(dev); +out: + rtnl_unlock(); + return 0; +} + +#endif /* CONFIG_PM */ + +static struct pci_driver de_driver = { + .name = DRV_NAME, + .id_table = de_pci_tbl, + .probe = de_init_one, + .remove = __devexit_p(de_remove_one), +#ifdef CONFIG_PM + .suspend = de_suspend, + .resume = de_resume, +#endif +}; + +static int __init de_init (void) +{ +#ifdef MODULE + pr_info("%s\n", version); +#endif + return pci_register_driver(&de_driver); +} + +static void __exit de_exit (void) +{ + pci_unregister_driver (&de_driver); +} + +module_init(de_init); +module_exit(de_exit); diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c new file mode 100644 index 000000000000..959b41021a65 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -0,0 +1,5599 @@ +/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500 + ethernet driver for Linux. + + Copyright 1994, 1995 Digital Equipment Corporation. + + Testing resources for this driver have been made available + in part by NASA Ames Research Center (mjacob@nas.nasa.gov). + + The author may be reached at davies@maniac.ultranet.com. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Originally, this driver was written for the Digital Equipment + Corporation series of EtherWORKS ethernet cards: + + DE425 TP/COAX EISA + DE434 TP PCI + DE435 TP/COAX/AUI PCI + DE450 TP/COAX/AUI PCI + DE500 10/100 PCI Fasternet + + but it will now attempt to support all cards which conform to the + Digital Semiconductor SROM Specification. The driver currently + recognises the following chips: + + DC21040 (no SROM) + DC21041[A] + DC21140[A] + DC21142 + DC21143 + + So far the driver is known to work with the following cards: + + KINGSTON + Linksys + ZNYX342 + SMC8432 + SMC9332 (w/new SROM) + ZNYX31[45] + ZNYX346 10/100 4 port (can act as a 10/100 bridge!) + + The driver has been tested on a relatively busy network using the DE425, + DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred + 16M of data to a DECstation 5000/200 as follows: + + TCP UDP + TX RX TX RX + DE425 1030k 997k 1170k 1128k + DE434 1063k 995k 1170k 1125k + DE435 1063k 995k 1170k 1125k + DE500 1063k 998k 1170k 1125k in 10Mb/s mode + + All values are typical (in kBytes/sec) from a sample of 4 for each + measurement. Their error is +/-20k on a quiet (private) network and also + depend on what load the CPU has. + + ========================================================================= + This driver has been written substantially from scratch, although its + inheritance of style and stack interface from 'ewrk3.c' and in turn from + Donald Becker's 'lance.c' should be obvious. With the module autoload of + every usable DECchip board, I pinched Donald's 'next_module' field to + link my modules together. + + Up to 15 EISA cards can be supported under this driver, limited primarily + by the available IRQ lines. I have checked different configurations of + multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a + problem yet (provided you have at least depca.c v0.38) ... + + PCI support has been added to allow the driver to work with the DE434, + DE435, DE450 and DE500 cards. The I/O accesses are a bit of a kludge due + to the differences in the EISA and PCI CSR address offsets from the base + address. + + 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). Loadable module support under PCI and EISA has been + achieved by letting the driver autoprobe as if it were compiled into the + kernel. Do make sure you're not sharing interrupts with anything that + cannot accommodate interrupt sharing! + + 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 de4x5.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) for fixed autoprobes (not recommended), edit the source code near + line 5594 to reflect the I/O address you're using, or assign these when + loading by: + + insmod de4x5 io=0xghh where g = bus number + hh = device number + + NB: autoprobing for modules is now supported by default. You may just + use: + + insmod de4x5 + + to load all available boards. For a specific board, still use + the 'io=?' above. + 3) compile de4x5.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 de4x5 configuration turned off and reboot. + 5) insmod de4x5 [io=0xghh] + 6) run the net startup bits for your new eth?? interface(s) manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + To unload a module, turn off the associated interface(s) + 'ifconfig eth?? down' then 'rmmod de4x5'. + + Automedia detection is included so that in principal you can disconnect + from, e.g. TP, reconnect to BNC and things will still work (after a + pause whilst the driver figures out where its media went). My tests + using ping showed that it appears to work.... + + By default, the driver will now autodetect any DECchip based card. + Should you have a need to restrict the driver to DIGITAL only cards, you + can compile with a DEC_ONLY define, or if loading as a module, use the + 'dec_only=1' parameter. + + I've changed the timing routines to use the kernel timer and scheduling + functions so that the hangs and other assorted problems that occurred + while autosensing the media should be gone. A bonus for the DC21040 + auto media sense algorithm is that it can now use one that is more in + line with the rest (the DC21040 chip doesn't have a hardware timer). + The downside is the 1 'jiffies' (10ms) resolution. + + IEEE 802.3u MII interface code has been added in anticipation that some + products may use it in the future. + + The SMC9332 card has a non-compliant SROM which needs fixing - I have + patched this driver to detect it because the SROM format used complies + to a previous DEC-STD format. + + I have removed the buffer copies needed for receive on Intels. I cannot + remove them for Alphas since the Tulip hardware only does longword + aligned DMA transfers and the Alphas get alignment traps with non + longword aligned data copies (which makes them really slow). No comment. + + I have added SROM decoding routines to make this driver work with any + card that supports the Digital Semiconductor SROM spec. This will help + all cards running the dc2114x series chips in particular. Cards using + the dc2104x chips should run correctly with the basic driver. I'm in + debt to for the testing and feedback that helped get + this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 + (with the latest SROM complying with the SROM spec V3: their first was + broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 + (quad 21041 MAC) cards also appear to work despite their incorrectly + wired IRQs. + + I have added a temporary fix for interrupt problems when some SCSI cards + share the same interrupt as the DECchip based cards. The problem occurs + because the SCSI card wants to grab the interrupt as a fast interrupt + (runs the service routine with interrupts turned off) vs. this card + which really needs to run the service routine with interrupts turned on. + This driver will now add the interrupt service routine as a fast + interrupt if it is bounced from the slow interrupt. THIS IS NOT A + RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time + until people sort out their compatibility issues and the kernel + interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST + INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not + run on the same interrupt. PCMCIA/CardBus is another can of worms... + + Finally, I think I have really fixed the module loading problem with + more than one DECchip based card. As a side effect, I don't mess with + the device structure any more which means that if more than 1 card in + 2.0.x is installed (4 in 2.1.x), the user will have to edit + linux/drivers/net/Space.c to make room for them. Hence, module loading + is the preferred way to use this driver, since it doesn't have this + limitation. + + Where SROM media detection is used and full duplex is specified in the + SROM, the feature is ignored unless lp->params.fdx is set at compile + time OR during a module load (insmod de4x5 args='eth??:fdx' [see + below]). This is because there is no way to automatically detect full + duplex links except through autonegotiation. When I include the + autonegotiation feature in the SROM autoconf code, this detection will + occur automatically for that case. + + Command line arguments are now allowed, similar to passing arguments + through LILO. This will allow a per adapter board set up of full duplex + and media. The only lexical constraints are: the board name (dev->name) + appears in the list before its parameters. The list of parameters ends + either at the end of the parameter list or with another board name. The + following parameters are allowed: + + fdx for full duplex + autosense to set the media/speed; with the following + sub-parameters: + TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO + + Case sensitivity is important for the sub-parameters. They *must* be + upper case. Examples: + + insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. + + For a compiled in driver, at or above line 548, place e.g. + #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" + + Yes, I know full duplex isn't permissible on BNC or AUI; they're just + examples. By default, full duplex is turned off and AUTO is the default + autosense setting. In reality, I expect only the full duplex option to + be used. Note the use of single quotes in the two examples above and the + lack of commas to separate items. ALSO, you must get the requested media + correct in relation to what the adapter SROM says it has. There's no way + to determine this in advance other than by trial and error and common + sense, e.g. call a BNC connectored port 'BNC', not '10Mb'. + + Changed the bus probing. EISA used to be done first, followed by PCI. + Most people probably don't even know what a de425 is today and the EISA + probe has messed up some SCSI cards in the past, so now PCI is always + probed first followed by EISA if a) the architecture allows EISA and + either b) there have been no PCI cards detected or c) an EISA probe is + forced by the user. To force a probe include "force_eisa" in your + insmod "args" line; for built-in kernels either change the driver to do + this automatically or include #define DE4X5_FORCE_EISA on or before + line 1040 in the driver. + + TO DO: + ------ + + Revision History + ---------------- + + Version Date Description + + 0.1 17-Nov-94 Initial writing. ALPHA code release. + 0.2 13-Jan-95 Added PCI support for DE435's. + 0.21 19-Jan-95 Added auto media detection. + 0.22 10-Feb-95 Fix interrupt handler call . + Fix recognition bug reported by . + Add request/release_region code. + Add loadable modules support for PCI. + Clean up loadable modules support. + 0.23 28-Feb-95 Added DC21041 and DC21140 support. + Fix missed frame counter value and initialisation. + Fixed EISA probe. + 0.24 11-Apr-95 Change delay routine to use . + Change TX_BUFFS_AVAIL macro. + Change media autodetection to allow manual setting. + Completed DE500 (DC21140) support. + 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm. + 0.242 10-May-95 Minor changes. + 0.30 12-Jun-95 Timer fix for DC21140. + Portability changes. + Add ALPHA changes from . + Add DE500 semi automatic autosense. + Add Link Fail interrupt TP failure detection. + Add timer based link change detection. + Plugged a memory leak in de4x5_queue_pkt(). + 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1. + 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from a + suggestion by . + 0.33 8-Aug-95 Add shared interrupt support (not released yet). + 0.331 21-Aug-95 Fix de4x5_open() with fast CPUs. + Fix de4x5_interrupt(). + Fix dc21140_autoconf() mess. + No shared interrupt support. + 0.332 11-Sep-95 Added MII management interface routines. + 0.40 5-Mar-96 Fix setup frame timeout . + Add kernel timer code (h/w is too flaky). + Add MII based PHY autosense. + Add new multicasting code. + Add new autosense algorithms for media/mode + selection using kernel scheduling/timing. + Re-formatted. + Made changes suggested by : + Change driver to detect all DECchip based cards + with DEC_ONLY restriction a special case. + Changed driver to autoprobe as a module. No irq + checking is done now - assume BIOS is good! + Added SMC9332 detection + 0.41 21-Mar-96 Don't check for get_hw_addr checksum unless DEC card + only + Fix for multiple PCI cards reported by + Duh, put the IRQF_SHARED flag into request_interrupt(). + Fix SMC ethernet address in enet_det[]. + Print chip name instead of "UNKNOWN" during boot. + 0.42 26-Apr-96 Fix MII write TA bit error. + Fix bug in dc21040 and dc21041 autosense code. + Remove buffer copies on receive for Intels. + Change sk_buff handling during media disconnects to + eliminate DUP packets. + Add dynamic TX thresholding. + Change all chips to use perfect multicast filtering. + Fix alloc_device() bug + 0.43 21-Jun-96 Fix unconnected media TX retry bug. + Add Accton to the list of broken cards. + Fix TX under-run bug for non DC21140 chips. + Fix boot command probe bug in alloc_device() as + reported by and + . + Add cache locks to prevent a race condition as + reported by and + . + Upgraded alloc_device() code. + 0.431 28-Jun-96 Fix potential bug in queue_pkt() from discussion + with + 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips. + Fix EISA probe bugs reported by + and . + 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media + with a loopback packet. + 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported + by + 0.45 8-Dec-96 Include endian functions for PPC use, from work + by and . + 0.451 28-Dec-96 Added fix to allow autoprobe for modules after + suggestion from . + 0.5 30-Jan-97 Added SROM decoding functions. + Updated debug flags. + Fix sleep/wakeup calls for PCI cards, bug reported + by . + Added multi-MAC, one SROM feature from discussion + with . + Added full module autoprobe capability. + Added attempt to use an SMC9332 with broken SROM. + Added fix for ZYNX multi-mac cards that didn't + get their IRQs wired correctly. + 0.51 13-Feb-97 Added endian fixes for the SROM accesses from + + Fix init_connection() to remove extra device reset. + Fix MAC/PHY reset ordering in dc21140m_autoconf(). + Fix initialisation problem with lp->timeout in + typeX_infoblock() from . + Fix MII PHY reset problem from work done by + . + 0.52 26-Apr-97 Some changes may not credit the right people - + a disk crash meant I lost some mail. + Change RX interrupt routine to drop rather than + defer packets to avoid hang reported by + . + Fix srom_exec() to return for COMPACT and type 1 + infoblocks. + Added DC21142 and DC21143 functions. + Added byte counters from + Added IRQF_DISABLED temporary fix from + . + 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during + module load: bug reported by + + Fix multi-MAC, one SROM, to work with 2114x chips: + bug reported by . + Make above search independent of BIOS device scan + direction. + Completed DC2114[23] autosense functions. + 0.531 21-Dec-97 Fix DE500-XA 100Mb/s bug reported by + and + . + Added argument list to set up each board from either + a module's command line or a compiled in #define. + Added generic MII PHY functionality to deal with + newer PHY chips. + Fix the mess in 2.1.67. + 0.532 5-Jan-98 Fix bug in mii_get_phy() reported by + . + Fix bug in pci_probe() for 64 bit systems reported + by . + 0.533 9-Jan-98 Fix more 64 bit bugs reported by . + 0.534 24-Jan-98 Fix last (?) endian bug from + 0.535 21-Feb-98 Fix Ethernet Address PROM reset bug for DC21040. + 0.536 21-Mar-98 Change pci_probe() to use the pci_dev structure. + **Incompatible with 2.0.x from here.** + 0.540 5-Jul-98 Atomicize assertion of dev->interrupt for SMP + from + Add TP, AUI and BNC cases to 21140m_autoconf() for + case where a 21140 under SROM control uses, e.g. AUI + from problem report by + Add MII parallel detection to 2114x_autoconf() for + case where no autonegotiation partner exists from + problem report by . + Add ability to force connection type directly even + when using SROM control from problem report by + . + Updated the PCI interface to conform with the latest + version. I hope nothing is broken... + Add TX done interrupt modification from suggestion + by . + Fix is_anc_capable() bug reported by + . + Fix type[13]_infoblock() bug: during MII search, PHY + lp->rst not run because lp->ibn not initialised - + from report & fix by . + Fix probe bug with EISA & PCI cards present from + report by . + 0.541 24-Aug-98 Fix compiler problems associated with i386-string + ops from multiple bug reports and temporary fix + from . + Fix pci_probe() to correctly emulate the old + pcibios_find_class() function. + Add an_exception() for old ZYNX346 and fix compile + warning on PPC & SPARC, from . + Fix lastPCI to correctly work with compiled in + kernels and modules from bug report by + et al. + 0.542 15-Sep-98 Fix dc2114x_autoconf() to stop multiple messages + when media is unconnected. + Change dev->interrupt to lp->interrupt to ensure + alignment for Alpha's and avoid their unaligned + access traps. This flag is merely for log messages: + should do something more definitive though... + 0.543 30-Dec-98 Add SMP spin locking. + 0.544 8-May-99 Fix for buggy SROM in Motorola embedded boards using + a 21143 by . + Change PCI/EISA bus probing order. + 0.545 28-Nov-99 Further Moto SROM bug fix from + + Remove double checking for DEBUG_RX in de4x5_dbg_rx() + from report by + 0.546 22-Feb-01 Fixes Alpha XP1000 oops. The srom_search function + was causing a page fault when initializing the + variable 'pb', on a non de4x5 PCI device, in this + case a PCI bridge (DEC chip 21152). The value of + 'pb' is now only initialized if a de4x5 chip is + present. + + 0.547 08-Nov-01 Use library crc32 functions by + 0.548 30-Aug-03 Big 2.6 cleanup. Ported to PCI/EISA probing and + generic DMA APIs. Fixed DE425 support on Alpha. + + ========================================================================= +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC_PMAC +#include +#endif /* CONFIG_PPC_PMAC */ + +#include "de4x5.h" + +static const char version[] __devinitconst = + KERN_INFO "de4x5.c:V0.546 2001/02/22 davies@maniac.ultranet.com\n"; + +#define c_char const char + +/* +** MII Information +*/ +struct phy_table { + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ + int ta; /* One cycle TA time - 802.3u is confusing here */ + struct { /* Non autonegotiation (parallel) speed det. */ + int reg; + int mask; + int value; + } spd; +}; + +struct mii_phy { + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ + int ta; /* One cycle TA time */ + struct { /* Non autonegotiation (parallel) speed det. */ + int reg; + int mask; + int value; + } spd; + int addr; /* MII address for the PHY */ + u_char *gep; /* Start of GEP sequence block in SROM */ + u_char *rst; /* Start of reset sequence in SROM */ + u_int mc; /* Media Capabilities */ + u_int ana; /* NWay Advertisement */ + u_int fdx; /* Full DupleX capabilities for each media */ + u_int ttm; /* Transmit Threshold Mode for each media */ + u_int mci; /* 21142 MII Connector Interrupt info */ +}; + +#define DE4X5_MAX_PHY 8 /* Allow up to 8 attached PHY devices per board */ + +struct sia_phy { + u_char mc; /* Media Code */ + u_char ext; /* csr13-15 valid when set */ + int csr13; /* SIA Connectivity Register */ + int csr14; /* SIA TX/RX Register */ + int csr15; /* SIA General Register */ + int gepc; /* SIA GEP Control Information */ + int gep; /* SIA GEP Data */ +}; + +/* +** Define the know universe of PHY devices that can be +** recognised by this driver. +*/ +static struct phy_table phy_info[] = { + {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ + {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ + {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ + {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}, /* Cypress T4 */ + {0, 0x7810 , 1, {0x14, 0x0800, 0x0800}} /* Level One LTX970 */ +}; + +/* +** These GENERIC values assumes that the PHY devices follow 802.3u and +** allow parallel detection to set the link partner ability register. +** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. +*/ +#define GENERIC_REG 0x05 /* Autoneg. Link Partner Advertisement Reg. */ +#define GENERIC_MASK MII_ANLPA_100M /* All 100Mb/s Technologies */ +#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4 */ + +/* +** Define special SROM detection cases +*/ +static c_char enet_det[][ETH_ALEN] = { + {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00} +}; + +#define SMC 1 +#define ACCTON 2 + +/* +** SROM Repair definitions. If a broken SROM is detected a card may +** use this information to help figure out what to do. This is a +** "stab in the dark" and so far for SMC9332's only. +*/ +static c_char srom_repair_info[][100] = { + {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */ + 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02, + 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50, + 0x00,0x18,} +}; + + +#ifdef DE4X5_DEBUG +static int de4x5_debug = DE4X5_DEBUG; +#else +/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/ +static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); +#endif + +/* +** Allow per adapter set up. For modules this is simply a command line +** parameter, e.g.: +** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. +** +** For a compiled in driver, place e.g. +** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" +** here +*/ +#ifdef DE4X5_PARM +static char *args = DE4X5_PARM; +#else +static char *args; +#endif + +struct parameters { + bool fdx; + int autosense; +}; + +#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ + +#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */ + +/* +** Ethernet PROM defines +*/ +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +/* +** Ethernet Info +*/ +#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */ +#define IEEE802_3_SZ 1518 /* Packet + CRC */ +#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */ +#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */ +#define MIN_DAT_SZ 1 /* Minimum ethernet data length */ +#define PKT_HDR_LEN 14 /* Addresses and data length info */ +#define FAKE_FRAME_LEN (MAX_PKT_SZ + 1) +#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */ + + +/* +** EISA bus defines +*/ +#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ +#define DE4X5_EISA_TOTAL_SIZE 0x100 /* I/O address extent */ + +#define EISA_ALLOWED_IRQ_LIST {5, 9, 10, 11} + +#define DE4X5_SIGNATURE {"DE425","DE434","DE435","DE450","DE500"} +#define DE4X5_NAME_LENGTH 8 + +static c_char *de4x5_signatures[] = DE4X5_SIGNATURE; + +/* +** Ethernet PROM defines for DC21040 +*/ +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +/* +** PCI Bus defines +*/ +#define PCI_MAX_BUS_NUM 8 +#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */ +#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */ + +/* +** Memory Alignment. Each descriptor is 4 longwords long. To force a +** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and +** DESC_ALIGN. ALIGN aligns the start address of the private memory area +** and hence the RX descriptor ring's first entry. +*/ +#define DE4X5_ALIGN4 ((u_long)4 - 1) /* 1 longword align */ +#define DE4X5_ALIGN8 ((u_long)8 - 1) /* 2 longword align */ +#define DE4X5_ALIGN16 ((u_long)16 - 1) /* 4 longword align */ +#define DE4X5_ALIGN32 ((u_long)32 - 1) /* 8 longword align */ +#define DE4X5_ALIGN64 ((u_long)64 - 1) /* 16 longword align */ +#define DE4X5_ALIGN128 ((u_long)128 - 1) /* 32 longword align */ + +#define DE4X5_ALIGN DE4X5_ALIGN32 /* Keep the DC21040 happy... */ +#define DE4X5_CACHE_ALIGN CAL_16LONG +#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */ +/*#define DESC_ALIGN u32 dummy[4]; / * Must agree with DESC_SKIP_LEN */ +#define DESC_ALIGN + +#ifndef DEC_ONLY /* See README.de4x5 for using this */ +static int dec_only; +#else +static int dec_only = 1; +#endif + +/* +** DE4X5 IRQ ENABLE/DISABLE +*/ +#define ENABLE_IRQs { \ + imr |= lp->irq_en;\ + outl(imr, DE4X5_IMR); /* Enable the IRQs */\ +} + +#define DISABLE_IRQs {\ + imr = inl(DE4X5_IMR);\ + imr &= ~lp->irq_en;\ + outl(imr, DE4X5_IMR); /* Disable the IRQs */\ +} + +#define UNMASK_IRQs {\ + imr |= lp->irq_mask;\ + outl(imr, DE4X5_IMR); /* Unmask the IRQs */\ +} + +#define MASK_IRQs {\ + imr = inl(DE4X5_IMR);\ + imr &= ~lp->irq_mask;\ + outl(imr, DE4X5_IMR); /* Mask the IRQs */\ +} + +/* +** DE4X5 START/STOP +*/ +#define START_DE4X5 {\ + omr = inl(DE4X5_OMR);\ + omr |= OMR_ST | OMR_SR;\ + outl(omr, DE4X5_OMR); /* Enable the TX and/or RX */\ +} + +#define STOP_DE4X5 {\ + omr = inl(DE4X5_OMR);\ + omr &= ~(OMR_ST|OMR_SR);\ + outl(omr, DE4X5_OMR); /* Disable the TX and/or RX */ \ +} + +/* +** DE4X5 SIA RESET +*/ +#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */ + +/* +** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS) +*/ +#define DE4X5_AUTOSENSE_MS 250 + +/* +** SROM Structure +*/ +struct de4x5_srom { + char sub_vendor_id[2]; + char sub_system_id[2]; + char reserved[12]; + char id_block_crc; + char reserved2; + char version; + char num_controllers; + char ieee_addr[6]; + char info[100]; + short chksum; +}; +#define SUB_VENDOR_ID 0x500a + +/* +** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous +** and have sizes of both a power of 2 and a multiple of 4. +** A size of 256 bytes for each buffer could be chosen because over 90% of +** all packets in our network are <256 bytes long and 64 longword alignment +** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX +** descriptors are needed for machines with an ALPHA CPU. +*/ +#define NUM_RX_DESC 8 /* Number of RX descriptors */ +#define NUM_TX_DESC 32 /* Number of TX descriptors */ +#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */ + /* Multiple of 4 for DC21040 */ + /* Allows 512 byte alignment */ +struct de4x5_desc { + volatile __le32 status; + __le32 des1; + __le32 buf; + __le32 next; + DESC_ALIGN +}; + +/* +** The DE4X5 private structure +*/ +#define DE4X5_PKT_STAT_SZ 16 +#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you + increase DE4X5_PKT_STAT_SZ */ + +struct pkt_stats { + u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */ + u_int unicast; + u_int multicast; + u_int broadcast; + u_int excessive_collisions; + u_int tx_underruns; + u_int excessive_underruns; + u_int rx_runt_frames; + u_int rx_collision; + u_int rx_dribble; + u_int rx_overflow; +}; + +struct de4x5_private { + char adapter_name[80]; /* Adapter name */ + u_long interrupt; /* Aligned ISR flag */ + struct de4x5_desc *rx_ring; /* RX descriptor ring */ + struct de4x5_desc *tx_ring; /* TX descriptor ring */ + struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */ + struct sk_buff *rx_skb[NUM_RX_DESC]; /* RX skb's */ + int rx_new, rx_old; /* RX descriptor ring pointers */ + int tx_new, tx_old; /* TX descriptor ring pointers */ + char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */ + char frame[64]; /* Min sized packet for loopback*/ + spinlock_t lock; /* Adapter specific spinlock */ + struct net_device_stats stats; /* Public stats */ + struct pkt_stats pktStats; /* Private stats counters */ + char rxRingSize; + char txRingSize; + int bus; /* EISA or PCI */ + int bus_num; /* PCI Bus number */ + int device; /* Device number on PCI bus */ + int state; /* Adapter OPENED or CLOSED */ + int chipset; /* DC21040, DC21041 or DC21140 */ + s32 irq_mask; /* Interrupt Mask (Enable) bits */ + s32 irq_en; /* Summary interrupt bits */ + int media; /* Media (eg TP), mode (eg 100B)*/ + int c_media; /* Remember the last media conn */ + bool fdx; /* media full duplex flag */ + int linkOK; /* Link is OK */ + int autosense; /* Allow/disallow autosensing */ + bool tx_enable; /* Enable descriptor polling */ + int setup_f; /* Setup frame filtering type */ + int local_state; /* State within a 'media' state */ + struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */ + struct sia_phy sia; /* SIA PHY Information */ + int active; /* Index to active PHY device */ + int mii_cnt; /* Number of attached PHY's */ + int timeout; /* Scheduling counter */ + struct timer_list timer; /* Timer info for kernel */ + int tmp; /* Temporary global per card */ + struct { + u_long lock; /* Lock the cache accesses */ + s32 csr0; /* Saved Bus Mode Register */ + s32 csr6; /* Saved Operating Mode Reg. */ + s32 csr7; /* Saved IRQ Mask Register */ + s32 gep; /* Saved General Purpose Reg. */ + s32 gepc; /* Control info for GEP */ + s32 csr13; /* Saved SIA Connectivity Reg. */ + s32 csr14; /* Saved SIA TX/RX Register */ + s32 csr15; /* Saved SIA General Register */ + int save_cnt; /* Flag if state already saved */ + struct sk_buff_head queue; /* Save the (re-ordered) skb's */ + } cache; + struct de4x5_srom srom; /* A copy of the SROM */ + int cfrv; /* Card CFRV copy */ + int rx_ovf; /* Check for 'RX overflow' tag */ + bool useSROM; /* For non-DEC card use SROM */ + bool useMII; /* Infoblock using the MII */ + int asBitValid; /* Autosense bits in GEP? */ + int asPolarity; /* 0 => asserted high */ + int asBit; /* Autosense bit number in GEP */ + int defMedium; /* SROM default medium */ + int tcount; /* Last infoblock number */ + int infoblock_init; /* Initialised this infoblock? */ + int infoleaf_offset; /* SROM infoleaf for controller */ + s32 infoblock_csr6; /* csr6 value in SROM infoblock */ + int infoblock_media; /* infoblock media */ + int (*infoleaf_fn)(struct net_device *); /* Pointer to infoleaf function */ + u_char *rst; /* Pointer to Type 5 reset info */ + u_char ibn; /* Infoblock number */ + struct parameters params; /* Command line/ #defined params */ + struct device *gendev; /* Generic device */ + dma_addr_t dma_rings; /* DMA handle for rings */ + int dma_size; /* Size of the DMA area */ + char *rx_bufs; /* rx bufs on alpha, sparc, ... */ +}; + +/* +** To get around certain poxy cards that don't provide an SROM +** for the second and more DECchip, I have to key off the first +** chip's address. I'll assume there's not a bad SROM iff: +** +** o the chipset is the same +** o the bus number is the same and > 0 +** o the sum of all the returned hw address bytes is 0 or 0x5fa +** +** Also have to save the irq for those cards whose hardware designers +** can't follow the PCI to PCI Bridge Architecture spec. +*/ +static struct { + int chipset; + int bus; + int irq; + u_char addr[ETH_ALEN]; +} last = {0,}; + +/* +** The transmit ring full condition is described by the tx_old and tx_new +** pointers by: +** tx_old = tx_new Empty ring +** tx_old = tx_new+1 Full ring +** tx_old+txRingSize = tx_new+1 Full ring (wrapped condition) +*/ +#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ + lp->tx_old+lp->txRingSize-lp->tx_new-1:\ + lp->tx_old -lp->tx_new-1) + +#define TX_PKT_PENDING (lp->tx_old != lp->tx_new) + +/* +** Public Functions +*/ +static int de4x5_open(struct net_device *dev); +static netdev_tx_t de4x5_queue_pkt(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t de4x5_interrupt(int irq, void *dev_id); +static int de4x5_close(struct net_device *dev); +static struct net_device_stats *de4x5_get_stats(struct net_device *dev); +static void de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len); +static void set_multicast_list(struct net_device *dev); +static int de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); + +/* +** Private functions +*/ +static int de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev); +static int de4x5_init(struct net_device *dev); +static int de4x5_sw_reset(struct net_device *dev); +static int de4x5_rx(struct net_device *dev); +static int de4x5_tx(struct net_device *dev); +static void de4x5_ast(struct net_device *dev); +static int de4x5_txur(struct net_device *dev); +static int de4x5_rx_ovfc(struct net_device *dev); + +static int autoconf_media(struct net_device *dev); +static void create_packet(struct net_device *dev, char *frame, int len); +static void load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb); +static int dc21040_autoconf(struct net_device *dev); +static int dc21041_autoconf(struct net_device *dev); +static int dc21140m_autoconf(struct net_device *dev); +static int dc2114x_autoconf(struct net_device *dev); +static int srom_autoconf(struct net_device *dev); +static int de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, int (*fn)(struct net_device *, int), int (*asfn)(struct net_device *)); +static int dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct net_device *, int)); +static int test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); +static int test_for_100Mb(struct net_device *dev, int msec); +static int wait_for_link(struct net_device *dev); +static int test_mii_reg(struct net_device *dev, int reg, int mask, bool pol, long msec); +static int is_spd_100(struct net_device *dev); +static int is_100_up(struct net_device *dev); +static int is_10_up(struct net_device *dev); +static int is_anc_capable(struct net_device *dev); +static int ping_media(struct net_device *dev, int msec); +static struct sk_buff *de4x5_alloc_rx_buff(struct net_device *dev, int index, int len); +static void de4x5_free_rx_buffs(struct net_device *dev); +static void de4x5_free_tx_buffs(struct net_device *dev); +static void de4x5_save_skbs(struct net_device *dev); +static void de4x5_rst_desc_ring(struct net_device *dev); +static void de4x5_cache_state(struct net_device *dev, int flag); +static void de4x5_put_cache(struct net_device *dev, struct sk_buff *skb); +static void de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb); +static struct sk_buff *de4x5_get_cache(struct net_device *dev); +static void de4x5_setup_intr(struct net_device *dev); +static void de4x5_init_connection(struct net_device *dev); +static int de4x5_reset_phy(struct net_device *dev); +static void reset_init_sia(struct net_device *dev, s32 sicr, s32 strr, s32 sigr); +static int test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec); +static int test_tp(struct net_device *dev, s32 msec); +static int EISA_signature(char *name, struct device *device); +static int PCI_signature(char *name, struct de4x5_private *lp); +static void DevicePresent(struct net_device *dev, u_long iobase); +static void enet_addr_rst(u_long aprom_addr); +static int de4x5_bad_srom(struct de4x5_private *lp); +static short srom_rd(u_long address, u_char offset); +static void srom_latch(u_int command, u_long address); +static void srom_command(u_int command, u_long address); +static void srom_address(u_int command, u_long address, u_char offset); +static short srom_data(u_int command, u_long address); +/*static void srom_busy(u_int command, u_long address);*/ +static void sendto_srom(u_int command, u_long addr); +static int getfrom_srom(u_long addr); +static int srom_map_media(struct net_device *dev); +static int srom_infoleaf_info(struct net_device *dev); +static void srom_init(struct net_device *dev); +static void srom_exec(struct net_device *dev, u_char *p); +static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr); +static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr); +static int mii_rdata(u_long ioaddr); +static void mii_wdata(int data, int len, u_long ioaddr); +static void mii_ta(u_long rw, u_long ioaddr); +static int mii_swap(int data, int len); +static void mii_address(u_char addr, u_long ioaddr); +static void sendto_mii(u32 command, int data, u_long ioaddr); +static int getfrom_mii(u32 command, u_long ioaddr); +static int mii_get_oui(u_char phyaddr, u_long ioaddr); +static int mii_get_phy(struct net_device *dev); +static void SetMulticastFilter(struct net_device *dev); +static int get_hw_addr(struct net_device *dev); +static void srom_repair(struct net_device *dev, int card); +static int test_bad_enet(struct net_device *dev, int status); +static int an_exception(struct de4x5_private *lp); +static char *build_setup_frame(struct net_device *dev, int mode); +static void disable_ast(struct net_device *dev); +static long de4x5_switch_mac_port(struct net_device *dev); +static int gep_rd(struct net_device *dev); +static void gep_wr(s32 data, struct net_device *dev); +static void yawn(struct net_device *dev, int state); +static void de4x5_parse_params(struct net_device *dev); +static void de4x5_dbg_open(struct net_device *dev); +static void de4x5_dbg_mii(struct net_device *dev, int k); +static void de4x5_dbg_media(struct net_device *dev); +static void de4x5_dbg_srom(struct de4x5_srom *p); +static void de4x5_dbg_rx(struct sk_buff *skb, int len); +static int de4x5_strncmp(char *a, char *b, int n); +static int dc21041_infoleaf(struct net_device *dev); +static int dc21140_infoleaf(struct net_device *dev); +static int dc21142_infoleaf(struct net_device *dev); +static int dc21143_infoleaf(struct net_device *dev); +static int type0_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type1_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type2_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type3_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type4_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type5_infoblock(struct net_device *dev, u_char count, u_char *p); +static int compact_infoblock(struct net_device *dev, u_char count, u_char *p); + +/* +** Note now that module autoprobing is allowed under EISA and PCI. The +** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes +** to "do the right thing". +*/ + +static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ + +module_param(io, int, 0); +module_param(de4x5_debug, int, 0); +module_param(dec_only, int, 0); +module_param(args, charp, 0); + +MODULE_PARM_DESC(io, "de4x5 I/O base address"); +MODULE_PARM_DESC(de4x5_debug, "de4x5 debug mask"); +MODULE_PARM_DESC(dec_only, "de4x5 probe only for Digital boards (0-1)"); +MODULE_PARM_DESC(args, "de4x5 full duplex and media type settings; see de4x5.c for details"); +MODULE_LICENSE("GPL"); + +/* +** List the SROM infoleaf functions and chipsets +*/ +struct InfoLeaf { + int chipset; + int (*fn)(struct net_device *); +}; +static struct InfoLeaf infoleaf_array[] = { + {DC21041, dc21041_infoleaf}, + {DC21140, dc21140_infoleaf}, + {DC21142, dc21142_infoleaf}, + {DC21143, dc21143_infoleaf} +}; +#define INFOLEAF_SIZE ARRAY_SIZE(infoleaf_array) + +/* +** List the SROM info block functions +*/ +static int (*dc_infoblock[])(struct net_device *dev, u_char, u_char *) = { + type0_infoblock, + type1_infoblock, + type2_infoblock, + type3_infoblock, + type4_infoblock, + type5_infoblock, + compact_infoblock +}; + +#define COMPACT (ARRAY_SIZE(dc_infoblock) - 1) + +/* +** Miscellaneous defines... +*/ +#define RESET_DE4X5 {\ + int i;\ + i=inl(DE4X5_BMR);\ + mdelay(1);\ + outl(i | BMR_SWR, DE4X5_BMR);\ + mdelay(1);\ + outl(i, DE4X5_BMR);\ + mdelay(1);\ + for (i=0;i<5;i++) {inl(DE4X5_BMR); mdelay(1);}\ + mdelay(1);\ +} + +#define PHY_HARD_RESET {\ + outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */\ + mdelay(1); /* Assert for 1ms */\ + outl(0x00, DE4X5_GEP);\ + mdelay(2); /* Wait for 2ms */\ +} + +static const struct net_device_ops de4x5_netdev_ops = { + .ndo_open = de4x5_open, + .ndo_stop = de4x5_close, + .ndo_start_xmit = de4x5_queue_pkt, + .ndo_get_stats = de4x5_get_stats, + .ndo_set_multicast_list = set_multicast_list, + .ndo_do_ioctl = de4x5_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address= eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + + +static int __devinit +de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev) +{ + char name[DE4X5_NAME_LENGTH + 1]; + struct de4x5_private *lp = netdev_priv(dev); + struct pci_dev *pdev = NULL; + int i, status=0; + + dev_set_drvdata(gendev, dev); + + /* Ensure we're not sleeping */ + if (lp->bus == EISA) { + outb(WAKEUP, PCI_CFPM); + } else { + pdev = to_pci_dev (gendev); + pci_write_config_byte(pdev, PCI_CFDA_PSM, WAKEUP); + } + mdelay(10); + + RESET_DE4X5; + + if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) { + return -ENXIO; /* Hardware could not reset */ + } + + /* + ** Now find out what kind of DC21040/DC21041/DC21140 board we have. + */ + lp->useSROM = false; + if (lp->bus == PCI) { + PCI_signature(name, lp); + } else { + EISA_signature(name, gendev); + } + + if (*name == '\0') { /* Not found a board signature */ + return -ENXIO; + } + + dev->base_addr = iobase; + printk ("%s: %s at 0x%04lx", dev_name(gendev), name, iobase); + + status = get_hw_addr(dev); + printk(", h/w address %pM\n", dev->dev_addr); + + if (status != 0) { + printk(" which has an Ethernet PROM CRC error.\n"); + return -ENXIO; + } else { + skb_queue_head_init(&lp->cache.queue); + lp->cache.gepc = GEP_INIT; + lp->asBit = GEP_SLNK; + lp->asPolarity = GEP_SLNK; + lp->asBitValid = ~0; + lp->timeout = -1; + lp->gendev = gendev; + spin_lock_init(&lp->lock); + init_timer(&lp->timer); + lp->timer.function = (void (*)(unsigned long))de4x5_ast; + lp->timer.data = (unsigned long)dev; + de4x5_parse_params(dev); + + /* + ** Choose correct autosensing in case someone messed up + */ + lp->autosense = lp->params.autosense; + if (lp->chipset != DC21140) { + if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { + lp->params.autosense = TP; + } + if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { + lp->params.autosense = BNC; + } + } + lp->fdx = lp->params.fdx; + sprintf(lp->adapter_name,"%s (%s)", name, dev_name(gendev)); + + lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc); +#if defined(__alpha__) || defined(__powerpc__) || defined(CONFIG_SPARC) || defined(DE4X5_DO_MEMCPY) + lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + DE4X5_ALIGN; +#endif + lp->rx_ring = dma_alloc_coherent(gendev, lp->dma_size, + &lp->dma_rings, GFP_ATOMIC); + if (lp->rx_ring == NULL) { + return -ENOMEM; + } + + lp->tx_ring = lp->rx_ring + NUM_RX_DESC; + + /* + ** Set up the RX descriptor ring (Intels) + ** Allocate contiguous receive buffers, long word aligned (Alphas) + */ +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY) + for (i=0; irx_ring[i].status = 0; + lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + lp->rx_ring[i].buf = 0; + lp->rx_ring[i].next = 0; + lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ + } + +#else + { + dma_addr_t dma_rx_bufs; + + dma_rx_bufs = lp->dma_rings + (NUM_RX_DESC + NUM_TX_DESC) + * sizeof(struct de4x5_desc); + dma_rx_bufs = (dma_rx_bufs + DE4X5_ALIGN) & ~DE4X5_ALIGN; + lp->rx_bufs = (char *)(((long)(lp->rx_ring + NUM_RX_DESC + + NUM_TX_DESC) + DE4X5_ALIGN) & ~DE4X5_ALIGN); + for (i=0; irx_ring[i].status = 0; + lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + lp->rx_ring[i].buf = + cpu_to_le32(dma_rx_bufs+i*RX_BUFF_SZ); + lp->rx_ring[i].next = 0; + lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ + } + + } +#endif + + barrier(); + + lp->rxRingSize = NUM_RX_DESC; + lp->txRingSize = NUM_TX_DESC; + + /* Write the end of list marker to the descriptor lists */ + lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); + lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER); + + /* Tell the adapter where the TX/RX rings are located. */ + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + /* Initialise the IRQ mask and Enable/Disable */ + lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM | IMR_UNM; + lp->irq_en = IMR_NIM | IMR_AIM; + + /* Create a loopback packet frame for later media probing */ + create_packet(dev, lp->frame, sizeof(lp->frame)); + + /* Check if the RX overflow bug needs testing for */ + i = lp->cfrv & 0x000000fe; + if ((lp->chipset == DC21140) && (i == 0x20)) { + lp->rx_ovf = 1; + } + + /* Initialise the SROM pointers if possible */ + if (lp->useSROM) { + lp->state = INITIALISED; + if (srom_infoleaf_info(dev)) { + dma_free_coherent (gendev, lp->dma_size, + lp->rx_ring, lp->dma_rings); + return -ENXIO; + } + srom_init(dev); + } + + lp->state = CLOSED; + + /* + ** Check for an MII interface + */ + if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) { + mii_get_phy(dev); + } + + printk(" and requires IRQ%d (provided by %s).\n", dev->irq, + ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); + } + + if (de4x5_debug & DEBUG_VERSION) { + printk(version); + } + + /* The DE4X5-specific entries in the device structure. */ + SET_NETDEV_DEV(dev, gendev); + dev->netdev_ops = &de4x5_netdev_ops; + dev->mem_start = 0; + + /* Fill in the generic fields of the device structure. */ + if ((status = register_netdev (dev))) { + dma_free_coherent (gendev, lp->dma_size, + lp->rx_ring, lp->dma_rings); + return status; + } + + /* Let the adapter sleep to save power */ + yawn(dev, SLEEP); + + return status; +} + + +static int +de4x5_open(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int i, status = 0; + s32 omr; + + /* Allocate the RX buffers */ + for (i=0; irxRingSize; i++) { + if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) { + de4x5_free_rx_buffs(dev); + return -EAGAIN; + } + } + + /* + ** Wake up the adapter + */ + yawn(dev, WAKEUP); + + /* + ** Re-initialize the DE4X5... + */ + status = de4x5_init(dev); + spin_lock_init(&lp->lock); + lp->state = OPEN; + de4x5_dbg_open(dev); + + if (request_irq(dev->irq, de4x5_interrupt, IRQF_SHARED, + lp->adapter_name, dev)) { + printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); + if (request_irq(dev->irq, de4x5_interrupt, IRQF_DISABLED | IRQF_SHARED, + lp->adapter_name, dev)) { + printk("\n Cannot get IRQ- reconfigure your hardware.\n"); + disable_ast(dev); + de4x5_free_rx_buffs(dev); + de4x5_free_tx_buffs(dev); + yawn(dev, SLEEP); + lp->state = CLOSED; + return -EAGAIN; + } else { + printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n"); + printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); + } + } + + lp->interrupt = UNMASK_INTERRUPTS; + dev->trans_start = jiffies; /* prevent tx timeout */ + + START_DE4X5; + + de4x5_setup_intr(dev); + + if (de4x5_debug & DEBUG_OPEN) { + printk("\tsts: 0x%08x\n", inl(DE4X5_STS)); + printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR)); + printk("\timr: 0x%08x\n", inl(DE4X5_IMR)); + printk("\tomr: 0x%08x\n", inl(DE4X5_OMR)); + printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR)); + printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR)); + printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR)); + printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR)); + } + + return status; +} + +/* +** Initialize the DE4X5 operating conditions. NB: a chip problem with the +** DC21140 requires using perfect filtering mode for that chip. Since I can't +** see why I'd want > 14 multicast addresses, I have changed all chips to use +** the perfect filtering mode. Keep the DMA burst length at 8: there seems +** to be data corruption problems if it is larger (UDP errors seen from a +** ttcp source). +*/ +static int +de4x5_init(struct net_device *dev) +{ + /* Lock out other processes whilst setting up the hardware */ + netif_stop_queue(dev); + + de4x5_sw_reset(dev); + + /* Autoconfigure the connected port */ + autoconf_media(dev); + + return 0; +} + +static int +de4x5_sw_reset(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int i, j, status = 0; + s32 bmr, omr; + + /* Select the MII or SRL port now and RESET the MAC */ + if (!lp->useSROM) { + if (lp->phy[lp->active].id != 0) { + lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD; + } else { + lp->infoblock_csr6 = OMR_SDP | OMR_TTM; + } + de4x5_switch_mac_port(dev); + } + + /* + ** Set the programmable burst length to 8 longwords for all the DC21140 + ** Fasternet chips and 4 longwords for all others: DMA errors result + ** without these values. Cache align 16 long. + */ + bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | DE4X5_CACHE_ALIGN; + bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0); + outl(bmr, DE4X5_BMR); + + omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */ + if (lp->chipset == DC21140) { + omr |= (OMR_SDP | OMR_SB); + } + lp->setup_f = PERFECT; + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + lp->rx_new = lp->rx_old = 0; + lp->tx_new = lp->tx_old = 0; + + for (i = 0; i < lp->rxRingSize; i++) { + lp->rx_ring[i].status = cpu_to_le32(R_OWN); + } + + for (i = 0; i < lp->txRingSize; i++) { + lp->tx_ring[i].status = cpu_to_le32(0); + } + + barrier(); + + /* Build the setup frame depending on filtering mode */ + SetMulticastFilter(dev); + + load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, (struct sk_buff *)1); + outl(omr|OMR_ST, DE4X5_OMR); + + /* Poll for setup frame completion (adapter interrupts are disabled now) */ + + for (j=0, i=0;(i<500) && (j==0);i++) { /* Up to 500ms delay */ + mdelay(1); + if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; + } + outl(omr, DE4X5_OMR); /* Stop everything! */ + + if (j == 0) { + printk("%s: Setup frame timed out, status %08x\n", dev->name, + inl(DE4X5_STS)); + status = -EIO; + } + + lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; + lp->tx_old = lp->tx_new; + + return status; +} + +/* +** Writes a socket buffer address to the next available transmit descriptor. +*/ +static netdev_tx_t +de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + u_long flags = 0; + + netif_stop_queue(dev); + if (!lp->tx_enable) /* Cannot send for now */ + return NETDEV_TX_LOCKED; + + /* + ** Clean out the TX ring asynchronously to interrupts - sometimes the + ** interrupts are lost by delayed descriptor status updates relative to + ** the irq assertion, especially with a busy PCI bus. + */ + spin_lock_irqsave(&lp->lock, flags); + de4x5_tx(dev); + spin_unlock_irqrestore(&lp->lock, flags); + + /* Test if cache is already locked - requeue skb if so */ + if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) + return NETDEV_TX_LOCKED; + + /* Transmit descriptor ring full or stale skb */ + if (netif_queue_stopped(dev) || (u_long) lp->tx_skb[lp->tx_new] > 1) { + if (lp->interrupt) { + de4x5_putb_cache(dev, skb); /* Requeue the buffer */ + } else { + de4x5_put_cache(dev, skb); + } + if (de4x5_debug & DEBUG_TX) { + printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%d\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), netif_queue_stopped(dev), inl(DE4X5_IMR), inl(DE4X5_OMR), ((u_long) lp->tx_skb[lp->tx_new] > 1) ? "YES" : "NO"); + } + } else if (skb->len > 0) { + /* If we already have stuff queued locally, use that first */ + if (!skb_queue_empty(&lp->cache.queue) && !lp->interrupt) { + de4x5_put_cache(dev, skb); + skb = de4x5_get_cache(dev); + } + + while (skb && !netif_queue_stopped(dev) && + (u_long) lp->tx_skb[lp->tx_new] <= 1) { + spin_lock_irqsave(&lp->lock, flags); + netif_stop_queue(dev); + load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); + lp->stats.tx_bytes += skb->len; + outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ + + lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; + + if (TX_BUFFS_AVAIL) { + netif_start_queue(dev); /* Another pkt may be queued */ + } + skb = de4x5_get_cache(dev); + spin_unlock_irqrestore(&lp->lock, flags); + } + if (skb) de4x5_putb_cache(dev, skb); + } + + lp->cache.lock = 0; + + return NETDEV_TX_OK; +} + +/* +** The DE4X5 interrupt handler. +** +** I/O Read/Writes through intermediate PCI bridges are never 'posted', +** so that the asserted interrupt always has some real data to work with - +** if these I/O accesses are ever changed to memory accesses, ensure the +** STS write is read immediately to complete the transaction if the adapter +** is not on bus 0. Lost interrupts can still occur when the PCI bus load +** is high and descriptor status bits cannot be set before the associated +** interrupt is asserted and this routine entered. +*/ +static irqreturn_t +de4x5_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct de4x5_private *lp; + s32 imr, omr, sts, limit; + u_long iobase; + unsigned int handled = 0; + + lp = netdev_priv(dev); + spin_lock(&lp->lock); + iobase = dev->base_addr; + + DISABLE_IRQs; /* Ensure non re-entrancy */ + + if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt)) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + synchronize_irq(dev->irq); + + for (limit=0; limit<8; limit++) { + sts = inl(DE4X5_STS); /* Read IRQ status */ + outl(sts, DE4X5_STS); /* Reset the board interrupts */ + + if (!(sts & lp->irq_mask)) break;/* All done */ + handled = 1; + + if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */ + de4x5_rx(dev); + + if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */ + de4x5_tx(dev); + + if (sts & STS_LNF) { /* TP Link has failed */ + lp->irq_mask &= ~IMR_LFM; + } + + if (sts & STS_UNF) { /* Transmit underrun */ + de4x5_txur(dev); + } + + if (sts & STS_SE) { /* Bus Error */ + STOP_DE4X5; + printk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n", + dev->name, sts); + spin_unlock(&lp->lock); + return IRQ_HANDLED; + } + } + + /* Load the TX ring with any locally stored packets */ + if (!test_and_set_bit(0, (void *)&lp->cache.lock)) { + while (!skb_queue_empty(&lp->cache.queue) && !netif_queue_stopped(dev) && lp->tx_enable) { + de4x5_queue_pkt(de4x5_get_cache(dev), dev); + } + lp->cache.lock = 0; + } + + lp->interrupt = UNMASK_INTERRUPTS; + ENABLE_IRQs; + spin_unlock(&lp->lock); + + return IRQ_RETVAL(handled); +} + +static int +de4x5_rx(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int entry; + s32 status; + + for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0; + entry=lp->rx_new) { + status = (s32)le32_to_cpu(lp->rx_ring[entry].status); + + if (lp->rx_ovf) { + if (inl(DE4X5_MFC) & MFC_FOCM) { + de4x5_rx_ovfc(dev); + break; + } + } + + if (status & RD_FS) { /* Remember the start of frame */ + lp->rx_old = entry; + } + + if (status & RD_LS) { /* Valid frame status */ + if (lp->tx_enable) lp->linkOK++; + if (status & RD_ES) { /* There was an error. */ + lp->stats.rx_errors++; /* Update the error stats. */ + if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++; + if (status & RD_CE) lp->stats.rx_crc_errors++; + if (status & RD_OF) lp->stats.rx_fifo_errors++; + if (status & RD_TL) lp->stats.rx_length_errors++; + if (status & RD_RF) lp->pktStats.rx_runt_frames++; + if (status & RD_CS) lp->pktStats.rx_collision++; + if (status & RD_DB) lp->pktStats.rx_dribble++; + if (status & RD_OF) lp->pktStats.rx_overflow++; + } else { /* A valid frame received */ + struct sk_buff *skb; + short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status) + >> 16) - 4; + + if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) { + printk("%s: Insufficient memory; nuking packet.\n", + dev->name); + lp->stats.rx_dropped++; + } else { + de4x5_dbg_rx(skb, pkt_len); + + /* Push up the protocol stack */ + skb->protocol=eth_type_trans(skb,dev); + de4x5_local_stats(dev, skb->data, pkt_len); + netif_rx(skb); + + /* Update stats */ + lp->stats.rx_packets++; + lp->stats.rx_bytes += pkt_len; + } + } + + /* Change buffer ownership for this frame, back to the adapter */ + for (;lp->rx_old!=entry;lp->rx_old=(lp->rx_old + 1)%lp->rxRingSize) { + lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN); + barrier(); + } + lp->rx_ring[entry].status = cpu_to_le32(R_OWN); + barrier(); + } + + /* + ** Update entry information + */ + lp->rx_new = (lp->rx_new + 1) % lp->rxRingSize; + } + + return 0; +} + +static inline void +de4x5_free_tx_buff(struct de4x5_private *lp, int entry) +{ + dma_unmap_single(lp->gendev, le32_to_cpu(lp->tx_ring[entry].buf), + le32_to_cpu(lp->tx_ring[entry].des1) & TD_TBS1, + DMA_TO_DEVICE); + if ((u_long) lp->tx_skb[entry] > 1) + dev_kfree_skb_irq(lp->tx_skb[entry]); + lp->tx_skb[entry] = NULL; +} + +/* +** Buffer sent - check for TX buffer errors. +*/ +static int +de4x5_tx(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int entry; + s32 status; + + for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { + status = (s32)le32_to_cpu(lp->tx_ring[entry].status); + if (status < 0) { /* Buffer not sent yet */ + break; + } else if (status != 0x7fffffff) { /* Not setup frame */ + if (status & TD_ES) { /* An error happened */ + lp->stats.tx_errors++; + if (status & TD_NC) lp->stats.tx_carrier_errors++; + if (status & TD_LC) lp->stats.tx_window_errors++; + if (status & TD_UF) lp->stats.tx_fifo_errors++; + if (status & TD_EC) lp->pktStats.excessive_collisions++; + if (status & TD_DE) lp->stats.tx_aborted_errors++; + + if (TX_PKT_PENDING) { + outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */ + } + } else { /* Packet sent */ + lp->stats.tx_packets++; + if (lp->tx_enable) lp->linkOK++; + } + /* Update the collision counter */ + lp->stats.collisions += ((status & TD_EC) ? 16 : + ((status & TD_CC) >> 3)); + + /* Free the buffer. */ + if (lp->tx_skb[entry] != NULL) + de4x5_free_tx_buff(lp, entry); + } + + /* Update all the pointers */ + lp->tx_old = (lp->tx_old + 1) % lp->txRingSize; + } + + /* Any resources available? */ + if (TX_BUFFS_AVAIL && netif_queue_stopped(dev)) { + if (lp->interrupt) + netif_wake_queue(dev); + else + netif_start_queue(dev); + } + + return 0; +} + +static void +de4x5_ast(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + int next_tick = DE4X5_AUTOSENSE_MS; + int dt; + + if (lp->useSROM) + next_tick = srom_autoconf(dev); + else if (lp->chipset == DC21140) + next_tick = dc21140m_autoconf(dev); + else if (lp->chipset == DC21041) + next_tick = dc21041_autoconf(dev); + else if (lp->chipset == DC21040) + next_tick = dc21040_autoconf(dev); + lp->linkOK = 0; + + dt = (next_tick * HZ) / 1000; + + if (!dt) + dt = 1; + + mod_timer(&lp->timer, jiffies + dt); +} + +static int +de4x5_txur(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int omr; + + omr = inl(DE4X5_OMR); + if (!(omr & OMR_SF) || (lp->chipset==DC21041) || (lp->chipset==DC21040)) { + omr &= ~(OMR_ST|OMR_SR); + outl(omr, DE4X5_OMR); + while (inl(DE4X5_STS) & STS_TS); + if ((omr & OMR_TR) < OMR_TR) { + omr += 0x4000; + } else { + omr |= OMR_SF; + } + outl(omr | OMR_ST | OMR_SR, DE4X5_OMR); + } + + return 0; +} + +static int +de4x5_rx_ovfc(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int omr; + + omr = inl(DE4X5_OMR); + outl(omr & ~OMR_SR, DE4X5_OMR); + while (inl(DE4X5_STS) & STS_RS); + + for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) { + lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN); + lp->rx_new = (lp->rx_new + 1) % lp->rxRingSize; + } + + outl(omr, DE4X5_OMR); + + return 0; +} + +static int +de4x5_close(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + s32 imr, omr; + + disable_ast(dev); + + netif_stop_queue(dev); + + if (de4x5_debug & DEBUG_CLOSE) { + printk("%s: Shutting down ethercard, status was %8.8x.\n", + dev->name, inl(DE4X5_STS)); + } + + /* + ** We stop the DE4X5 here... mask interrupts and stop TX & RX + */ + DISABLE_IRQs; + STOP_DE4X5; + + /* Free the associated irq */ + free_irq(dev->irq, dev); + lp->state = CLOSED; + + /* Free any socket buffers */ + de4x5_free_rx_buffs(dev); + de4x5_free_tx_buffs(dev); + + /* Put the adapter to sleep to save power */ + yawn(dev, SLEEP); + + return 0; +} + +static struct net_device_stats * +de4x5_get_stats(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + lp->stats.rx_missed_errors = (int)(inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR)); + + return &lp->stats; +} + +static void +de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len) +{ + struct de4x5_private *lp = netdev_priv(dev); + int i; + + for (i=1; ipktStats.bins[i]++; + i = DE4X5_PKT_STAT_SZ; + } + } + if (is_multicast_ether_addr(buf)) { + if (is_broadcast_ether_addr(buf)) { + lp->pktStats.broadcast++; + } else { + lp->pktStats.multicast++; + } + } else if (compare_ether_addr(buf, dev->dev_addr) == 0) { + lp->pktStats.unicast++; + } + + lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ + if (lp->pktStats.bins[0] == 0) { /* Reset counters */ + memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats)); + } +} + +/* +** Removes the TD_IC flag from previous descriptor to improve TX performance. +** If the flag is changed on a descriptor that is being read by the hardware, +** I assume PCI transaction ordering will mean you are either successful or +** just miss asserting the change to the hardware. Anyway you're messing with +** a descriptor you don't own, but this shouldn't kill the chip provided +** the descriptor register is read only to the hardware. +*/ +static void +load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb) +{ + struct de4x5_private *lp = netdev_priv(dev); + int entry = (lp->tx_new ? lp->tx_new-1 : lp->txRingSize-1); + dma_addr_t buf_dma = dma_map_single(lp->gendev, buf, flags & TD_TBS1, DMA_TO_DEVICE); + + lp->tx_ring[lp->tx_new].buf = cpu_to_le32(buf_dma); + lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER); + lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags); + lp->tx_skb[lp->tx_new] = skb; + lp->tx_ring[entry].des1 &= cpu_to_le32(~TD_IC); + barrier(); + + lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN); + barrier(); +} + +/* +** Set or clear the multicast filter for this adaptor. +*/ +static void +set_multicast_list(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + /* First, double check that the adapter is open */ + if (lp->state == OPEN) { + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + u32 omr; + omr = inl(DE4X5_OMR); + omr |= OMR_PR; + outl(omr, DE4X5_OMR); + } else { + SetMulticastFilter(dev); + load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | + SETUP_FRAME_LEN, (struct sk_buff *)1); + + lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ + dev->trans_start = jiffies; /* prevent tx timeout */ + } + } +} + +/* +** Calculate the hash code and update the logical address filter +** from a list of ethernet multicast addresses. +** Little endian crc one liner from Matt Thomas, DEC. +*/ +static void +SetMulticastFilter(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + struct netdev_hw_addr *ha; + u_long iobase = dev->base_addr; + int i, bit, byte; + u16 hashcode; + u32 omr, crc; + char *pa; + unsigned char *addrs; + + omr = inl(DE4X5_OMR); + omr &= ~(OMR_PR | OMR_PM); + pa = build_setup_frame(dev, ALL); /* Build the basic frame */ + + if ((dev->flags & IFF_ALLMULTI) || (netdev_mc_count(dev) > 14)) { + omr |= OMR_PM; /* Pass all multicasts */ + } else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */ + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(ETH_ALEN, ha->addr); + hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */ + + byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ + bit = 1 << (hashcode & 0x07);/* bit[0-2] -> bit in byte */ + + byte <<= 1; /* calc offset into setup frame */ + if (byte & 0x02) { + byte -= 1; + } + lp->setup_frame[byte] |= bit; + } + } else { /* Perfect filtering */ + netdev_for_each_mc_addr(ha, dev) { + addrs = ha->addr; + for (i=0; ibase_addr; + + if (!request_region (iobase, DE4X5_EISA_TOTAL_SIZE, "de4x5")) + return -EBUSY; + + if (!request_region (iobase + DE4X5_EISA_IO_PORTS, + DE4X5_EISA_TOTAL_SIZE, "de4x5")) { + status = -EBUSY; + goto release_reg_1; + } + + if (!(dev = alloc_etherdev (sizeof (struct de4x5_private)))) { + status = -ENOMEM; + goto release_reg_2; + } + lp = netdev_priv(dev); + + cfid = (u32) inl(PCI_CFID); + lp->cfrv = (u_short) inl(PCI_CFRV); + device = (cfid >> 8) & 0x00ffff00; + vendor = (u_short) cfid; + + /* Read the EISA Configuration Registers */ + regval = inb(EISA_REG0) & (ER0_INTL | ER0_INTT); +#ifdef CONFIG_ALPHA + /* Looks like the Jensen firmware (rev 2.2) doesn't really + * care about the EISA configuration, and thus doesn't + * configure the PLX bridge properly. Oh well... Simply mimic + * the EISA config file to sort it out. */ + + /* EISA REG1: Assert DecChip 21040 HW Reset */ + outb (ER1_IAM | 1, EISA_REG1); + mdelay (1); + + /* EISA REG1: Deassert DecChip 21040 HW Reset */ + outb (ER1_IAM, EISA_REG1); + mdelay (1); + + /* EISA REG3: R/W Burst Transfer Enable */ + outb (ER3_BWE | ER3_BRE, EISA_REG3); + + /* 32_bit slave/master, Preempt Time=23 bclks, Unlatched Interrupt */ + outb (ER0_BSW | ER0_BMW | ER0_EPT | regval, EISA_REG0); +#endif + irq = de4x5_irq[(regval >> 1) & 0x03]; + + if (is_DC2114x) { + device = ((lp->cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); + } + lp->chipset = device; + lp->bus = EISA; + + /* Write the PCI Configuration Registers */ + outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS); + outl(0x00006000, PCI_CFLT); + outl(iobase, PCI_CBIO); + + DevicePresent(dev, EISA_APROM); + + dev->irq = irq; + + if (!(status = de4x5_hw_init (dev, iobase, gendev))) { + return 0; + } + + free_netdev (dev); + release_reg_2: + release_region (iobase + DE4X5_EISA_IO_PORTS, DE4X5_EISA_TOTAL_SIZE); + release_reg_1: + release_region (iobase, DE4X5_EISA_TOTAL_SIZE); + + return status; +} + +static int __devexit de4x5_eisa_remove (struct device *device) +{ + struct net_device *dev; + u_long iobase; + + dev = dev_get_drvdata(device); + iobase = dev->base_addr; + + unregister_netdev (dev); + free_netdev (dev); + release_region (iobase + DE4X5_EISA_IO_PORTS, DE4X5_EISA_TOTAL_SIZE); + release_region (iobase, DE4X5_EISA_TOTAL_SIZE); + + return 0; +} + +static struct eisa_device_id de4x5_eisa_ids[] = { + { "DEC4250", 0 }, /* 0 is the board name index... */ + { "" } +}; +MODULE_DEVICE_TABLE(eisa, de4x5_eisa_ids); + +static struct eisa_driver de4x5_eisa_driver = { + .id_table = de4x5_eisa_ids, + .driver = { + .name = "de4x5", + .probe = de4x5_eisa_probe, + .remove = __devexit_p (de4x5_eisa_remove), + } +}; +MODULE_DEVICE_TABLE(eisa, de4x5_eisa_ids); +#endif + +#ifdef CONFIG_PCI + +/* +** This function searches the current bus (which is >0) for a DECchip with an +** SROM, so that in multiport cards that have one SROM shared between multiple +** DECchips, we can find the base SROM irrespective of the BIOS scan direction. +** For single port cards this is a time waster... +*/ +static void __devinit +srom_search(struct net_device *dev, struct pci_dev *pdev) +{ + u_char pb; + u_short vendor, status; + u_int irq = 0, device; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ + int i, j; + struct de4x5_private *lp = netdev_priv(dev); + struct list_head *walk; + + list_for_each(walk, &pdev->bus_list) { + struct pci_dev *this_dev = pci_dev_b(walk); + + /* Skip the pci_bus list entry */ + if (list_entry(walk, struct pci_bus, devices) == pdev->bus) continue; + + vendor = this_dev->vendor; + device = this_dev->device << 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; + + /* Get the chip configuration revision register */ + pb = this_dev->bus->number; + + /* Set the device number information */ + lp->device = PCI_SLOT(this_dev->devfn); + lp->bus_num = pb; + + /* Set the chipset information */ + if (is_DC2114x) { + device = ((this_dev->revision & CFRV_RN) < DC2114x_BRK + ? DC21142 : DC21143); + } + lp->chipset = device; + + /* Get the board I/O address (64 bits on sparc64) */ + iobase = pci_resource_start(this_dev, 0); + + /* Fetch the IRQ to be used */ + irq = this_dev->irq; + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; + + /* Check if I/O accesses are enabled */ + pci_read_config_word(this_dev, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_IO)) continue; + + /* Search for a valid SROM attached to this DECchip */ + DevicePresent(dev, DE4X5_APROM); + for (j=0, i=0; isrom + SROM_HWADD + i); + } + if (j != 0 && j != 6 * 0xff) { + last.chipset = device; + last.bus = pb; + last.irq = irq; + for (i=0; isrom + SROM_HWADD + i); + } + return; + } + } +} + +/* +** PCI bus I/O device probe +** NB: PCI I/O accesses and Bus Mastering are enabled by the PCI BIOS, not +** the driver. Some PCI BIOS's, pre V2.1, need the slot + features to be +** enabled by the user first in the set up utility. Hence we just check for +** enabled features and silently ignore the card if they're not. +** +** STOP PRESS: Some BIOS's __require__ the driver to enable the bus mastering +** bit. Here, check for I/O accesses and then set BM. If you put the card in +** a non BM slot, you're on your own (and complain to the PC vendor that your +** PC doesn't conform to the PCI standard)! +** +** This function is only compatible with the *latest* 2.1.x kernels. For 2.0.x +** kernels use the V0.535[n] drivers. +*/ + +static int __devinit de4x5_pci_probe (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + u_char pb, pbus = 0, dev_num, dnum = 0, timer; + u_short vendor, status; + u_int irq = 0, device; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ + int error; + struct net_device *dev; + struct de4x5_private *lp; + + dev_num = PCI_SLOT(pdev->devfn); + pb = pdev->bus->number; + + if (io) { /* probe a single PCI device */ + pbus = (u_short)(io >> 8); + dnum = (u_short)(io & 0xff); + if ((pbus != pb) || (dnum != dev_num)) + return -ENODEV; + } + + vendor = pdev->vendor; + device = pdev->device << 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) + return -ENODEV; + + /* Ok, the device seems to be for us. */ + if ((error = pci_enable_device (pdev))) + return error; + + if (!(dev = alloc_etherdev (sizeof (struct de4x5_private)))) { + error = -ENOMEM; + goto disable_dev; + } + + lp = netdev_priv(dev); + lp->bus = PCI; + lp->bus_num = 0; + + /* Search for an SROM on this bus */ + if (lp->bus_num != pb) { + lp->bus_num = pb; + srom_search(dev, pdev); + } + + /* Get the chip configuration revision register */ + lp->cfrv = pdev->revision; + + /* Set the device number information */ + lp->device = dev_num; + lp->bus_num = pb; + + /* Set the chipset information */ + if (is_DC2114x) { + device = ((lp->cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); + } + lp->chipset = device; + + /* Get the board I/O address (64 bits on sparc64) */ + iobase = pci_resource_start(pdev, 0); + + /* Fetch the IRQ to be used */ + irq = pdev->irq; + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) { + error = -ENODEV; + goto free_dev; + } + + /* Check if I/O accesses and Bus Mastering are enabled */ + pci_read_config_word(pdev, PCI_COMMAND, &status); +#ifdef __powerpc__ + if (!(status & PCI_COMMAND_IO)) { + status |= PCI_COMMAND_IO; + pci_write_config_word(pdev, PCI_COMMAND, status); + pci_read_config_word(pdev, PCI_COMMAND, &status); + } +#endif /* __powerpc__ */ + if (!(status & PCI_COMMAND_IO)) { + error = -ENODEV; + goto free_dev; + } + + if (!(status & PCI_COMMAND_MASTER)) { + status |= PCI_COMMAND_MASTER; + pci_write_config_word(pdev, PCI_COMMAND, status); + pci_read_config_word(pdev, PCI_COMMAND, &status); + } + if (!(status & PCI_COMMAND_MASTER)) { + error = -ENODEV; + goto free_dev; + } + + /* Check the latency timer for values >= 0x60 */ + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &timer); + if (timer < 0x60) { + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x60); + } + + DevicePresent(dev, DE4X5_APROM); + + if (!request_region (iobase, DE4X5_PCI_TOTAL_SIZE, "de4x5")) { + error = -EBUSY; + goto free_dev; + } + + dev->irq = irq; + + if ((error = de4x5_hw_init(dev, iobase, &pdev->dev))) { + goto release; + } + + return 0; + + release: + release_region (iobase, DE4X5_PCI_TOTAL_SIZE); + free_dev: + free_netdev (dev); + disable_dev: + pci_disable_device (pdev); + return error; +} + +static void __devexit de4x5_pci_remove (struct pci_dev *pdev) +{ + struct net_device *dev; + u_long iobase; + + dev = dev_get_drvdata(&pdev->dev); + iobase = dev->base_addr; + + unregister_netdev (dev); + free_netdev (dev); + release_region (iobase, DE4X5_PCI_TOTAL_SIZE); + pci_disable_device (pdev); +} + +static struct pci_device_id de4x5_pci_tbl[] = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, + { }, +}; + +static struct pci_driver de4x5_pci_driver = { + .name = "de4x5", + .id_table = de4x5_pci_tbl, + .probe = de4x5_pci_probe, + .remove = __devexit_p (de4x5_pci_remove), +}; + +#endif + +/* +** Auto configure the media here rather than setting the port at compile +** time. This routine is called by de4x5_init() and when a loss of media is +** detected (excessive collisions, loss of carrier, no carrier or link fail +** [TP] or no recent receive activity) to check whether the user has been +** sneaky and changed the port on us. +*/ +static int +autoconf_media(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + disable_ast(dev); + + lp->c_media = AUTO; /* Bogus last media */ + inl(DE4X5_MFC); /* Zero the lost frames counter */ + lp->media = INIT; + lp->tcount = 0; + + de4x5_ast(dev); + + return lp->media; +} + +/* +** Autoconfigure the media when using the DC21040. AUI cannot be distinguished +** from BNC as the port has a jumper to set thick or thin wire. When set for +** BNC, the BNC port will indicate activity if it's not terminated correctly. +** The only way to test for that is to place a loopback packet onto the +** network and watch for errors. Since we're messing with the interrupt mask +** register, disable the board interrupts and do not allow any more packets to +** be queued to the hardware. Re-enable everything only when the media is +** found. +** I may have to "age out" locally queued packets so that the higher layer +** timeouts don't effectively duplicate packets on the network. +*/ +static int +dc21040_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int next_tick = DE4X5_AUTOSENSE_MS; + s32 imr; + + switch (lp->media) { + case INIT: + DISABLE_IRQs; + lp->tx_enable = false; + lp->timeout = -1; + de4x5_save_skbs(dev); + if ((lp->autosense == AUTO) || (lp->autosense == TP)) { + lp->media = TP; + } else if ((lp->autosense == BNC) || (lp->autosense == AUI) || (lp->autosense == BNC_AUI)) { + lp->media = BNC_AUI; + } else if (lp->autosense == EXT_SIA) { + lp->media = EXT_SIA; + } else { + lp->media = NC; + } + lp->local_state = 0; + next_tick = dc21040_autoconf(dev); + break; + + case TP: + next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, + TP_SUSPECT, test_tp); + break; + + case TP_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf); + break; + + case BNC: + case AUI: + case BNC_AUI: + next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, + BNC_AUI_SUSPECT, ping_media); + break; + + case BNC_AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf); + break; + + case EXT_SIA: + next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, + NC, EXT_SIA_SUSPECT, ping_media); + break; + + case EXT_SIA_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf); + break; + + case NC: + /* default to TP for all */ + reset_init_sia(dev, 0x8f01, 0xffff, 0x0000); + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = false; + break; + } + + return next_tick; +} + +static int +dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, + int next_state, int suspect_state, + int (*fn)(struct net_device *, int)) +{ + struct de4x5_private *lp = netdev_priv(dev); + int next_tick = DE4X5_AUTOSENSE_MS; + int linkBad; + + switch (lp->local_state) { + case 0: + reset_init_sia(dev, csr13, csr14, csr15); + lp->local_state++; + next_tick = 500; + break; + + case 1: + if (!lp->tx_enable) { + linkBad = fn(dev, timeout); + if (linkBad < 0) { + next_tick = linkBad & ~TIMER_CB; + } else { + if (linkBad && (lp->autosense == AUTO)) { + lp->local_state = 0; + lp->media = next_state; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = suspect_state; + next_tick = 3000; + } + break; + } + + return next_tick; +} + +static int +de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, + int (*fn)(struct net_device *, int), + int (*asfn)(struct net_device *)) +{ + struct de4x5_private *lp = netdev_priv(dev); + int next_tick = DE4X5_AUTOSENSE_MS; + int linkBad; + + switch (lp->local_state) { + case 1: + if (lp->linkOK) { + lp->media = prev_state; + } else { + lp->local_state++; + next_tick = asfn(dev); + } + break; + + case 2: + linkBad = fn(dev, timeout); + if (linkBad < 0) { + next_tick = linkBad & ~TIMER_CB; + } else if (!linkBad) { + lp->local_state--; + lp->media = prev_state; + } else { + lp->media = INIT; + lp->tcount++; + } + } + + return next_tick; +} + +/* +** Autoconfigure the media when using the DC21041. AUI needs to be tested +** before BNC, because the BNC port will indicate activity if it's not +** terminated correctly. The only way to test for that is to place a loopback +** packet onto the network and watch for errors. Since we're messing with +** the interrupt mask register, disable the board interrupts and do not allow +** any more packets to be queued to the hardware. Re-enable everything only +** when the media is found. +*/ +static int +dc21041_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + s32 sts, irqs, irq_mask, imr, omr; + int next_tick = DE4X5_AUTOSENSE_MS; + + switch (lp->media) { + case INIT: + DISABLE_IRQs; + lp->tx_enable = false; + lp->timeout = -1; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + if ((lp->autosense == AUTO) || (lp->autosense == TP_NW)) { + lp->media = TP; /* On chip auto negotiation is broken */ + } else if (lp->autosense == TP) { + lp->media = TP; + } else if (lp->autosense == BNC) { + lp->media = BNC; + } else if (lp->autosense == AUI) { + lp->media = AUI; + } else { + lp->media = NC; + } + lp->local_state = 0; + next_tick = dc21041_autoconf(dev); + break; + + case TP_NW: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ + outl(omr | OMR_FDX, DE4X5_OMR); + } + irqs = STS_LNF | STS_LNP; + irq_mask = IMR_LFM | IMR_LPM; + sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts & STS_LNP) { + lp->media = ANS; + } else { + lp->media = AUI; + } + next_tick = dc21041_autoconf(dev); + } + break; + + case ANS: + if (!lp->tx_enable) { + irqs = STS_LNP; + irq_mask = IMR_LPM; + sts = test_ans(dev, irqs, irq_mask, 3000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { + lp->media = TP; + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = ANS_SUSPECT; + next_tick = 3000; + } + break; + + case ANS_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf); + break; + + case TP: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = STS_LNF | STS_LNP; + irq_mask = IMR_LFM | IMR_LPM; + sts = test_media(dev,irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { + if (inl(DE4X5_SISR) & SISR_NRA) { + lp->media = AUI; /* Non selected port activity */ + } else { + lp->media = BNC; + } + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = TP_SUSPECT; + next_tick = 3000; + } + break; + + case TP_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf); + break; + + case AUI: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x000e, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { + lp->media = BNC; + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = AUI_SUSPECT; + next_tick = 3000; + } + break; + + case AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf); + break; + + case BNC: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x0006, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + lp->local_state++; /* Ensure media connected */ + next_tick = dc21041_autoconf(dev); + } + break; + + case 1: + if (!lp->tx_enable) { + if ((sts = ping_media(dev, 3000)) < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts) { + lp->local_state = 0; + lp->media = NC; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = BNC_SUSPECT; + next_tick = 3000; + } + break; + } + break; + + case BNC_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf); + break; + + case NC: + omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ + outl(omr | OMR_FDX, DE4X5_OMR); + reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = false; + break; + } + + return next_tick; +} + +/* +** Some autonegotiation chips are broken in that they do not return the +** acknowledge bit (anlpa & MII_ANLPA_ACK) in the link partner advertisement +** register, except at the first power up negotiation. +*/ +static int +dc21140m_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + int ana, anlpa, cap, cr, slnk, sr; + int next_tick = DE4X5_AUTOSENSE_MS; + u_long imr, omr, iobase = dev->base_addr; + + switch(lp->media) { + case INIT: + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = false; + lp->linkOK = 0; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + } + if ((next_tick = de4x5_reset_phy(dev)) < 0) { + next_tick &= ~TIMER_CB; + } else { + if (lp->useSROM) { + if (srom_map_media(dev) < 0) { + lp->tcount++; + return next_tick; + } + srom_exec(dev, lp->phy[lp->active].gep); + if (lp->infoblock_media == ANS) { + ana = lp->phy[lp->active].ana | MII_ANA_CSMA; + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + } + } else { + lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ + SET_10Mb; + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if ((lp->autosense == AUTO) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } else if (lp->autosense == AUTO) { + lp->media = SPD_DET; + } else if (is_spd_100(dev) && is_100_up(dev)) { + lp->media = _100Mb; + } else { + lp->media = NC; + } + } + lp->local_state = 0; + next_tick = dc21140m_autoconf(dev); + } + break; + + case ANS: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, false, 500); + if (cr < 0) { + next_tick = cr & ~TIMER_CB; + } else { + if (cr) { + lp->local_state = 0; + lp->media = SPD_DET; + } else { + lp->local_state++; + } + next_tick = dc21140m_autoconf(dev); + } + break; + + case 1: + if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, true, 2000)) < 0) { + next_tick = sr & ~TIMER_CB; + } else { + lp->media = SPD_DET; + lp->local_state = 0; + if (sr) { /* Success! */ + lp->tmp = MII_SR_ASSC; + anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); + ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + if (!(anlpa & MII_ANLPA_RF) && + (cap = anlpa & MII_ANLPA_TAF & ana)) { + if (cap & MII_ANA_100M) { + lp->fdx = (ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) != 0; + lp->media = _100Mb; + } else if (cap & MII_ANA_10M) { + lp->fdx = (ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) != 0; + + lp->media = _10Mb; + } + } + } /* Auto Negotiation failed to finish */ + next_tick = dc21140m_autoconf(dev); + } /* Auto Negotiation failed to start */ + break; + } + break; + + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + if (lp->timeout < 0) { + lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : + (~gep_rd(dev) & GEP_LNP)); + SET_100Mb_PDET; + } + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { + next_tick = slnk & ~TIMER_CB; + } else { + if (is_spd_100(dev) && is_100_up(dev)) { + lp->media = _100Mb; + } else if ((!is_spd_100(dev) && (is_10_up(dev) & lp->tmp))) { + lp->media = _10Mb; + } else { + lp->media = NC; + } + next_tick = dc21140m_autoconf(dev); + } + break; + + case _100Mb: /* Set 100Mb/s */ + next_tick = 3000; + if (!lp->tx_enable) { + SET_100Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case BNC: + case AUI: + case _10Mb: /* Set 10Mb/s */ + next_tick = 3000; + if (!lp->tx_enable) { + SET_10Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case NC: + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = false; + break; + } + + return next_tick; +} + +/* +** This routine may be merged into dc21140m_autoconf() sometime as I'm +** changing how I figure out the media - but trying to keep it backwards +** compatible with the de500-xa and de500-aa. +** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock +** functions and set during de4x5_mac_port() and/or de4x5_reset_phy(). +** This routine just has to figure out whether 10Mb/s or 100Mb/s is +** active. +** When autonegotiation is working, the ANS part searches the SROM for +** the highest common speed (TP) link that both can run and if that can +** be full duplex. That infoblock is executed and then the link speed set. +** +** Only _10Mb and _100Mb are tested here. +*/ +static int +dc2114x_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; + int next_tick = DE4X5_AUTOSENSE_MS; + + switch (lp->media) { + case INIT: + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = false; + lp->linkOK = 0; + lp->timeout = -1; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + if (lp->params.autosense & ~AUTO) { + srom_map_media(dev); /* Fixed media requested */ + if (lp->media != lp->params.autosense) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + lp->media = INIT; + } + } + if ((next_tick = de4x5_reset_phy(dev)) < 0) { + next_tick &= ~TIMER_CB; + } else { + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if (lp->autosense == TP) { + lp->media = TP; + } else if (lp->autosense == BNC) { + lp->media = BNC; + } else if (lp->autosense == AUI) { + lp->media = AUI; + } else { + lp->media = SPD_DET; + if ((lp->infoblock_media == ANS) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } + } + lp->local_state = 0; + next_tick = dc2114x_autoconf(dev); + } + break; + + case ANS: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, false, 500); + if (cr < 0) { + next_tick = cr & ~TIMER_CB; + } else { + if (cr) { + lp->local_state = 0; + lp->media = SPD_DET; + } else { + lp->local_state++; + } + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + sr = test_mii_reg(dev, MII_SR, MII_SR_ASSC, true, 2000); + if (sr < 0) { + next_tick = sr & ~TIMER_CB; + } else { + lp->media = SPD_DET; + lp->local_state = 0; + if (sr) { /* Success! */ + lp->tmp = MII_SR_ASSC; + anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); + ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + if (!(anlpa & MII_ANLPA_RF) && + (cap = anlpa & MII_ANLPA_TAF & ana)) { + if (cap & MII_ANA_100M) { + lp->fdx = (ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) != 0; + lp->media = _100Mb; + } else if (cap & MII_ANA_10M) { + lp->fdx = (ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) != 0; + lp->media = _10Mb; + } + } + } /* Auto Negotiation failed to finish */ + next_tick = dc2114x_autoconf(dev); + } /* Auto Negotiation failed to start */ + break; + } + break; + + case AUI: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { + lp->media = BNC; + next_tick = dc2114x_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = AUI_SUSPECT; + next_tick = 3000; + } + break; + + case AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); + break; + + case BNC: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + lp->local_state++; /* Ensure media connected */ + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + if (!lp->tx_enable) { + if ((sts = ping_media(dev, 3000)) < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts) { + lp->local_state = 0; + lp->tcount++; + lp->media = INIT; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = BNC_SUSPECT; + next_tick = 3000; + } + break; + } + break; + + case BNC_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); + break; + + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + if (srom_map_media(dev) < 0) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + if (lp->media == _100Mb) { + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { + lp->media = SPD_DET; + return slnk & ~TIMER_CB; + } + } else { + if (wait_for_link(dev) < 0) { + lp->media = SPD_DET; + return PDET_LINK_WAIT; + } + } + if (lp->media == ANS) { /* Do MII parallel detection */ + if (is_spd_100(dev)) { + lp->media = _100Mb; + } else { + lp->media = _10Mb; + } + next_tick = dc2114x_autoconf(dev); + } else if (((lp->media == _100Mb) && is_100_up(dev)) || + (((lp->media == _10Mb) || (lp->media == TP) || + (lp->media == BNC) || (lp->media == AUI)) && + is_10_up(dev))) { + next_tick = dc2114x_autoconf(dev); + } else { + lp->tcount++; + lp->media = INIT; + } + break; + + case _10Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_10Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case _100Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_100Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + default: + lp->tcount++; +printk("Huh?: media:%02x\n", lp->media); + lp->media = INIT; + break; + } + + return next_tick; +} + +static int +srom_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + + return lp->infoleaf_fn(dev); +} + +/* +** This mapping keeps the original media codes and FDX flag unchanged. +** While it isn't strictly necessary, it helps me for the moment... +** The early return avoids a media state / SROM media space clash. +*/ +static int +srom_map_media(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + + lp->fdx = false; + if (lp->infoblock_media == lp->media) + return 0; + + switch(lp->infoblock_media) { + case SROM_10BASETF: + if (!lp->params.fdx) return -1; + lp->fdx = true; + case SROM_10BASET: + if (lp->params.fdx && !lp->fdx) return -1; + if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { + lp->media = _10Mb; + } else { + lp->media = TP; + } + break; + + case SROM_10BASE2: + lp->media = BNC; + break; + + case SROM_10BASE5: + lp->media = AUI; + break; + + case SROM_100BASETF: + if (!lp->params.fdx) return -1; + lp->fdx = true; + case SROM_100BASET: + if (lp->params.fdx && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case SROM_100BASET4: + lp->media = _100Mb; + break; + + case SROM_100BASEFF: + if (!lp->params.fdx) return -1; + lp->fdx = true; + case SROM_100BASEF: + if (lp->params.fdx && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case ANS: + lp->media = ANS; + lp->fdx = lp->params.fdx; + break; + + default: + printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, + lp->infoblock_media); + return -1; + break; + } + + return 0; +} + +static void +de4x5_init_connection(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + u_long flags = 0; + + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; /* Stop scrolling media messages */ + } + + spin_lock_irqsave(&lp->lock, flags); + de4x5_rst_desc_ring(dev); + de4x5_setup_intr(dev); + lp->tx_enable = true; + spin_unlock_irqrestore(&lp->lock, flags); + outl(POLL_DEMAND, DE4X5_TPD); + + netif_wake_queue(dev); +} + +/* +** General PHY reset function. Some MII devices don't reset correctly +** since their MII address pins can float at voltages that are dependent +** on the signal pin use. Do a double reset to ensure a reset. +*/ +static int +de4x5_reset_phy(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int next_tick = 0; + + if ((lp->useSROM) || (lp->phy[lp->active].id)) { + if (lp->timeout < 0) { + if (lp->useSROM) { + if (lp->phy[lp->active].rst) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].rst); + } else if (lp->rst) { /* Type 5 infoblock reset */ + srom_exec(dev, lp->rst); + srom_exec(dev, lp->rst); + } + } else { + PHY_HARD_RESET; + } + if (lp->useMII) { + mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + } + if (lp->useMII) { + next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, false, 500); + } + } else if (lp->chipset == DC21140) { + PHY_HARD_RESET; + } + + return next_tick; +} + +static int +test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + s32 sts, csr12; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */ + reset_init_sia(dev, csr13, csr14, csr15); + } + + /* set up the interrupt mask */ + outl(irq_mask, DE4X5_IMR); + + /* clear all pending interrupts */ + sts = inl(DE4X5_STS); + outl(sts, DE4X5_STS); + + /* clear csr12 NRA and SRA bits */ + if ((lp->chipset == DC21041) || lp->useSROM) { + csr12 = inl(DE4X5_SISR); + outl(csr12, DE4X5_SISR); + } + } + + sts = inl(DE4X5_STS) & ~TIMER_CB; + + if (!(sts & irqs) && --lp->timeout) { + sts = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sts; +} + +static int +test_tp(struct net_device *dev, s32 msec) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int sisr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + } + + sisr = (inl(DE4X5_SISR) & ~TIMER_CB) & (SISR_LKF | SISR_NCR); + + if (sisr && --lp->timeout) { + sisr = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sisr; +} + +/* +** Samples the 100Mb Link State Signal. The sample interval is important +** because too fast a rate can give erroneous results and confuse the +** speed sense algorithm. +*/ +#define SAMPLE_INTERVAL 500 /* ms */ +#define SAMPLE_DELAY 2000 /* ms */ +static int +test_for_100Mb(struct net_device *dev, int msec) +{ + struct de4x5_private *lp = netdev_priv(dev); + int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK); + + if (lp->timeout < 0) { + if ((msec/SAMPLE_INTERVAL) <= 0) return 0; + if (msec > SAMPLE_DELAY) { + lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL; + gep = SAMPLE_DELAY | TIMER_CB; + return gep; + } else { + lp->timeout = msec/SAMPLE_INTERVAL; + } + } + + if (lp->phy[lp->active].id || lp->useSROM) { + gep = is_100_up(dev) | is_spd_100(dev); + } else { + gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP)); + } + if (!(gep & ret) && --lp->timeout) { + gep = SAMPLE_INTERVAL | TIMER_CB; + } else { + lp->timeout = -1; + } + + return gep; +} + +static int +wait_for_link(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + + if (lp->timeout < 0) { + lp->timeout = 1; + } + + if (lp->timeout--) { + return TIMER_CB; + } else { + lp->timeout = -1; + } + + return 0; +} + +/* +** +** +*/ +static int +test_mii_reg(struct net_device *dev, int reg, int mask, bool pol, long msec) +{ + struct de4x5_private *lp = netdev_priv(dev); + int test; + u_long iobase = dev->base_addr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + } + + reg = mii_rd((u_char)reg, lp->phy[lp->active].addr, DE4X5_MII) & mask; + test = (reg ^ (pol ? ~0 : 0)) & mask; + + if (test && --lp->timeout) { + reg = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return reg; +} + +static int +is_spd_100(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int spd; + + if (lp->useMII) { + spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); + spd = ~(spd ^ lp->phy[lp->active].spd.value); + spd &= lp->phy[lp->active].spd.mask; + } else if (!lp->useSROM) { /* de500-xa */ + spd = ((~gep_rd(dev)) & GEP_SLNK); + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return (lp->chipset == DC21143) ? (~inl(DE4X5_SISR)&SISR_LS100) : 0; + + spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); + } + + return spd; +} + +static int +is_100_up(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + if (lp->useMII) { + /* Double read for sticky bits & temporary drops */ + mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); + return mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS; + } else if (!lp->useSROM) { /* de500-xa */ + return (~gep_rd(dev)) & GEP_SLNK; + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return (lp->chipset == DC21143) ? (~inl(DE4X5_SISR)&SISR_LS100) : 0; + + return (lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); + } +} + +static int +is_10_up(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + if (lp->useMII) { + /* Double read for sticky bits & temporary drops */ + mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); + return mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS; + } else if (!lp->useSROM) { /* de500-xa */ + return (~gep_rd(dev)) & GEP_LNP; + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset & ~0x00ff) == DC2114x) ? + (~inl(DE4X5_SISR)&SISR_LS10): + 0; + + return (lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); + } +} + +static int +is_anc_capable(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + return mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SISR) & SISR_LPN) >> 12; + } else { + return 0; + } +} + +/* +** Send a packet onto the media and watch for send errors that indicate the +** media is bad or unconnected. +*/ +static int +ping_media(struct net_device *dev, int msec) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int sisr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + + lp->tmp = lp->tx_new; /* Remember the ring position */ + load_packet(dev, lp->frame, TD_LS | TD_FS | sizeof(lp->frame), (struct sk_buff *)1); + lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); + } + + sisr = inl(DE4X5_SISR); + + if ((!(sisr & SISR_NCR)) && + ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && + (--lp->timeout)) { + sisr = 100 | TIMER_CB; + } else { + if ((!(sisr & SISR_NCR)) && + !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) && + lp->timeout) { + sisr = 0; + } else { + sisr = 1; + } + lp->timeout = -1; + } + + return sisr; +} + +/* +** This function does 2 things: on Intels it kmalloc's another buffer to +** replace the one about to be passed up. On Alpha's it kmallocs a buffer +** into which the packet is copied. +*/ +static struct sk_buff * +de4x5_alloc_rx_buff(struct net_device *dev, int index, int len) +{ + struct de4x5_private *lp = netdev_priv(dev); + struct sk_buff *p; + +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY) + struct sk_buff *ret; + u_long i=0, tmp; + + p = dev_alloc_skb(IEEE802_3_SZ + DE4X5_ALIGN + 2); + if (!p) return NULL; + + tmp = virt_to_bus(p->data); + i = ((tmp + DE4X5_ALIGN) & ~DE4X5_ALIGN) - tmp; + skb_reserve(p, i); + lp->rx_ring[index].buf = cpu_to_le32(tmp + i); + + ret = lp->rx_skb[index]; + lp->rx_skb[index] = p; + + if ((u_long) ret > 1) { + skb_put(ret, len); + } + + return ret; + +#else + if (lp->state != OPEN) return (struct sk_buff *)1; /* Fake out the open */ + + p = dev_alloc_skb(len + 2); + if (!p) return NULL; + + skb_reserve(p, 2); /* Align */ + if (index < lp->rx_old) { /* Wrapped buffer */ + short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; + memcpy(skb_put(p,tlen),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,tlen); + memcpy(skb_put(p,len-tlen),lp->rx_bufs,len-tlen); + } else { /* Linear buffer */ + memcpy(skb_put(p,len),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,len); + } + + return p; +#endif +} + +static void +de4x5_free_rx_buffs(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + int i; + + for (i=0; irxRingSize; i++) { + if ((u_long) lp->rx_skb[i] > 1) { + dev_kfree_skb(lp->rx_skb[i]); + } + lp->rx_ring[i].status = 0; + lp->rx_skb[i] = (struct sk_buff *)1; /* Dummy entry */ + } +} + +static void +de4x5_free_tx_buffs(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + int i; + + for (i=0; itxRingSize; i++) { + if (lp->tx_skb[i]) + de4x5_free_tx_buff(lp, i); + lp->tx_ring[i].status = 0; + } + + /* Unload the locally queued packets */ + __skb_queue_purge(&lp->cache.queue); +} + +/* +** When a user pulls a connection, the DECchip can end up in a +** 'running - waiting for end of transmission' state. This means that we +** have to perform a chip soft reset to ensure that we can synchronize +** the hardware and software and make any media probes using a loopback +** packet meaningful. +*/ +static void +de4x5_save_skbs(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + s32 omr; + + if (!lp->cache.save_cnt) { + STOP_DE4X5; + de4x5_tx(dev); /* Flush any sent skb's */ + de4x5_free_tx_buffs(dev); + de4x5_cache_state(dev, DE4X5_SAVE_STATE); + de4x5_sw_reset(dev); + de4x5_cache_state(dev, DE4X5_RESTORE_STATE); + lp->cache.save_cnt++; + START_DE4X5; + } +} + +static void +de4x5_rst_desc_ring(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int i; + s32 omr; + + if (lp->cache.save_cnt) { + STOP_DE4X5; + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + lp->rx_new = lp->rx_old = 0; + lp->tx_new = lp->tx_old = 0; + + for (i = 0; i < lp->rxRingSize; i++) { + lp->rx_ring[i].status = cpu_to_le32(R_OWN); + } + + for (i = 0; i < lp->txRingSize; i++) { + lp->tx_ring[i].status = cpu_to_le32(0); + } + + barrier(); + lp->cache.save_cnt--; + START_DE4X5; + } +} + +static void +de4x5_cache_state(struct net_device *dev, int flag) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + switch(flag) { + case DE4X5_SAVE_STATE: + lp->cache.csr0 = inl(DE4X5_BMR); + lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR)); + lp->cache.csr7 = inl(DE4X5_IMR); + break; + + case DE4X5_RESTORE_STATE: + outl(lp->cache.csr0, DE4X5_BMR); + outl(lp->cache.csr6, DE4X5_OMR); + outl(lp->cache.csr7, DE4X5_IMR); + if (lp->chipset == DC21140) { + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); + } else { + reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, + lp->cache.csr15); + } + break; + } +} + +static void +de4x5_put_cache(struct net_device *dev, struct sk_buff *skb) +{ + struct de4x5_private *lp = netdev_priv(dev); + + __skb_queue_tail(&lp->cache.queue, skb); +} + +static void +de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb) +{ + struct de4x5_private *lp = netdev_priv(dev); + + __skb_queue_head(&lp->cache.queue, skb); +} + +static struct sk_buff * +de4x5_get_cache(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + + return __skb_dequeue(&lp->cache.queue); +} + +/* +** Check the Auto Negotiation State. Return OK when a link pass interrupt +** is received and the auto-negotiation status is NWAY OK. +*/ +static int +test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + s32 sts, ans; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + outl(irq_mask, DE4X5_IMR); + + /* clear all pending interrupts */ + sts = inl(DE4X5_STS); + outl(sts, DE4X5_STS); + } + + ans = inl(DE4X5_SISR) & SISR_ANS; + sts = inl(DE4X5_STS) & ~TIMER_CB; + + if (!(sts & irqs) && (ans ^ ANS_NWOK) && --lp->timeout) { + sts = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sts; +} + +static void +de4x5_setup_intr(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + s32 imr, sts; + + if (inl(DE4X5_OMR) & OMR_SR) { /* Only unmask if TX/RX is enabled */ + imr = 0; + UNMASK_IRQs; + sts = inl(DE4X5_STS); /* Reset any pending (stale) interrupts */ + outl(sts, DE4X5_STS); + ENABLE_IRQs; + } +} + +/* +** +*/ +static void +reset_init_sia(struct net_device *dev, s32 csr13, s32 csr14, s32 csr15) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + RESET_SIA; + if (lp->useSROM) { + if (lp->ibn == 3) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].gep); + outl(1, DE4X5_SICR); + return; + } else { + csr15 = lp->cache.csr15; + csr14 = lp->cache.csr14; + csr13 = lp->cache.csr13; + outl(csr15 | lp->cache.gepc, DE4X5_SIGR); + outl(csr15 | lp->cache.gep, DE4X5_SIGR); + } + } else { + outl(csr15, DE4X5_SIGR); + } + outl(csr14, DE4X5_STRR); + outl(csr13, DE4X5_SICR); + + mdelay(10); +} + +/* +** Create a loopback ethernet packet +*/ +static void +create_packet(struct net_device *dev, char *frame, int len) +{ + int i; + char *buf = frame; + + for (i=0; idev_addr[i]; + } + for (i=0; idev_addr[i]; + } + + *buf++ = 0; /* Packet length (2 bytes) */ + *buf++ = 1; +} + +/* +** Look for a particular board name in the EISA configuration space +*/ +static int +EISA_signature(char *name, struct device *device) +{ + int i, status = 0, siglen = ARRAY_SIZE(de4x5_signatures); + struct eisa_device *edev; + + *name = '\0'; + edev = to_eisa_device (device); + i = edev->id.driver_data; + + if (i >= 0 && i < siglen) { + strcpy (name, de4x5_signatures[i]); + status = 1; + } + + return status; /* return the device name string */ +} + +/* +** Look for a particular board name in the PCI configuration space +*/ +static int +PCI_signature(char *name, struct de4x5_private *lp) +{ + int i, status = 0, siglen = ARRAY_SIZE(de4x5_signatures); + + if (lp->chipset == DC21040) { + strcpy(name, "DE434/5"); + return status; + } else { /* Search for a DEC name in the SROM */ + int tmp = *((char *)&lp->srom + 19) * 3; + strncpy(name, (char *)&lp->srom + 26 + tmp, 8); + } + name[8] = '\0'; + for (i=0; ichipset == DC21040) ? "DC21040" : + ((lp->chipset == DC21041) ? "DC21041" : + ((lp->chipset == DC21140) ? "DC21140" : + ((lp->chipset == DC21142) ? "DC21142" : + ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN" + ))))))); + } + if (lp->chipset != DC21041) { + lp->useSROM = true; /* card is not recognisably DEC */ + } + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + lp->useSROM = true; + } + + return status; +} + +/* +** Set up the Ethernet PROM counter to the start of the Ethernet address on +** the DC21040, else read the SROM for the other chips. +** The SROM may not be present in a multi-MAC card, so first read the +** MAC address and check for a bad address. If there is a bad one then exit +** immediately with the prior srom contents intact (the h/w address will +** be fixed up later). +*/ +static void +DevicePresent(struct net_device *dev, u_long aprom_addr) +{ + int i, j=0; + struct de4x5_private *lp = netdev_priv(dev); + + if (lp->chipset == DC21040) { + if (lp->bus == EISA) { + enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } else { + outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } + } else { /* Read new srom */ + u_short tmp; + __le16 *p = (__le16 *)((char *)&lp->srom + SROM_HWADD); + for (i=0; i<(ETH_ALEN>>1); i++) { + tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i); + j += tmp; /* for check for 0:0:0:0:0:0 or ff:ff:ff:ff:ff:ff */ + *p = cpu_to_le16(tmp); + } + if (j == 0 || j == 3 * 0xffff) { + /* could get 0 only from all-0 and 3 * 0xffff only from all-1 */ + return; + } + + p = (__le16 *)&lp->srom; + for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { + tmp = srom_rd(aprom_addr, i); + *p++ = cpu_to_le16(tmp); + } + de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); + } +} + +/* +** Since the write on the Enet PROM register doesn't seem to reset the PROM +** pointer correctly (at least on my DE425 EISA card), this routine should do +** it...from depca.c. +*/ +static void +enet_addr_rst(u_long aprom_addr) +{ + union { + struct { + u32 a; + u32 b; + } llsig; + char Sig[sizeof(u32) << 1]; + } dev; + short sigLength=0; + s8 data; + int i, j; + + dev.llsig.a = ETH_PROM_SIG; + dev.llsig.b = ETH_PROM_SIG; + sigLength = sizeof(u32) << 1; + + for (i=0,j=0;jbase_addr; + int broken, i, k, tmp, status = 0; + u_short j,chksum; + struct de4x5_private *lp = netdev_priv(dev); + + broken = de4x5_bad_srom(lp); + + for (i=0,k=0,j=0;j<3;j++) { + k <<= 1; + if (k > 0xffff) k-=0xffff; + + if (lp->bus == PCI) { + if (lp->chipset == DC21040) { + while ((tmp = inl(DE4X5_APROM)) < 0); + k += (u_char) tmp; + dev->dev_addr[i++] = (u_char) tmp; + while ((tmp = inl(DE4X5_APROM)) < 0); + k += (u_short) (tmp << 8); + dev->dev_addr[i++] = (u_char) tmp; + } else if (!broken) { + dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; + dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; + } else if ((broken == SMC) || (broken == ACCTON)) { + dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; + dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; + } + } else { + k += (u_char) (tmp = inb(EISA_APROM)); + dev->dev_addr[i++] = (u_char) tmp; + k += (u_short) ((tmp = inb(EISA_APROM)) << 8); + dev->dev_addr[i++] = (u_char) tmp; + } + + if (k > 0xffff) k-=0xffff; + } + if (k == 0xffff) k=0; + + if (lp->bus == PCI) { + if (lp->chipset == DC21040) { + while ((tmp = inl(DE4X5_APROM)) < 0); + chksum = (u_char) tmp; + while ((tmp = inl(DE4X5_APROM)) < 0); + chksum |= (u_short) (tmp << 8); + if ((k != chksum) && (dec_only)) status = -1; + } + } else { + chksum = (u_char) inb(EISA_APROM); + chksum |= (u_short) (inb(EISA_APROM) << 8); + if ((k != chksum) && (dec_only)) status = -1; + } + + /* If possible, try to fix a broken card - SMC only so far */ + srom_repair(dev, broken); + +#ifdef CONFIG_PPC_PMAC + /* + ** If the address starts with 00 a0, we have to bit-reverse + ** each byte of the address. + */ + if ( machine_is(powermac) && + (dev->dev_addr[0] == 0) && + (dev->dev_addr[1] == 0xa0) ) + { + for (i = 0; i < ETH_ALEN; ++i) + { + int x = dev->dev_addr[i]; + x = ((x & 0xf) << 4) + ((x & 0xf0) >> 4); + x = ((x & 0x33) << 2) + ((x & 0xcc) >> 2); + dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); + } + } +#endif /* CONFIG_PPC_PMAC */ + + /* Test for a bad enet address */ + status = test_bad_enet(dev, status); + + return status; +} + +/* +** Test for enet addresses in the first 32 bytes. The built-in strncmp +** didn't seem to work here...? +*/ +static int +de4x5_bad_srom(struct de4x5_private *lp) +{ + int i, status = 0; + + for (i = 0; i < ARRAY_SIZE(enet_det); i++) { + if (!de4x5_strncmp((char *)&lp->srom, (char *)&enet_det[i], 3) && + !de4x5_strncmp((char *)&lp->srom+0x10, (char *)&enet_det[i], 3)) { + if (i == 0) { + status = SMC; + } else if (i == 1) { + status = ACCTON; + } + break; + } + } + + return status; +} + +static int +de4x5_strncmp(char *a, char *b, int n) +{ + int ret=0; + + for (;n && !ret; n--) { + ret = *a++ - *b++; + } + + return ret; +} + +static void +srom_repair(struct net_device *dev, int card) +{ + struct de4x5_private *lp = netdev_priv(dev); + + switch(card) { + case SMC: + memset((char *)&lp->srom, 0, sizeof(struct de4x5_srom)); + memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN); + memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100); + lp->useSROM = true; + break; + } +} + +/* +** Assume that the irq's do not follow the PCI spec - this is seems +** to be true so far (2 for 2). +*/ +static int +test_bad_enet(struct net_device *dev, int status) +{ + struct de4x5_private *lp = netdev_priv(dev); + int i, tmp; + + for (tmp=0,i=0; idev_addr[i]; + if ((tmp == 0) || (tmp == 0x5fa)) { + if ((lp->chipset == last.chipset) && + (lp->bus_num == last.bus) && (lp->bus_num > 0)) { + for (i=0; idev_addr[i] = last.addr[i]; + for (i=ETH_ALEN-1; i>2; --i) { + dev->dev_addr[i] += 1; + if (dev->dev_addr[i] != 0) break; + } + for (i=0; idev_addr[i]; + if (!an_exception(lp)) { + dev->irq = last.irq; + } + + status = 0; + } + } else if (!status) { + last.chipset = lp->chipset; + last.bus = lp->bus_num; + last.irq = dev->irq; + for (i=0; idev_addr[i]; + } + + return status; +} + +/* +** List of board exceptions with correctly wired IRQs +*/ +static int +an_exception(struct de4x5_private *lp) +{ + if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) && + (*(u_short *)lp->srom.sub_system_id == 0x95e0)) { + return -1; + } + + return 0; +} + +/* +** SROM Read +*/ +static short +srom_rd(u_long addr, u_char offset) +{ + sendto_srom(SROM_RD | SROM_SR, addr); + + srom_latch(SROM_RD | SROM_SR | DT_CS, addr); + srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr); + srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset); + + return srom_data(SROM_RD | SROM_SR | DT_CS, addr); +} + +static void +srom_latch(u_int command, u_long addr) +{ + sendto_srom(command, addr); + sendto_srom(command | DT_CLK, addr); + sendto_srom(command, addr); +} + +static void +srom_command(u_int command, u_long addr) +{ + srom_latch(command, addr); + srom_latch(command, addr); + srom_latch((command & 0x0000ff00) | DT_CS, addr); +} + +static void +srom_address(u_int command, u_long addr, u_char offset) +{ + int i, a; + + a = offset << 2; + for (i=0; i<6; i++, a <<= 1) { + srom_latch(command | ((a & 0x80) ? DT_IN : 0), addr); + } + udelay(1); + + i = (getfrom_srom(addr) >> 3) & 0x01; +} + +static short +srom_data(u_int command, u_long addr) +{ + int i; + short word = 0; + s32 tmp; + + for (i=0; i<16; i++) { + sendto_srom(command | DT_CLK, addr); + tmp = getfrom_srom(addr); + sendto_srom(command, addr); + + word = (word << 1) | ((tmp >> 3) & 0x01); + } + + sendto_srom(command & 0x0000ff00, addr); + + return word; +} + +/* +static void +srom_busy(u_int command, u_long addr) +{ + sendto_srom((command & 0x0000ff00) | DT_CS, addr); + + while (!((getfrom_srom(addr) >> 3) & 0x01)) { + mdelay(1); + } + + sendto_srom(command & 0x0000ff00, addr); +} +*/ + +static void +sendto_srom(u_int command, u_long addr) +{ + outl(command, addr); + udelay(1); +} + +static int +getfrom_srom(u_long addr) +{ + s32 tmp; + + tmp = inl(addr); + udelay(1); + + return tmp; +} + +static int +srom_infoleaf_info(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + int i, count; + u_char *p; + + /* Find the infoleaf decoder function that matches this chipset */ + for (i=0; ichipset == infoleaf_array[i].chipset) break; + } + if (i == INFOLEAF_SIZE) { + lp->useSROM = false; + printk("%s: Cannot find correct chipset for SROM decoding!\n", + dev->name); + return -ENXIO; + } + + lp->infoleaf_fn = infoleaf_array[i].fn; + + /* Find the information offset that this function should use */ + count = *((u_char *)&lp->srom + 19); + p = (u_char *)&lp->srom + 26; + + if (count > 1) { + for (i=count; i; --i, p+=3) { + if (lp->device == *p) break; + } + if (i == 0) { + lp->useSROM = false; + printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", + dev->name, lp->device); + return -ENXIO; + } + } + + lp->infoleaf_offset = get_unaligned_le16(p + 1); + + return 0; +} + +/* +** This routine loads any type 1 or 3 MII info into the mii device +** struct and executes any type 5 code to reset PHY devices for this +** controller. +** The info for the MII devices will be valid since the index used +** will follow the discovery process from MII address 1-31 then 0. +*/ +static void +srom_init(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + u_char count; + + p+=2; + if (lp->chipset == DC21140) { + lp->cache.gepc = (*p++ | GEP_CTRL); + gep_wr(lp->cache.gepc, dev); + } + + /* Block count */ + count = *p++; + + /* Jump the infoblocks to find types */ + for (;count; --count) { + if (*p < 128) { + p += COMPACT_LEN; + } else if (*(p+1) == 5) { + type5_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 4) { + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 3) { + type3_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 2) { + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 1) { + type1_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else { + p += ((*p & BLOCK_LEN) + 1); + } + } +} + +/* +** A generic routine that writes GEP control, data and reset information +** to the GEP register (21140) or csr15 GEP portion (2114[23]). +*/ +static void +srom_exec(struct net_device *dev, u_char *p) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + u_char count = (p ? *p++ : 0); + u_short *w = (u_short *)p; + + if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return; + + if (lp->chipset != DC21140) RESET_SIA; + + while (count--) { + gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? + *p++ : get_unaligned_le16(w++)), dev); + mdelay(2); /* 2ms per action */ + } + + if (lp->chipset != DC21140) { + outl(lp->cache.csr14, DE4X5_STRR); + outl(lp->cache.csr13, DE4X5_SICR); + } +} + +/* +** Basically this function is a NOP since it will never be called, +** unless I implement the DC21041 SROM functions. There's no need +** since the existing code will be satisfactory for all boards. +*/ +static int +dc21041_infoleaf(struct net_device *dev) +{ + return DE4X5_AUTOSENSE_MS; +} + +static int +dc21140_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* GEP control */ + lp->cache.gepc = (*p++ | GEP_CTRL); + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = false; + } + + return next_tick & ~TIMER_CB; +} + +static int +dc21142_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = false; + } + + return next_tick & ~TIMER_CB; +} + +static int +dc21143_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = false; + } + + return next_tick & ~TIMER_CB; +} + +/* +** The compact infoblock is only designed for DC21140[A] chips, so +** we'll reuse the dc21140m_autoconf function. Non MII media only. +*/ +static int +compact_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char flags, csr6; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+COMPACT_LEN) < 128) { + return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN); + } else { + return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = COMPACT; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); + lp->infoblock_media = (*p++) & COMPACT_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = false; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* +** This block describes non MII media for the DC21140[A] only. +*/ +static int +type0_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 0; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); + p+=2; + lp->infoblock_media = (*p++) & BLOCK0_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = false; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* These functions are under construction! */ + +static int +type1_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + p += 2; + if (lp->state == INITIALISED) { + lp->ibn = 1; + lp->active = *p++; + lp->phy[lp->active].gep = (*p ? p : NULL); p += (*p + 1); + lp->phy[lp->active].rst = (*p ? p : NULL); p += (*p + 1); + lp->phy[lp->active].mc = get_unaligned_le16(p); p += 2; + lp->phy[lp->active].ana = get_unaligned_le16(p); p += 2; + lp->phy[lp->active].fdx = get_unaligned_le16(p); p += 2; + lp->phy[lp->active].ttm = get_unaligned_le16(p); + return 0; + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 1; + lp->active = *p; + lp->infoblock_csr6 = OMR_MII_100; + lp->useMII = true; + lp->infoblock_media = ANS; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +static int +type2_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 2; + lp->active = 0; + p += 2; + lp->infoblock_media = (*p) & MEDIA_CODE; + + if ((*p++) & EXT_FIELD) { + lp->cache.csr13 = get_unaligned_le16(p); p += 2; + lp->cache.csr14 = get_unaligned_le16(p); p += 2; + lp->cache.csr15 = get_unaligned_le16(p); p += 2; + } else { + lp->cache.csr13 = CSR13; + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + } + lp->cache.gepc = ((s32)(get_unaligned_le16(p)) << 16); p += 2; + lp->cache.gep = ((s32)(get_unaligned_le16(p)) << 16); + lp->infoblock_csr6 = OMR_SIA; + lp->useMII = false; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +static int +type3_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + p += 2; + if (lp->state == INITIALISED) { + lp->ibn = 3; + lp->active = *p++; + if (MOTO_SROM_BUG) lp->active = 0; + lp->phy[lp->active].gep = (*p ? p : NULL); p += (2 * (*p) + 1); + lp->phy[lp->active].rst = (*p ? p : NULL); p += (2 * (*p) + 1); + lp->phy[lp->active].mc = get_unaligned_le16(p); p += 2; + lp->phy[lp->active].ana = get_unaligned_le16(p); p += 2; + lp->phy[lp->active].fdx = get_unaligned_le16(p); p += 2; + lp->phy[lp->active].ttm = get_unaligned_le16(p); p += 2; + lp->phy[lp->active].mci = *p; + return 0; + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 3; + lp->active = *p; + if (MOTO_SROM_BUG) lp->active = 0; + lp->infoblock_csr6 = OMR_MII_100; + lp->useMII = true; + lp->infoblock_media = ANS; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +static int +type4_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 4; + lp->active = 0; + p+=2; + lp->infoblock_media = (*p++) & MEDIA_CODE; + lp->cache.csr13 = CSR13; /* Hard coded defaults */ + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + lp->cache.gepc = ((s32)(get_unaligned_le16(p)) << 16); p += 2; + lp->cache.gep = ((s32)(get_unaligned_le16(p)) << 16); p += 2; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = false; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +/* +** This block type provides information for resetting external devices +** (chips) through the General Purpose Register. +*/ +static int +type5_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + /* Must be initializing to run this code */ + if ((lp->state == INITIALISED) || (lp->media == INIT)) { + p+=2; + lp->rst = p; + srom_exec(dev, lp->rst); + } + + return DE4X5_AUTOSENSE_MS; +} + +/* +** MII Read/Write +*/ + +static int +mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) +{ + mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ + mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ + mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */ + mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ + mii_address(phyreg, ioaddr); /* PHY Register to read */ + mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */ + + return mii_rdata(ioaddr); /* Read data */ +} + +static void +mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr) +{ + mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ + mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ + mii_wdata(MII_STWR, 4, ioaddr); /* SFD and Write operation */ + mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ + mii_address(phyreg, ioaddr); /* PHY Register to write */ + mii_ta(MII_STWR, ioaddr); /* Turn around time - 2 MDC */ + data = mii_swap(data, 16); /* Swap data bit ordering */ + mii_wdata(data, 16, ioaddr); /* Write data */ +} + +static int +mii_rdata(u_long ioaddr) +{ + int i; + s32 tmp = 0; + + for (i=0; i<16; i++) { + tmp <<= 1; + tmp |= getfrom_mii(MII_MRD | MII_RD, ioaddr); + } + + return tmp; +} + +static void +mii_wdata(int data, int len, u_long ioaddr) +{ + int i; + + for (i=0; i>= 1; + } +} + +static void +mii_address(u_char addr, u_long ioaddr) +{ + int i; + + addr = mii_swap(addr, 5); + for (i=0; i<5; i++) { + sendto_mii(MII_MWR | MII_WR, addr, ioaddr); + addr >>= 1; + } +} + +static void +mii_ta(u_long rw, u_long ioaddr) +{ + if (rw == MII_STWR) { + sendto_mii(MII_MWR | MII_WR, 1, ioaddr); + sendto_mii(MII_MWR | MII_WR, 0, ioaddr); + } else { + getfrom_mii(MII_MRD | MII_RD, ioaddr); /* Tri-state MDIO */ + } +} + +static int +mii_swap(int data, int len) +{ + int i, tmp = 0; + + for (i=0; i>= 1; + } + + return tmp; +} + +static void +sendto_mii(u32 command, int data, u_long ioaddr) +{ + u32 j; + + j = (data & 1) << 17; + outl(command | j, ioaddr); + udelay(1); + outl(command | MII_MDC | j, ioaddr); + udelay(1); +} + +static int +getfrom_mii(u32 command, u_long ioaddr) +{ + outl(command, ioaddr); + udelay(1); + outl(command | MII_MDC, ioaddr); + udelay(1); + + return (inl(ioaddr) >> 19) & 1; +} + +/* +** Here's 3 ways to calculate the OUI from the ID registers. +*/ +static int +mii_get_oui(u_char phyaddr, u_long ioaddr) +{ +/* + union { + u_short reg; + u_char breg[2]; + } a; + int i, r2, r3, ret=0;*/ + int r2, r3; + + /* Read r2 and r3 */ + r2 = mii_rd(MII_ID0, phyaddr, ioaddr); + r3 = mii_rd(MII_ID1, phyaddr, ioaddr); + /* SEEQ and Cypress way * / + / * Shuffle r2 and r3 * / + a.reg=0; + r3 = ((r3>>10)|(r2<<6))&0x0ff; + r2 = ((r2>>2)&0x3fff); + + / * Bit reverse r3 * / + for (i=0;i<8;i++) { + ret<<=1; + ret |= (r3&1); + r3>>=1; + } + + / * Bit reverse r2 * / + for (i=0;i<16;i++) { + a.reg<<=1; + a.reg |= (r2&1); + r2>>=1; + } + + / * Swap r2 bytes * / + i=a.breg[0]; + a.breg[0]=a.breg[1]; + a.breg[1]=i; + + return (a.reg<<8)|ret; */ /* SEEQ and Cypress way */ +/* return (r2<<6)|(u_int)(r3>>10); */ /* NATIONAL and BROADCOM way */ + return r2; /* (I did it) My way */ +} + +/* +** The SROM spec forces us to search addresses [1-31 0]. Bummer. +*/ +static int +mii_get_phy(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + int i, j, k, n, limit=ARRAY_SIZE(phy_info); + int id; + + lp->active = 0; + lp->useMII = true; + + /* Search the MII address space for possible PHY devices */ + for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(i+1)%DE4X5_MAX_MII) { + lp->phy[lp->active].addr = i; + if (i==0) n++; /* Count cycles */ + while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ + id = mii_get_oui(i, DE4X5_MII); + if ((id == 0) || (id == 65535)) continue; /* Valid ID? */ + for (j=0; jphy[k].id; k++); + if (k < DE4X5_MAX_PHY) { + memcpy((char *)&lp->phy[k], + (char *)&phy_info[j], sizeof(struct phy_table)); + lp->phy[k].addr = i; + lp->mii_cnt++; + lp->active++; + } else { + goto purgatory; /* Stop the search */ + } + break; + } + if ((j == limit) && (i < DE4X5_MAX_MII)) { + for (k=0; k < DE4X5_MAX_PHY && lp->phy[k].id; k++); + lp->phy[k].addr = i; + lp->phy[k].id = id; + lp->phy[k].spd.reg = GENERIC_REG; /* ANLPA register */ + lp->phy[k].spd.mask = GENERIC_MASK; /* 100Mb/s technologies */ + lp->phy[k].spd.value = GENERIC_VALUE; /* TX & T4, H/F Duplex */ + lp->mii_cnt++; + lp->active++; + printk("%s: Using generic MII device control. If the board doesn't operate,\nplease mail the following dump to the author:\n", dev->name); + j = de4x5_debug; + de4x5_debug |= DEBUG_MII; + de4x5_dbg_mii(dev, k); + de4x5_debug = j; + printk("\n"); + } + } + purgatory: + lp->active = 0; + if (lp->phy[0].id) { /* Reset the PHY devices */ + for (k=0; k < DE4X5_MAX_PHY && lp->phy[k].id; k++) { /*For each PHY*/ + mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII); + while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST); + + de4x5_dbg_mii(dev, k); + } + } + if (!lp->mii_cnt) lp->useMII = false; + + return lp->mii_cnt; +} + +static char * +build_setup_frame(struct net_device *dev, int mode) +{ + struct de4x5_private *lp = netdev_priv(dev); + int i; + char *pa = lp->setup_frame; + + /* Initialise the setup frame */ + if (mode == ALL) { + memset(lp->setup_frame, 0, SETUP_FRAME_LEN); + } + + if (lp->setup_f == HASH_PERF) { + for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; idev_addr[i]; /* Host address */ + if (i & 0x01) pa += 2; + } + *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; + } else { + for (i=0; idev_addr[i]; + if (i & 0x01) pa += 4; + } + for (i=0; itimer); +} + +static long +de4x5_switch_mac_port(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + s32 omr; + + STOP_DE4X5; + + /* Assert the OMR_PS bit in CSR6 */ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | + OMR_FDX)); + omr |= lp->infoblock_csr6; + if (omr & OMR_PS) omr |= OMR_HBD; + outl(omr, DE4X5_OMR); + + /* Soft Reset */ + RESET_DE4X5; + + /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */ + if (lp->chipset == DC21140) { + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); + } else if ((lp->chipset & ~0x0ff) == DC2114x) { + reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); + } + + /* Restore CSR6 */ + outl(omr, DE4X5_OMR); + + /* Reset CSR8 */ + inl(DE4X5_MFC); + + return omr; +} + +static void +gep_wr(s32 data, struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + outl(data, DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + outl((data<<16) | lp->cache.csr15, DE4X5_SIGR); + } +} + +static int +gep_rd(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + return inl(DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return inl(DE4X5_SIGR) & 0x000fffff; + } + + return 0; +} + +static void +yawn(struct net_device *dev, int state) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; + + if(lp->bus == EISA) { + switch(state) { + case WAKEUP: + outb(WAKEUP, PCI_CFPM); + mdelay(10); + break; + + case SNOOZE: + outb(SNOOZE, PCI_CFPM); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + outb(SLEEP, PCI_CFPM); + break; + } + } else { + struct pci_dev *pdev = to_pci_dev (lp->gendev); + switch(state) { + case WAKEUP: + pci_write_config_byte(pdev, PCI_CFDA_PSM, WAKEUP); + mdelay(10); + break; + + case SNOOZE: + pci_write_config_byte(pdev, PCI_CFDA_PSM, SNOOZE); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + pci_write_config_byte(pdev, PCI_CFDA_PSM, SLEEP); + break; + } + } +} + +static void +de4x5_parse_params(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + char *p, *q, t; + + lp->params.fdx = 0; + lp->params.autosense = AUTO; + + if (args == NULL) return; + + if ((p = strstr(args, dev->name))) { + if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p); + t = *q; + *q = '\0'; + + if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1; + + if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) { + if (strstr(p, "TP")) { + lp->params.autosense = TP; + } else if (strstr(p, "TP_NW")) { + lp->params.autosense = TP_NW; + } else if (strstr(p, "BNC")) { + lp->params.autosense = BNC; + } else if (strstr(p, "AUI")) { + lp->params.autosense = AUI; + } else if (strstr(p, "BNC_AUI")) { + lp->params.autosense = BNC; + } else if (strstr(p, "10Mb")) { + lp->params.autosense = _10Mb; + } else if (strstr(p, "100Mb")) { + lp->params.autosense = _100Mb; + } else if (strstr(p, "AUTO")) { + lp->params.autosense = AUTO; + } + } + *q = t; + } +} + +static void +de4x5_dbg_open(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + int i; + + if (de4x5_debug & DEBUG_OPEN) { + printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq); + printk("\tphysical address: "); + for (i=0;i<6;i++) { + printk("%2.2x:",(short)dev->dev_addr[i]); + } + printk("\n"); + printk("Descriptor head addresses:\n"); + printk("\t0x%8.8lx 0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring); + printk("Descriptor addresses:\nRX: "); + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8lx ",(u_long)&lp->rx_ring[i].status); + } + } + printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status); + printk("TX: "); + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8lx ", (u_long)&lp->tx_ring[i].status); + } + } + printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status); + printk("Descriptor buffers:\nRX: "); + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf)); + } + } + printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf)); + printk("TX: "); + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf)); + } + } + printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf)); + printk("Ring size:\nRX: %d\nTX: %d\n", + (short)lp->rxRingSize, + (short)lp->txRingSize); + } +} + +static void +de4x5_dbg_mii(struct net_device *dev, int k) +{ + struct de4x5_private *lp = netdev_priv(dev); + u_long iobase = dev->base_addr; + + if (de4x5_debug & DEBUG_MII) { + printk("\nMII device address: %d\n", lp->phy[k].addr); + printk("MII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); + printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); + printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); + printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII)); + if (lp->phy[k].id != BROADCOM_T4) { + printk("MII ANA: %x\n",mii_rd(0x04,lp->phy[k].addr,DE4X5_MII)); + printk("MII ANC: %x\n",mii_rd(0x05,lp->phy[k].addr,DE4X5_MII)); + } + printk("MII 16: %x\n",mii_rd(0x10,lp->phy[k].addr,DE4X5_MII)); + if (lp->phy[k].id != BROADCOM_T4) { + printk("MII 17: %x\n",mii_rd(0x11,lp->phy[k].addr,DE4X5_MII)); + printk("MII 18: %x\n",mii_rd(0x12,lp->phy[k].addr,DE4X5_MII)); + } else { + printk("MII 20: %x\n",mii_rd(0x14,lp->phy[k].addr,DE4X5_MII)); + } + } +} + +static void +de4x5_dbg_media(struct net_device *dev) +{ + struct de4x5_private *lp = netdev_priv(dev); + + if (lp->media != lp->c_media) { + if (de4x5_debug & DEBUG_MEDIA) { + printk("%s: media is %s%s\n", dev->name, + (lp->media == NC ? "unconnected, link down or incompatible connection" : + (lp->media == TP ? "TP" : + (lp->media == ANS ? "TP/Nway" : + (lp->media == BNC ? "BNC" : + (lp->media == AUI ? "AUI" : + (lp->media == BNC_AUI ? "BNC/AUI" : + (lp->media == EXT_SIA ? "EXT SIA" : + (lp->media == _100Mb ? "100Mb/s" : + (lp->media == _10Mb ? "10Mb/s" : + "???" + ))))))))), (lp->fdx?" full duplex.":".")); + } + lp->c_media = lp->media; + } +} + +static void +de4x5_dbg_srom(struct de4x5_srom *p) +{ + int i; + + if (de4x5_debug & DEBUG_SROM) { + printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id)); + printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id)); + printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc)); + printk("SROM version: %02x\n", (u_char)(p->version)); + printk("# controllers: %02x\n", (u_char)(p->num_controllers)); + + printk("Hardware Address: %pM\n", p->ieee_addr); + printk("CRC checksum: %04x\n", (u_short)(p->chksum)); + for (i=0; i<64; i++) { + printk("%3d %04x\n", i<<1, (u_short)*((u_short *)p+i)); + } + } +} + +static void +de4x5_dbg_rx(struct sk_buff *skb, int len) +{ + int i, j; + + if (de4x5_debug & DEBUG_RX) { + printk("R: %pM <- %pM len/SAP:%02x%02x [%d]\n", + skb->data, &skb->data[6], + (u_char)skb->data[12], + (u_char)skb->data[13], + len); + for (j=0; len>0;j+=16, len-=16) { + printk(" %03x: ",j); + for (i=0; i<16 && idata[i+j]); + } + printk("\n"); + } + } +} + +/* +** Perform IOCTL call functions here. Some are privileged operations and the +** effective uid is checked in those cases. In the normal course of events +** this function is only used for my testing. +*/ +static int +de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct de4x5_private *lp = netdev_priv(dev); + struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_ifru; + u_long iobase = dev->base_addr; + int i, j, status = 0; + s32 omr; + union { + u8 addr[144]; + u16 sval[72]; + u32 lval[36]; + } tmp; + u_long flags = 0; + + switch(ioc->cmd) { + case DE4X5_GET_HWADDR: /* Get the hardware address */ + ioc->len = ETH_ALEN; + for (i=0; idev_addr[i]; + } + if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; + break; + + case DE4X5_SET_HWADDR: /* Set the hardware address */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (copy_from_user(tmp.addr, ioc->data, ETH_ALEN)) return -EFAULT; + if (netif_queue_stopped(dev)) + return -EBUSY; + netif_stop_queue(dev); + for (i=0; idev_addr[i] = tmp.addr[i]; + } + build_setup_frame(dev, PHYS_ADDR_ONLY); + /* Set up the descriptor and give ownership to the card */ + load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | + SETUP_FRAME_LEN, (struct sk_buff *)1); + lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ + netif_wake_queue(dev); /* Unlock the TX ring */ + break; + + case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + printk("%s: Boo!\n", dev->name); + break; + + case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + omr = inl(DE4X5_OMR); + omr |= OMR_PM; + outl(omr, DE4X5_OMR); + break; + + case DE4X5_GET_STATS: /* Get the driver statistics */ + { + struct pkt_stats statbuf; + ioc->len = sizeof(statbuf); + spin_lock_irqsave(&lp->lock, flags); + memcpy(&statbuf, &lp->pktStats, ioc->len); + spin_unlock_irqrestore(&lp->lock, flags); + if (copy_to_user(ioc->data, &statbuf, ioc->len)) + return -EFAULT; + break; + } + case DE4X5_CLR_STATS: /* Zero out the driver statistics */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + spin_lock_irqsave(&lp->lock, flags); + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + spin_unlock_irqrestore(&lp->lock, flags); + break; + + case DE4X5_GET_OMR: /* Get the OMR Register contents */ + tmp.addr[0] = inl(DE4X5_OMR); + if (copy_to_user(ioc->data, tmp.addr, 1)) return -EFAULT; + break; + + case DE4X5_SET_OMR: /* Set the OMR Register contents */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (copy_from_user(tmp.addr, ioc->data, 1)) return -EFAULT; + outl(tmp.addr[0], DE4X5_OMR); + break; + + case DE4X5_GET_REG: /* Get the DE4X5 Registers */ + j = 0; + tmp.lval[0] = inl(DE4X5_STS); j+=4; + tmp.lval[1] = inl(DE4X5_BMR); j+=4; + tmp.lval[2] = inl(DE4X5_IMR); j+=4; + tmp.lval[3] = inl(DE4X5_OMR); j+=4; + tmp.lval[4] = inl(DE4X5_SISR); j+=4; + tmp.lval[5] = inl(DE4X5_SICR); j+=4; + tmp.lval[6] = inl(DE4X5_STRR); j+=4; + tmp.lval[7] = inl(DE4X5_SIGR); j+=4; + ioc->len = j; + if (copy_to_user(ioc->data, tmp.lval, ioc->len)) + return -EFAULT; + break; + +#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ +/* + case DE4X5_DUMP: + j = 0; + tmp.addr[j++] = dev->irq; + for (i=0; idev_addr[i]; + } + tmp.addr[j++] = lp->rxRingSize; + tmp.lval[j>>2] = (long)lp->rx_ring; j+=4; + tmp.lval[j>>2] = (long)lp->tx_ring; j+=4; + + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; + } + } + tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; + } + } + tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; + + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; + } + } + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; + } + } + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; + + for (i=0;irxRingSize;i++){ + tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4; + } + for (i=0;itxRingSize;i++){ + tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4; + } + + tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_TPD); j+=4; + tmp.lval[j>>2] = inl(DE4X5_RPD); j+=4; + tmp.lval[j>>2] = inl(DE4X5_RRBA); j+=4; + tmp.lval[j>>2] = inl(DE4X5_TRBA); j+=4; + tmp.lval[j>>2] = inl(DE4X5_STS); j+=4; + tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4; + tmp.lval[j>>2] = lp->chipset; j+=4; + if (lp->chipset == DC21140) { + tmp.lval[j>>2] = gep_rd(dev); j+=4; + } else { + tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; + } + tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + tmp.lval[j>>2] = lp->active; j+=4; + tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ID0,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ID1,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + if (lp->phy[lp->active].id != BROADCOM_T4) { + tmp.lval[j>>2]=mii_rd(MII_ANA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ANLPA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } + tmp.lval[j>>2]=mii_rd(0x10,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + if (lp->phy[lp->active].id != BROADCOM_T4) { + tmp.lval[j>>2]=mii_rd(0x11,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(0x12,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } else { + tmp.lval[j>>2]=mii_rd(0x14,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } + } + + tmp.addr[j++] = lp->txRingSize; + tmp.addr[j++] = netif_queue_stopped(dev); + + ioc->len = j; + if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; + break; + +*/ + default: + return -EOPNOTSUPP; + } + + return status; +} + +static int __init de4x5_module_init (void) +{ + int err = 0; + +#ifdef CONFIG_PCI + err = pci_register_driver(&de4x5_pci_driver); +#endif +#ifdef CONFIG_EISA + err |= eisa_driver_register (&de4x5_eisa_driver); +#endif + + return err; +} + +static void __exit de4x5_module_exit (void) +{ +#ifdef CONFIG_PCI + pci_unregister_driver (&de4x5_pci_driver); +#endif +#ifdef CONFIG_EISA + eisa_driver_unregister (&de4x5_eisa_driver); +#endif +} + +module_init (de4x5_module_init); +module_exit (de4x5_module_exit); diff --git a/drivers/net/ethernet/dec/tulip/de4x5.h b/drivers/net/ethernet/dec/tulip/de4x5.h new file mode 100644 index 000000000000..9f2877438fb0 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/de4x5.h @@ -0,0 +1,1019 @@ +/* + 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. + + The author may be reached as davies@wanton.lkg.dec.com or Digital + Equipment Corporation, 550 King Street, Littleton MA 01460. + + ========================================================================= +*/ + +/* +** DC21040 CSR<1..15> Register Address Map +*/ +#define DE4X5_BMR iobase+(0x000 << lp->bus) /* Bus Mode Register */ +#define DE4X5_TPD iobase+(0x008 << lp->bus) /* Transmit Poll Demand Reg */ +#define DE4X5_RPD iobase+(0x010 << lp->bus) /* Receive Poll Demand Reg */ +#define DE4X5_RRBA iobase+(0x018 << lp->bus) /* RX Ring Base Address Reg */ +#define DE4X5_TRBA iobase+(0x020 << lp->bus) /* TX Ring Base Address Reg */ +#define DE4X5_STS iobase+(0x028 << lp->bus) /* Status Register */ +#define DE4X5_OMR iobase+(0x030 << lp->bus) /* Operation Mode Register */ +#define DE4X5_IMR iobase+(0x038 << lp->bus) /* Interrupt Mask Register */ +#define DE4X5_MFC iobase+(0x040 << lp->bus) /* Missed Frame Counter */ +#define DE4X5_APROM iobase+(0x048 << lp->bus) /* Ethernet Address PROM */ +#define DE4X5_BROM iobase+(0x048 << lp->bus) /* Boot ROM Register */ +#define DE4X5_SROM iobase+(0x048 << lp->bus) /* Serial ROM Register */ +#define DE4X5_MII iobase+(0x048 << lp->bus) /* MII Interface Register */ +#define DE4X5_DDR iobase+(0x050 << lp->bus) /* Data Diagnostic Register */ +#define DE4X5_FDR iobase+(0x058 << lp->bus) /* Full Duplex Register */ +#define DE4X5_GPT iobase+(0x058 << lp->bus) /* General Purpose Timer Reg.*/ +#define DE4X5_GEP iobase+(0x060 << lp->bus) /* General Purpose Register */ +#define DE4X5_SISR iobase+(0x060 << lp->bus) /* SIA Status Register */ +#define DE4X5_SICR iobase+(0x068 << lp->bus) /* SIA Connectivity Register */ +#define DE4X5_STRR iobase+(0x070 << lp->bus) /* SIA TX/RX Register */ +#define DE4X5_SIGR iobase+(0x078 << lp->bus) /* SIA General Register */ + +/* +** EISA Register Address Map +*/ +#define EISA_ID iobase+0x0c80 /* EISA ID Registers */ +#define EISA_ID0 iobase+0x0c80 /* EISA ID Register 0 */ +#define EISA_ID1 iobase+0x0c81 /* EISA ID Register 1 */ +#define EISA_ID2 iobase+0x0c82 /* EISA ID Register 2 */ +#define EISA_ID3 iobase+0x0c83 /* EISA ID Register 3 */ +#define EISA_CR iobase+0x0c84 /* EISA Control Register */ +#define EISA_REG0 iobase+0x0c88 /* EISA Configuration Register 0 */ +#define EISA_REG1 iobase+0x0c89 /* EISA Configuration Register 1 */ +#define EISA_REG2 iobase+0x0c8a /* EISA Configuration Register 2 */ +#define EISA_REG3 iobase+0x0c8f /* EISA Configuration Register 3 */ +#define EISA_APROM iobase+0x0c90 /* Ethernet Address PROM */ + +/* +** PCI/EISA Configuration Registers Address Map +*/ +#define PCI_CFID iobase+0x0008 /* PCI Configuration ID Register */ +#define PCI_CFCS iobase+0x000c /* PCI Command/Status Register */ +#define PCI_CFRV iobase+0x0018 /* PCI Revision Register */ +#define PCI_CFLT iobase+0x001c /* PCI Latency Timer Register */ +#define PCI_CBIO iobase+0x0028 /* PCI Base I/O Register */ +#define PCI_CBMA iobase+0x002c /* PCI Base Memory Address Register */ +#define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */ +#define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */ +#define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */ +#define PCI_CFDD iobase+0x0041 /* PCI Driver Dependent Area Register */ +#define PCI_CFPM iobase+0x0043 /* PCI Power Management Area Register */ + +/* +** EISA Configuration Register 0 bit definitions +*/ +#define ER0_BSW 0x80 /* EISA Bus Slave Width, 1: 32 bits */ +#define ER0_BMW 0x40 /* EISA Bus Master Width, 1: 32 bits */ +#define ER0_EPT 0x20 /* EISA PREEMPT Time, 0: 23 BCLKs */ +#define ER0_ISTS 0x10 /* Interrupt Status (X) */ +#define ER0_LI 0x08 /* Latch Interrupts */ +#define ER0_INTL 0x06 /* INTerrupt Level */ +#define ER0_INTT 0x01 /* INTerrupt Type, 0: Level, 1: Edge */ + +/* +** EISA Configuration Register 1 bit definitions +*/ +#define ER1_IAM 0xe0 /* ISA Address Mode */ +#define ER1_IAE 0x10 /* ISA Addressing Enable */ +#define ER1_UPIN 0x0f /* User Pins */ + +/* +** EISA Configuration Register 2 bit definitions +*/ +#define ER2_BRS 0xc0 /* Boot ROM Size */ +#define ER2_BRA 0x3c /* Boot ROM Address <16:13> */ + +/* +** EISA Configuration Register 3 bit definitions +*/ +#define ER3_BWE 0x40 /* Burst Write Enable */ +#define ER3_BRE 0x04 /* Burst Read Enable */ +#define ER3_LSR 0x02 /* Local Software Reset */ + +/* +** PCI Configuration ID Register (PCI_CFID). The Device IDs are left +** shifted 8 bits to allow detection of DC21142 and DC21143 variants with +** the configuration revision register step number. +*/ +#define CFID_DID 0xff00 /* Device ID */ +#define CFID_VID 0x00ff /* Vendor ID */ +#define DC21040_DID 0x0200 /* Unique Device ID # */ +#define DC21040_VID 0x1011 /* DC21040 Manufacturer */ +#define DC21041_DID 0x1400 /* Unique Device ID # */ +#define DC21041_VID 0x1011 /* DC21041 Manufacturer */ +#define DC21140_DID 0x0900 /* Unique Device ID # */ +#define DC21140_VID 0x1011 /* DC21140 Manufacturer */ +#define DC2114x_DID 0x1900 /* Unique Device ID # */ +#define DC2114x_VID 0x1011 /* DC2114[23] Manufacturer */ + +/* +** Chipset defines +*/ +#define DC21040 DC21040_DID +#define DC21041 DC21041_DID +#define DC21140 DC21140_DID +#define DC2114x DC2114x_DID +#define DC21142 (DC2114x_DID | 0x0010) +#define DC21143 (DC2114x_DID | 0x0030) +#define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */ + +#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) +#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) +#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID)) +#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID)) +#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142)) +#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143)) + +/* +** PCI Configuration Command/Status Register (PCI_CFCS) +*/ +#define CFCS_DPE 0x80000000 /* Detected Parity Error (S) */ +#define CFCS_SSE 0x40000000 /* Signal System Error (S) */ +#define CFCS_RMA 0x20000000 /* Receive Master Abort (S) */ +#define CFCS_RTA 0x10000000 /* Receive Target Abort (S) */ +#define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */ +#define CFCS_DPR 0x01000000 /* Data Parity Report (S) */ +#define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */ +#define CFCS_SEE 0x00000100 /* System Error Enable (C) */ +#define CFCS_PER 0x00000040 /* Parity Error Response (C) */ +#define CFCS_MO 0x00000004 /* Master Operation (C) */ +#define CFCS_MSA 0x00000002 /* Memory Space Access (C) */ +#define CFCS_IOSA 0x00000001 /* I/O Space Access (C) */ + +/* +** PCI Configuration Revision Register (PCI_CFRV) +*/ +#define CFRV_BC 0xff000000 /* Base Class */ +#define CFRV_SC 0x00ff0000 /* Subclass */ +#define CFRV_RN 0x000000f0 /* Revision Number */ +#define CFRV_SN 0x0000000f /* Step Number */ +#define BASE_CLASS 0x02000000 /* Indicates Network Controller */ +#define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */ +#define STEP_NUMBER 0x00000020 /* Increments for future chips */ +#define REV_NUMBER 0x00000003 /* 0x00, 0x01, 0x02, 0x03: Rev in Step */ +#define CFRV_MASK 0xffff0000 /* Register mask */ + +/* +** PCI Configuration Latency Timer Register (PCI_CFLT) +*/ +#define CFLT_BC 0x0000ff00 /* Latency Timer bits */ + +/* +** PCI Configuration Base I/O Address Register (PCI_CBIO) +*/ +#define CBIO_MASK -128 /* Base I/O Address Mask */ +#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */ + +/* +** PCI Configuration Card Information Structure Register (PCI_CCIS) +*/ +#define CCIS_ROMI 0xf0000000 /* ROM Image */ +#define CCIS_ASO 0x0ffffff8 /* Address Space Offset */ +#define CCIS_ASI 0x00000007 /* Address Space Indicator */ + +/* +** PCI Configuration Subsystem ID Register (PCI_SSID) +*/ +#define SSID_SSID 0xffff0000 /* Subsystem ID */ +#define SSID_SVID 0x0000ffff /* Subsystem Vendor ID */ + +/* +** PCI Configuration Expansion ROM Base Address Register (PCI_CBER) +*/ +#define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */ +#define CBER_ROME 0x00000001 /* ROM Enable */ + +/* +** PCI Configuration Interrupt Register (PCI_CFIT) +*/ +#define CFIT_MXLT 0xff000000 /* MAX_LAT Value (0.25us periods) */ +#define CFIT_MNGT 0x00ff0000 /* MIN_GNT Value (0.25us periods) */ +#define CFIT_IRQP 0x0000ff00 /* Interrupt Pin */ +#define CFIT_IRQL 0x000000ff /* Interrupt Line */ + +/* +** PCI Configuration Power Management Area Register (PCI_CFPM) +*/ +#define SLEEP 0x80 /* Power Saving Sleep Mode */ +#define SNOOZE 0x40 /* Power Saving Snooze Mode */ +#define WAKEUP 0x00 /* Power Saving Wakeup */ + +#define PCI_CFDA_DSU 0x41 /* 8 bit Configuration Space Address */ +#define PCI_CFDA_PSM 0x43 /* 8 bit Configuration Space Address */ + +/* +** DC21040 Bus Mode Register (DE4X5_BMR) +*/ +#define BMR_RML 0x00200000 /* [Memory] Read Multiple */ +#define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */ +#define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */ +#define BMR_DAS 0x00010000 /* Diagnostic Address Space */ +#define BMR_CAL 0x0000c000 /* Cache Alignment */ +#define BMR_PBL 0x00003f00 /* Programmable Burst Length */ +#define BMR_BLE 0x00000080 /* Big/Little Endian */ +#define BMR_DSL 0x0000007c /* Descriptor Skip Length */ +#define BMR_BAR 0x00000002 /* Bus ARbitration */ +#define BMR_SWR 0x00000001 /* Software Reset */ + + /* Timings here are for 10BASE-T/AUI only*/ +#define TAP_NOPOLL 0x00000000 /* No automatic polling */ +#define TAP_200US 0x00020000 /* TX automatic polling every 200us */ +#define TAP_800US 0x00040000 /* TX automatic polling every 800us */ +#define TAP_1_6MS 0x00060000 /* TX automatic polling every 1.6ms */ +#define TAP_12_8US 0x00080000 /* TX automatic polling every 12.8us */ +#define TAP_25_6US 0x000a0000 /* TX automatic polling every 25.6us */ +#define TAP_51_2US 0x000c0000 /* TX automatic polling every 51.2us */ +#define TAP_102_4US 0x000e0000 /* TX automatic polling every 102.4us */ + +#define CAL_NOUSE 0x00000000 /* Not used */ +#define CAL_8LONG 0x00004000 /* 8-longword alignment */ +#define CAL_16LONG 0x00008000 /* 16-longword alignment */ +#define CAL_32LONG 0x0000c000 /* 32-longword alignment */ + +#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */ +#define PBL_1 0x00000100 /* 1 longword DMA burst length */ +#define PBL_2 0x00000200 /* 2 longwords DMA burst length */ +#define PBL_4 0x00000400 /* 4 longwords DMA burst length */ +#define PBL_8 0x00000800 /* 8 longwords DMA burst length */ +#define PBL_16 0x00001000 /* 16 longwords DMA burst length */ +#define PBL_32 0x00002000 /* 32 longwords DMA burst length */ + +#define DSL_0 0x00000000 /* 0 longword / descriptor */ +#define DSL_1 0x00000004 /* 1 longword / descriptor */ +#define DSL_2 0x00000008 /* 2 longwords / descriptor */ +#define DSL_4 0x00000010 /* 4 longwords / descriptor */ +#define DSL_8 0x00000020 /* 8 longwords / descriptor */ +#define DSL_16 0x00000040 /* 16 longwords / descriptor */ +#define DSL_32 0x00000080 /* 32 longwords / descriptor */ + +/* +** DC21040 Transmit Poll Demand Register (DE4X5_TPD) +*/ +#define TPD 0x00000001 /* Transmit Poll Demand */ + +/* +** DC21040 Receive Poll Demand Register (DE4X5_RPD) +*/ +#define RPD 0x00000001 /* Receive Poll Demand */ + +/* +** DC21040 Receive Ring Base Address Register (DE4X5_RRBA) +*/ +#define RRBA 0xfffffffc /* RX Descriptor List Start Address */ + +/* +** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA) +*/ +#define TRBA 0xfffffffc /* TX Descriptor List Start Address */ + +/* +** Status Register (DE4X5_STS) +*/ +#define STS_GPI 0x04000000 /* General Purpose Port Interrupt */ +#define STS_BE 0x03800000 /* Bus Error Bits */ +#define STS_TS 0x00700000 /* Transmit Process State */ +#define STS_RS 0x000e0000 /* Receive Process State */ +#define STS_NIS 0x00010000 /* Normal Interrupt Summary */ +#define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */ +#define STS_ER 0x00004000 /* Early Receive */ +#define STS_FBE 0x00002000 /* Fatal Bus Error */ +#define STS_SE 0x00002000 /* System Error */ +#define STS_LNF 0x00001000 /* Link Fail */ +#define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */ +#define STS_TM 0x00000800 /* Timer Expired (DC21041) */ +#define STS_ETI 0x00000400 /* Early Transmit Interrupt */ +#define STS_AT 0x00000400 /* AUI/TP Pin */ +#define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */ +#define STS_RPS 0x00000100 /* Receive Process Stopped */ +#define STS_RU 0x00000080 /* Receive Buffer Unavailable */ +#define STS_RI 0x00000040 /* Receive Interrupt */ +#define STS_UNF 0x00000020 /* Transmit Underflow */ +#define STS_LNP 0x00000010 /* Link Pass */ +#define STS_ANC 0x00000010 /* Autonegotiation Complete */ +#define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */ +#define STS_TU 0x00000004 /* Transmit Buffer Unavailable */ +#define STS_TPS 0x00000002 /* Transmit Process Stopped */ +#define STS_TI 0x00000001 /* Transmit Interrupt */ + +#define EB_PAR 0x00000000 /* Parity Error */ +#define EB_MA 0x00800000 /* Master Abort */ +#define EB_TA 0x01000000 /* Target Abort */ +#define EB_RES0 0x01800000 /* Reserved */ +#define EB_RES1 0x02000000 /* Reserved */ + +#define TS_STOP 0x00000000 /* Stopped */ +#define TS_FTD 0x00100000 /* Fetch Transmit Descriptor */ +#define TS_WEOT 0x00200000 /* Wait for End Of Transmission */ +#define TS_QDAT 0x00300000 /* Queue skb data into TX FIFO */ +#define TS_RES 0x00400000 /* Reserved */ +#define TS_SPKT 0x00500000 /* Setup Packet */ +#define TS_SUSP 0x00600000 /* Suspended */ +#define TS_CLTD 0x00700000 /* Close Transmit Descriptor */ + +#define RS_STOP 0x00000000 /* Stopped */ +#define RS_FRD 0x00020000 /* Fetch Receive Descriptor */ +#define RS_CEOR 0x00040000 /* Check for End of Receive Packet */ +#define RS_WFRP 0x00060000 /* Wait for Receive Packet */ +#define RS_SUSP 0x00080000 /* Suspended */ +#define RS_CLRD 0x000a0000 /* Close Receive Descriptor */ +#define RS_FLUSH 0x000c0000 /* Flush RX FIFO */ +#define RS_QRFS 0x000e0000 /* Queue RX FIFO into RX Skb */ + +#define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */ + +/* +** Operation Mode Register (DE4X5_OMR) +*/ +#define OMR_SC 0x80000000 /* Special Capture Effect Enable */ +#define OMR_RA 0x40000000 /* Receive All */ +#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ +#define OMR_SCR 0x01000000 /* Scrambler Mode */ +#define OMR_PCS 0x00800000 /* PCS Function */ +#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */ +#define OMR_SF 0x00200000 /* Store and Forward */ +#define OMR_HBD 0x00080000 /* HeartBeat Disable */ +#define OMR_PS 0x00040000 /* Port Select */ +#define OMR_CA 0x00020000 /* Capture Effect Enable */ +#define OMR_BP 0x00010000 /* Back Pressure */ +#define OMR_TR 0x0000c000 /* Threshold Control Bits */ +#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ +#define OMR_FC 0x00001000 /* Force Collision Mode */ +#define OMR_OM 0x00000c00 /* Operating Mode */ +#define OMR_FDX 0x00000200 /* Full Duplex Mode */ +#define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */ +#define OMR_PM 0x00000080 /* Pass All Multicast */ +#define OMR_PR 0x00000040 /* Promiscuous Mode */ +#define OMR_SB 0x00000020 /* Start/Stop Backoff Counter */ +#define OMR_IF 0x00000010 /* Inverse Filtering */ +#define OMR_PB 0x00000008 /* Pass Bad Frames */ +#define OMR_HO 0x00000004 /* Hash Only Filtering Mode */ +#define OMR_SR 0x00000002 /* Start/Stop Receive */ +#define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */ + +#define TR_72 0x00000000 /* Threshold set to 72 (128) bytes */ +#define TR_96 0x00004000 /* Threshold set to 96 (256) bytes */ +#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ +#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ + +#define OMR_DEF (OMR_SDP) +#define OMR_SIA (OMR_SDP | OMR_TTM) +#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS) +#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS) +#define OMR_MII_100 (OMR_SDP | OMR_HBD | OMR_PS) + +/* +** DC21040 Interrupt Mask Register (DE4X5_IMR) +*/ +#define IMR_GPM 0x04000000 /* General Purpose Port Mask */ +#define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */ +#define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */ +#define IMR_ERM 0x00004000 /* Early Receive Mask */ +#define IMR_FBM 0x00002000 /* Fatal Bus Error Mask */ +#define IMR_SEM 0x00002000 /* System Error Mask */ +#define IMR_LFM 0x00001000 /* Link Fail Mask */ +#define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */ +#define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */ +#define IMR_ETM 0x00000400 /* Early Transmit Interrupt Mask */ +#define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */ +#define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */ +#define IMR_RSM 0x00000100 /* Receive Stopped Mask */ +#define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */ +#define IMR_RIM 0x00000040 /* Receive Interrupt Mask */ +#define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */ +#define IMR_ANM 0x00000010 /* Autonegotiation Complete Mask */ +#define IMR_LPM 0x00000010 /* Link Pass */ +#define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */ +#define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */ +#define IMR_TSM 0x00000002 /* Transmission Stopped Mask */ +#define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */ + +/* +** Missed Frames and FIFO Overflow Counters (DE4X5_MFC) +*/ +#define MFC_FOCO 0x10000000 /* FIFO Overflow Counter Overflow Bit */ +#define MFC_FOC 0x0ffe0000 /* FIFO Overflow Counter Bits */ +#define MFC_OVFL 0x00010000 /* Missed Frames Counter Overflow Bit */ +#define MFC_CNTR 0x0000ffff /* Missed Frames Counter Bits */ +#define MFC_FOCM 0x1ffe0000 /* FIFO Overflow Counter Mask */ + +/* +** DC21040 Ethernet Address PROM (DE4X5_APROM) +*/ +#define APROM_DN 0x80000000 /* Data Not Valid */ +#define APROM_DT 0x000000ff /* Address Byte */ + +/* +** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM) +*/ +#define BROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ +#define BROM_RD 0x00004000 /* Read from Boot ROM */ +#define BROM_WR 0x00002000 /* Write to Boot ROM */ +#define BROM_BR 0x00001000 /* Select Boot ROM when set */ +#define BROM_SR 0x00000800 /* Select Serial ROM when set */ +#define BROM_REG 0x00000400 /* External Register Select */ +#define BROM_DT 0x000000ff /* Data Byte */ + +/* +** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM, DE4X5_MII) +*/ +#define MII_MDI 0x00080000 /* MII Management Data In */ +#define MII_MDO 0x00060000 /* MII Management Mode/Data Out */ +#define MII_MRD 0x00040000 /* MII Management Define Read Mode */ +#define MII_MWR 0x00000000 /* MII Management Define Write Mode */ +#define MII_MDT 0x00020000 /* MII Management Data Out */ +#define MII_MDC 0x00010000 /* MII Management Clock */ +#define MII_RD 0x00004000 /* Read from MII */ +#define MII_WR 0x00002000 /* Write to MII */ +#define MII_SEL 0x00000800 /* Select MII when RESET */ + +#define SROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ +#define SROM_RD 0x00004000 /* Read from Boot ROM */ +#define SROM_WR 0x00002000 /* Write to Boot ROM */ +#define SROM_BR 0x00001000 /* Select Boot ROM when set */ +#define SROM_SR 0x00000800 /* Select Serial ROM when set */ +#define SROM_REG 0x00000400 /* External Register Select */ +#define SROM_DT 0x000000ff /* Data Byte */ + +#define DT_OUT 0x00000008 /* Serial Data Out */ +#define DT_IN 0x00000004 /* Serial Data In */ +#define DT_CLK 0x00000002 /* Serial ROM Clock */ +#define DT_CS 0x00000001 /* Serial ROM Chip Select */ + +#define MII_PREAMBLE 0xffffffff /* MII Management Preamble */ +#define MII_TEST 0xaaaaaaaa /* MII Test Signal */ +#define MII_STRD 0x06 /* Start of Frame+Op Code: use low nibble */ +#define MII_STWR 0x0a /* Start of Frame+Op Code: use low nibble */ + +#define MII_CR 0x00 /* MII Management Control Register */ +#define MII_SR 0x01 /* MII Management Status Register */ +#define MII_ID0 0x02 /* PHY Identifier Register 0 */ +#define MII_ID1 0x03 /* PHY Identifier Register 1 */ +#define MII_ANA 0x04 /* Auto Negotiation Advertisement */ +#define MII_ANLPA 0x05 /* Auto Negotiation Link Partner Ability */ +#define MII_ANE 0x06 /* Auto Negotiation Expansion */ +#define MII_ANP 0x07 /* Auto Negotiation Next Page TX */ + +#define DE4X5_MAX_MII 32 /* Maximum address of MII PHY devices */ + +/* +** MII Management Control Register +*/ +#define MII_CR_RST 0x8000 /* RESET the PHY chip */ +#define MII_CR_LPBK 0x4000 /* Loopback enable */ +#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ +#define MII_CR_10 0x0000 /* Set 10Mb/s */ +#define MII_CR_100 0x2000 /* Set 100Mb/s */ +#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ +#define MII_CR_PD 0x0800 /* Power Down */ +#define MII_CR_ISOL 0x0400 /* Isolate Mode */ +#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ +#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ +#define MII_CR_CTE 0x0080 /* Collision Test Enable */ + +/* +** MII Management Status Register +*/ +#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ +#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ +#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ +#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ +#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ +#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ +#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ +#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ +#define MII_SR_LKS 0x0004 /* Link Status */ +#define MII_SR_JABD 0x0002 /* Jabber Detect */ +#define MII_SR_XC 0x0001 /* Extended Capabilities */ + +/* +** MII Management Auto Negotiation Advertisement Register +*/ +#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* +** MII Management Auto Negotiation Remote End Register +*/ +#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ +#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ +#define MII_ANLPA_RF 0x2000 /* Remote Fault */ +#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* +** SROM Media Definitions (ABG SROM Section) +*/ +#define MEDIA_NWAY 0x0080 /* Nway (Auto Negotiation) on PHY */ +#define MEDIA_MII 0x0040 /* MII Present on the adapter */ +#define MEDIA_FIBRE 0x0008 /* Fibre Media present */ +#define MEDIA_AUI 0x0004 /* AUI Media present */ +#define MEDIA_TP 0x0002 /* TP Media present */ +#define MEDIA_BNC 0x0001 /* BNC Media present */ + +/* +** SROM Definitions (Digital Semiconductor Format) +*/ +#define SROM_SSVID 0x0000 /* Sub-system Vendor ID offset */ +#define SROM_SSID 0x0002 /* Sub-system ID offset */ +#define SROM_CISPL 0x0004 /* CardBus CIS Pointer low offset */ +#define SROM_CISPH 0x0006 /* CardBus CIS Pointer high offset */ +#define SROM_IDCRC 0x0010 /* ID Block CRC offset*/ +#define SROM_RSVD2 0x0011 /* ID Reserved 2 offset */ +#define SROM_SFV 0x0012 /* SROM Format Version offset */ +#define SROM_CCNT 0x0013 /* Controller Count offset */ +#define SROM_HWADD 0x0014 /* Hardware Address offset */ +#define SROM_MRSVD 0x007c /* Manufacturer Reserved offset*/ +#define SROM_CRC 0x007e /* SROM CRC offset */ + +/* +** SROM Media Connection Definitions +*/ +#define SROM_10BT 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BTN 0x0100 /* 10BASE-T with Nway */ +#define SROM_10BTF 0x0204 /* 10BASE-T full duplex */ +#define SROM_10BTNLP 0x0400 /* 10BASE-T without Link Pass test */ +#define SROM_10B2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10B5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BTH 0x0003 /* 100BASE-T half duplex */ +#define SROM_100BTF 0x0205 /* 100BASE-T full duplex */ +#define SROM_100BT4 0x0006 /* 100BASE-T4 */ +#define SROM_100BFX 0x0007 /* 100BASE-FX half duplex (Fiber) */ +#define SROM_M10BT 0x0009 /* MII 10BASE-T half duplex */ +#define SROM_M10BTF 0x020a /* MII 10BASE-T full duplex */ +#define SROM_M100BT 0x000d /* MII 100BASE-T half duplex */ +#define SROM_M100BTF 0x020e /* MII 100BASE-T full duplex */ +#define SROM_M100BT4 0x000f /* MII 100BASE-T4 */ +#define SROM_M100BF 0x0010 /* MII 100BASE-FX half duplex */ +#define SROM_M100BFF 0x0211 /* MII 100BASE-FX full duplex */ +#define SROM_PDA 0x0800 /* Powerup & Dynamic Autosense */ +#define SROM_PAO 0x8800 /* Powerup Autosense Only */ +#define SROM_NSMI 0xffff /* No Selected Media Information */ + +/* +** SROM Media Definitions +*/ +#define SROM_10BASET 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BASE2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10BASE5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BASET 0x0003 /* 100BASE-T half duplex */ +#define SROM_10BASETF 0x0004 /* 10BASE-T full duplex */ +#define SROM_100BASETF 0x0005 /* 100BASE-T full duplex */ +#define SROM_100BASET4 0x0006 /* 100BASE-T4 */ +#define SROM_100BASEF 0x0007 /* 100BASE-FX half duplex */ +#define SROM_100BASEFF 0x0008 /* 100BASE-FX full duplex */ + +#define BLOCK_LEN 0x7f /* Extended blocks length mask */ +#define EXT_FIELD 0x40 /* Extended blocks extension field bit */ +#define MEDIA_CODE 0x3f /* Extended blocks media code mask */ + +/* +** SROM Compact Format Block Masks +*/ +#define COMPACT_FI 0x80 /* Format Indicator */ +#define COMPACT_LEN 0x04 /* Length */ +#define COMPACT_MC 0x3f /* Media Code */ + +/* +** SROM Extended Format Block Type 0 Masks +*/ +#define BLOCK0_FI 0x80 /* Format Indicator */ +#define BLOCK0_MCS 0x80 /* Media Code byte Sign */ +#define BLOCK0_MC 0x3f /* Media Code */ + +/* +** DC21040 Full Duplex Register (DE4X5_FDR) +*/ +#define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */ + +/* +** DC21041 General Purpose Timer Register (DE4X5_GPT) +*/ +#define GPT_CON 0x00010000 /* One shot: 0, Continuous: 1 */ +#define GPT_VAL 0x0000ffff /* Timer Value */ + +/* +** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits) +*/ +/* Valid ONLY for DE500 hardware */ +#define GEP_LNP 0x00000080 /* Link Pass (input) */ +#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ +#define GEP_SDET 0x00000020 /* Signal Detect (input) */ +#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ +#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ +#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ +#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ +#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ +#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ +#define GEP_CTRL 0x00000100 /* GEP control bit */ + +/* +** SIA Register Defaults +*/ +#define CSR13 0x00000001 +#define CSR14 0x0003ff7f /* Autonegotiation disabled */ +#define CSR15 0x00000008 + +/* +** SIA Status Register (DE4X5_SISR) +*/ +#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ +#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ +#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ +#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ +#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ +#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ +#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ +#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ +#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ +#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ +#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ +#define SISR_DAO 0x00000080 /* PLL All One */ +#define SISR_DAZ 0x00000040 /* PLL All Zero */ +#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ +#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ +#define SISR_APS 0x00000008 /* Auto Polarity State */ +#define SISR_LKF 0x00000004 /* Link Fail Status */ +#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */ +#define SISR_NCR 0x00000002 /* Network Connection Error */ +#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */ +#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ +#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ + +#define ANS_NDIS 0x00000000 /* Nway disable */ +#define ANS_TDIS 0x00001000 /* Transmit Disable */ +#define ANS_ADET 0x00002000 /* Ability Detect */ +#define ANS_ACK 0x00003000 /* Acknowledge */ +#define ANS_CACK 0x00004000 /* Complete Acknowledge */ +#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ +#define ANS_LCHK 0x00006000 /* Link Check */ + +#define SISR_RST 0x00000301 /* CSR12 reset */ +#define SISR_ANR 0x00001301 /* Autonegotiation restart */ + +/* +** SIA Connectivity Register (DE4X5_SICR) +*/ +#define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */ +#define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */ +#define SICR_OE24 0x00004000 /* Output Enable 2 4 */ +#define SICR_OE13 0x00002000 /* Output Enable 1 3 */ +#define SICR_IE 0x00001000 /* Input Enable */ +#define SICR_EXT 0x00000000 /* SIA MUX Select External SIA Mode */ +#define SICR_D_SIA 0x00000400 /* SIA MUX Select Diagnostics - SIA Sigs */ +#define SICR_DPLL 0x00000800 /* SIA MUX Select Diagnostics - DPLL Sigs*/ +#define SICR_APLL 0x00000a00 /* SIA MUX Select Diagnostics - DPLL Sigs*/ +#define SICR_D_RxM 0x00000c00 /* SIA MUX Select Diagnostics - RxM Sigs */ +#define SICR_M_RxM 0x00000d00 /* SIA MUX Select Diagnostics - RxM Sigs */ +#define SICR_LNKT 0x00000e00 /* SIA MUX Select Diagnostics - Link Test*/ +#define SICR_SEL 0x00000f00 /* SIA MUX Select AUI or TP with LEDs */ +#define SICR_ASE 0x00000080 /* APLL Start Enable*/ +#define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */ +#define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */ +#define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */ +#define SICR_AUI 0x00000008 /* 10Base-T (0) or AUI (1) */ +#define SICR_CAC 0x00000004 /* CSR Auto Configuration */ +#define SICR_PS 0x00000002 /* Pin AUI/TP Selection */ +#define SICR_SRL 0x00000001 /* SIA Reset */ +#define SIA_RESET 0x00000000 /* SIA Reset Value */ + +/* +** SIA Transmit and Receive Register (DE4X5_STRR) +*/ +#define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */ +#define STRR_SPP 0x00004000 /* Set Polarity Plus */ +#define STRR_APE 0x00002000 /* Auto Polarity Enable */ +#define STRR_LTE 0x00001000 /* Link Test Enable */ +#define STRR_SQE 0x00000800 /* Signal Quality Enable */ +#define STRR_CLD 0x00000400 /* Collision Detect Enable */ +#define STRR_CSQ 0x00000200 /* Collision Squelch Enable */ +#define STRR_RSQ 0x00000100 /* Receive Squelch Enable */ +#define STRR_ANE 0x00000080 /* Auto Negotiate Enable */ +#define STRR_HDE 0x00000040 /* Half Duplex Enable */ +#define STRR_CPEN 0x00000030 /* Compensation Enable */ +#define STRR_LSE 0x00000008 /* Link Pulse Send Enable */ +#define STRR_DREN 0x00000004 /* Driver Enable */ +#define STRR_LBK 0x00000002 /* Loopback Enable */ +#define STRR_ECEN 0x00000001 /* Encoder Enable */ +#define STRR_RESET 0xffffffff /* Reset value for STRR */ + +/* +** SIA General Register (DE4X5_SIGR) +*/ +#define SIGR_RMI 0x40000000 /* Receive Match Interrupt */ +#define SIGR_GI1 0x20000000 /* General Port Interrupt 1 */ +#define SIGR_GI0 0x10000000 /* General Port Interrupt 0 */ +#define SIGR_CWE 0x08000000 /* Control Write Enable */ +#define SIGR_RME 0x04000000 /* Receive Match Enable */ +#define SIGR_GEI1 0x02000000 /* GEP Interrupt Enable on Port 1 */ +#define SIGR_GEI0 0x01000000 /* GEP Interrupt Enable on Port 0 */ +#define SIGR_LGS3 0x00800000 /* LED/GEP3 Select */ +#define SIGR_LGS2 0x00400000 /* LED/GEP2 Select */ +#define SIGR_LGS1 0x00200000 /* LED/GEP1 Select */ +#define SIGR_LGS0 0x00100000 /* LED/GEP0 Select */ +#define SIGR_MD 0x000f0000 /* General Purpose Mode and Data */ +#define SIGR_LV2 0x00008000 /* General Purpose LED2 value */ +#define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */ +#define SIGR_FRL 0x00002000 /* Force Receiver Low */ +#define SIGR_DPST 0x00001000 /* PLL Self Test Start */ +#define SIGR_LSD 0x00000800 /* LED Stretch Disable */ +#define SIGR_FLF 0x00000400 /* Force Link Fail */ +#define SIGR_FUSQ 0x00000200 /* Force Unsquelch */ +#define SIGR_TSCK 0x00000100 /* Test Clock */ +#define SIGR_LV1 0x00000080 /* General Purpose LED1 value */ +#define SIGR_LE1 0x00000040 /* General Purpose LED1 enable */ +#define SIGR_RWR 0x00000020 /* Receive Watchdog Release */ +#define SIGR_RWD 0x00000010 /* Receive Watchdog Disable */ +#define SIGR_ABM 0x00000008 /* BNC: 0, AUI:1 */ +#define SIGR_JCK 0x00000004 /* Jabber Clock */ +#define SIGR_HUJ 0x00000002 /* Host Unjab */ +#define SIGR_JBD 0x00000001 /* Jabber Disable */ +#define SIGR_RESET 0xffff0000 /* Reset value for SIGR */ + +/* +** Receive Descriptor Bit Summary +*/ +#define R_OWN 0x80000000 /* Own Bit */ +#define RD_FF 0x40000000 /* Filtering Fail */ +#define RD_FL 0x3fff0000 /* Frame Length */ +#define RD_ES 0x00008000 /* Error Summary */ +#define RD_LE 0x00004000 /* Length Error */ +#define RD_DT 0x00003000 /* Data Type */ +#define RD_RF 0x00000800 /* Runt Frame */ +#define RD_MF 0x00000400 /* Multicast Frame */ +#define RD_FS 0x00000200 /* First Descriptor */ +#define RD_LS 0x00000100 /* Last Descriptor */ +#define RD_TL 0x00000080 /* Frame Too Long */ +#define RD_CS 0x00000040 /* Collision Seen */ +#define RD_FT 0x00000020 /* Frame Type */ +#define RD_RJ 0x00000010 /* Receive Watchdog */ +#define RD_RE 0x00000008 /* Report on MII Error */ +#define RD_DB 0x00000004 /* Dribbling Bit */ +#define RD_CE 0x00000002 /* CRC Error */ +#define RD_OF 0x00000001 /* Overflow */ + +#define RD_RER 0x02000000 /* Receive End Of Ring */ +#define RD_RCH 0x01000000 /* Second Address Chained */ +#define RD_RBS2 0x003ff800 /* Buffer 2 Size */ +#define RD_RBS1 0x000007ff /* Buffer 1 Size */ + +/* +** Transmit Descriptor Bit Summary +*/ +#define T_OWN 0x80000000 /* Own Bit */ +#define TD_ES 0x00008000 /* Error Summary */ +#define TD_TO 0x00004000 /* Transmit Jabber Time-Out */ +#define TD_LO 0x00000800 /* Loss Of Carrier */ +#define TD_NC 0x00000400 /* No Carrier */ +#define TD_LC 0x00000200 /* Late Collision */ +#define TD_EC 0x00000100 /* Excessive Collisions */ +#define TD_HF 0x00000080 /* Heartbeat Fail */ +#define TD_CC 0x00000078 /* Collision Counter */ +#define TD_LF 0x00000004 /* Link Fail */ +#define TD_UF 0x00000002 /* Underflow Error */ +#define TD_DE 0x00000001 /* Deferred */ + +#define TD_IC 0x80000000 /* Interrupt On Completion */ +#define TD_LS 0x40000000 /* Last Segment */ +#define TD_FS 0x20000000 /* First Segment */ +#define TD_FT1 0x10000000 /* Filtering Type */ +#define TD_SET 0x08000000 /* Setup Packet */ +#define TD_AC 0x04000000 /* Add CRC Disable */ +#define TD_TER 0x02000000 /* Transmit End Of Ring */ +#define TD_TCH 0x01000000 /* Second Address Chained */ +#define TD_DPD 0x00800000 /* Disabled Padding */ +#define TD_FT0 0x00400000 /* Filtering Type */ +#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ +#define TD_TBS1 0x000007ff /* Buffer 1 Size */ + +#define PERFECT_F 0x00000000 +#define HASH_F TD_FT0 +#define INVERSE_F TD_FT1 +#define HASH_O_F (TD_FT1 | TD_F0) + +/* +** Media / mode state machine definitions +** User selectable: +*/ +#define TP 0x0040 /* 10Base-T (now equiv to _10Mb) */ +#define TP_NW 0x0002 /* 10Base-T with Nway */ +#define BNC 0x0004 /* Thinwire */ +#define AUI 0x0008 /* Thickwire */ +#define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */ +#define _10Mb 0x0040 /* 10Mb/s Ethernet */ +#define _100Mb 0x0080 /* 100Mb/s Ethernet */ +#define AUTO 0x4000 /* Auto sense the media or speed */ + +/* +** Internal states +*/ +#define NC 0x0000 /* No Connection */ +#define ANS 0x0020 /* Intermediate AutoNegotiation State */ +#define SPD_DET 0x0100 /* Parallel speed detection */ +#define INIT 0x0200 /* Initial state */ +#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ +#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ +#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ +#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ +#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ +#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ +#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ +#define MII 0x1000 /* MII on the 21143 */ + +#define TIMER_CB 0x80000000 /* Timer callback detection */ + +/* +** DE4X5 DEBUG Options +*/ +#define DEBUG_NONE 0x0000 /* No DEBUG messages */ +#define DEBUG_VERSION 0x0001 /* Print version message */ +#define DEBUG_MEDIA 0x0002 /* Print media messages */ +#define DEBUG_TX 0x0004 /* Print TX (queue_pkt) messages */ +#define DEBUG_RX 0x0008 /* Print RX (de4x5_rx) messages */ +#define DEBUG_SROM 0x0010 /* Print SROM messages */ +#define DEBUG_MII 0x0020 /* Print MII messages */ +#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ +#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ +#define DEBUG_PCICFG 0x0100 +#define DEBUG_ALL 0x01ff + +/* +** Miscellaneous +*/ +#define PCI 0 +#define EISA 1 + +#define HASH_TABLE_LEN 512 /* Bits */ +#define HASH_BITS 0x01ff /* 9 LS bits */ + +#define SETUP_FRAME_LEN 192 /* Bytes */ +#define IMPERF_PA_OFFSET 156 /* Bytes */ + +#define POLL_DEMAND 1 + +#define LOST_MEDIA_THRESHOLD 3 + +#define MASK_INTERRUPTS 1 +#define UNMASK_INTERRUPTS 0 + +#define DE4X5_STRLEN 8 + +#define DE4X5_INIT 0 /* Initialisation time */ +#define DE4X5_RUN 1 /* Run time */ + +#define DE4X5_SAVE_STATE 0 +#define DE4X5_RESTORE_STATE 1 + +/* +** Address Filtering Modes +*/ +#define PERFECT 0 /* 16 perfect physical addresses */ +#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */ +#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */ +#define ALL_HASH 3 /* Hashes all physical & multicast addrs */ + +#define ALL 0 /* Clear out all the setup frame */ +#define PHYS_ADDR_ONLY 1 /* Update the physical address only */ + +/* +** Adapter state +*/ +#define INITIALISED 0 /* After h/w initialised and mem alloc'd */ +#define CLOSED 1 /* Ready for opening */ +#define OPEN 2 /* Running */ + +/* +** Various wait times +*/ +#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */ +#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */ + +/* +** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since +** the vendors seem split 50-50 on how to calculate the OUI register values +** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()]. +*/ +#define NATIONAL_TX 0x2000 +#define BROADCOM_T4 0x03e0 +#define SEEQ_T4 0x0016 +#define CYPRESS_T4 0x0014 + +/* +** Speed Selection stuff +*/ +#define SET_10Mb {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ + if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ + mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\ + outl(omr, DE4X5_OMR);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | OMR_SDP | OMR_TTM, DE4X5_OMR);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +#define SET_100Mb {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + int fdx=0;\ + if (lp->phy[lp->active].id == NATIONAL_TX) {\ + mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\ + 0x18, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ + sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\ + if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\ + if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ + mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + if (fdx) omr |= OMR_FDX;\ + outl(omr, DE4X5_OMR);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +/* FIX ME so I don't jam 10Mb networks */ +#define SET_100Mb_PDET {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr, DE4X5_OMR);\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr, DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS, DE4X5_OMR);\ + lp->cache.gep = (GEP_FDXD | GEP_MODE);\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +/* +** Include the IOCTL stuff +*/ +#include + +#define DE4X5IOCTL SIOCDEVPRIVATE + +struct de4x5_ioctl { + unsigned short cmd; /* Command to run */ + unsigned short len; /* Length of the data buffer */ + unsigned char __user *data; /* Pointer to the data buffer */ +}; + +/* +** Recognised commands for the driver +*/ +#define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */ +#define DE4X5_SET_HWADDR 0x02 /* Set the hardware address */ +/* 0x03 and 0x04 were used before and are obsoleted now. Don't use them. */ +#define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ +#define DE4X5_GET_MCA 0x06 /* Get a multicast address */ +#define DE4X5_SET_MCA 0x07 /* Set a multicast address */ +#define DE4X5_CLR_MCA 0x08 /* Clear a multicast address */ +#define DE4X5_MCA_EN 0x09 /* Enable a multicast address group */ +#define DE4X5_GET_STATS 0x0a /* Get the driver statistics */ +#define DE4X5_CLR_STATS 0x0b /* Zero out the driver statistics */ +#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */ +#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */ +#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */ + +#define MOTO_SROM_BUG (lp->active == 8 && (get_unaligned_le32(dev->dev_addr) & 0x00ffffff) == 0x3e0008) diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c new file mode 100644 index 000000000000..9a21ca3873fc --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -0,0 +1,2253 @@ +/* + A Davicom DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast + ethernet driver for Linux. + Copyright (C) 1997 Sten Wang + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + DAVICOM Web-Site: www.davicom.com.tw + + Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw + Maintainer: Tobias Ringstrom + + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. + + Marcelo Tosatti : + Made it compile in 2.3 (device to net_device) + + Alan Cox : + Cleaned up for kernel merge. + Removed the back compatibility support + Reformatted, fixing spelling etc as I went + Removed IRQ 0-15 assumption + + Jeff Garzik : + Updated to use new PCI driver API. + Resource usage cleanups. + Report driver version to user. + + Tobias Ringstrom : + Cleaned up and added SMP safety. Thanks go to Jeff Garzik, + Andrew Morton and Frank Davis for the SMP safety fixes. + + Vojtech Pavlik : + Cleaned up pointer arithmetics. + Fixed a lot of 64bit issues. + Cleaned up printk()s a bit. + Fixed some obvious big endian problems. + + Tobias Ringstrom : + Use time_after for jiffies calculation. Added ethtool + support. Updated PCI resource allocation. Do not + forget to unmap PCI mapped skbs. + + Alan Cox + Added new PCI identifiers provided by Clear Zhang at ALi + for their 1563 ethernet device. + + TODO + + Check on 64 bit boxes. + Check and fix on big endian boxes. + + Test and make sure PCI latency is now correct for all cases. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "dmfe" +#define DRV_VERSION "1.36.4" +#define DRV_RELDATE "2002-01-17" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_TULIP_DM910X +#include +#endif + + +/* Board/System/Debug information/definition ---------------- */ +#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */ +#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */ +#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ +#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */ + +#define DM9102_IO_SIZE 0x80 +#define DM9102A_IO_SIZE 0x100 +#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */ +#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ +#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */ +#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ +#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ +#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) +#define TX_BUF_ALLOC 0x600 +#define RX_ALLOC_SIZE 0x620 +#define DM910X_RESET 1 +#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */ +#define CR6_DEFAULT 0x00080000 /* HD */ +#define CR7_DEFAULT 0x180c1 +#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ +#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ +#define MAX_PACKET_SIZE 1514 +#define DMFE_MAX_MULTICAST 14 +#define RX_COPY_SIZE 100 +#define MAX_CHECK_PACKET 0x8000 +#define DM9801_NOISE_FLOOR 8 +#define DM9802_NOISE_FLOOR 5 + +#define DMFE_WOL_LINKCHANGE 0x20000000 +#define DMFE_WOL_SAMPLEPACKET 0x10000000 +#define DMFE_WOL_MAGICPACKET 0x08000000 + + +#define DMFE_10MHF 0 +#define DMFE_100MHF 1 +#define DMFE_10MFD 4 +#define DMFE_100MFD 5 +#define DMFE_AUTO 8 +#define DMFE_1M_HPNA 0x10 + +#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */ +#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */ +#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */ +#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */ +#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */ +#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */ + +#define DMFE_TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */ +#define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */ +#define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */ + +#define DMFE_DBUG(dbug_now, msg, value) \ + do { \ + if (dmfe_debug || (dbug_now)) \ + pr_err("%s %lx\n", \ + (msg), (long) (value)); \ + } while (0) + +#define SHOW_MEDIA_TYPE(mode) \ + pr_info("Change Speed to %sMhz %s duplex\n" , \ + (mode & 1) ? "100":"10", \ + (mode & 4) ? "full":"half"); + + +/* CR9 definition: SROM/MII */ +#define CR9_SROM_READ 0x4800 +#define CR9_SRCS 0x1 +#define CR9_SRCLK 0x2 +#define CR9_CRDOUT 0x8 +#define SROM_DATA_0 0x0 +#define SROM_DATA_1 0x4 +#define PHY_DATA_1 0x20000 +#define PHY_DATA_0 0x00000 +#define MDCLKH 0x10000 + +#define PHY_POWER_DOWN 0x800 + +#define SROM_V41_CODE 0x14 + +#define SROM_CLK_WRITE(data, ioaddr) \ + outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr); \ + udelay(5); + +#define __CHK_IO_SIZE(pci_id, dev_rev) \ + (( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x30) ) ? \ + DM9102A_IO_SIZE: DM9102_IO_SIZE) + +#define CHK_IO_SIZE(pci_dev) \ + (__CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, \ + (pci_dev)->revision)) + +/* Sten Check */ +#define DEVICE net_device + +/* Structure/enum declaration ------------------------------- */ +struct tx_desc { + __le32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ + char *tx_buf_ptr; /* Data for us */ + struct tx_desc *next_tx_desc; +} __attribute__(( aligned(32) )); + +struct rx_desc { + __le32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ + struct sk_buff *rx_skb_ptr; /* Data for us */ + struct rx_desc *next_rx_desc; +} __attribute__(( aligned(32) )); + +struct dmfe_board_info { + u32 chip_id; /* Chip vendor/Device ID */ + u8 chip_revision; /* Chip revision */ + struct DEVICE *next_dev; /* next device */ + struct pci_dev *pdev; /* PCI device */ + spinlock_t lock; + + long ioaddr; /* I/O base address */ + u32 cr0_data; + u32 cr5_data; + u32 cr6_data; + u32 cr7_data; + u32 cr15_data; + + /* pointer for memory physical address */ + dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */ + dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */ + dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */ + dma_addr_t first_tx_desc_dma; + dma_addr_t first_rx_desc_dma; + + /* descriptor pointer */ + unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ + unsigned char *buf_pool_start; /* Tx buffer pool align dword */ + unsigned char *desc_pool_ptr; /* descriptor pool memory */ + struct tx_desc *first_tx_desc; + struct tx_desc *tx_insert_ptr; + struct tx_desc *tx_remove_ptr; + struct rx_desc *first_rx_desc; + struct rx_desc *rx_insert_ptr; + struct rx_desc *rx_ready_ptr; /* packet come pointer */ + unsigned long tx_packet_cnt; /* transmitted packet count */ + unsigned long tx_queue_cnt; /* wait to send packet count */ + unsigned long rx_avail_cnt; /* available rx descriptor count */ + unsigned long interval_rx_cnt; /* rx packet count a callback time */ + + u16 HPNA_command; /* For HPNA register 16 */ + u16 HPNA_timer; /* For HPNA remote device check */ + u16 dbug_cnt; + u16 NIC_capability; /* NIC media capability */ + u16 PHY_reg4; /* Saved Phyxcer register 4 value */ + + u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */ + u8 chip_type; /* Keep DM9102A chip type */ + u8 media_mode; /* user specify media mode */ + u8 op_mode; /* real work media mode */ + u8 phy_addr; + u8 wait_reset; /* Hardware failed, need to reset */ + u8 dm910x_chk_mode; /* Operating mode check */ + u8 first_in_callback; /* Flag to record state */ + u8 wol_mode; /* user WOL settings */ + struct timer_list timer; + + /* Driver defined statistic counter */ + unsigned long tx_fifo_underrun; + unsigned long tx_loss_carrier; + unsigned long tx_no_carrier; + unsigned long tx_late_collision; + unsigned long tx_excessive_collision; + unsigned long tx_jabber_timeout; + unsigned long reset_count; + unsigned long reset_cr8; + unsigned long reset_fatal; + unsigned long reset_TXtimeout; + + /* NIC SROM data */ + unsigned char srom[128]; +}; + +enum dmfe_offsets { + DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, + DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, + DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, + DCR15 = 0x78 +}; + +enum dmfe_CR6_bits { + CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, + CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, + CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 +}; + +/* Global variable declaration ----------------------------- */ +static int __devinitdata printed_version; +static const char version[] __devinitconst = + "Davicom DM9xxx net driver, version " DRV_VERSION " (" DRV_RELDATE ")"; + +static int dmfe_debug; +static unsigned char dmfe_media_mode = DMFE_AUTO; +static u32 dmfe_cr6_user_set; + +/* For module input parameter */ +static int debug; +static u32 cr6set; +static unsigned char mode = 8; +static u8 chkmode = 1; +static u8 HPNA_mode; /* Default: Low Power/High Speed */ +static u8 HPNA_rx_cmd; /* Default: Disable Rx remote command */ +static u8 HPNA_tx_cmd; /* Default: Don't issue remote command */ +static u8 HPNA_NoiseFloor; /* Default: HPNA NoiseFloor */ +static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control + 4: TX pause packet */ + + +/* function declaration ------------------------------------- */ +static int dmfe_open(struct DEVICE *); +static netdev_tx_t dmfe_start_xmit(struct sk_buff *, struct DEVICE *); +static int dmfe_stop(struct DEVICE *); +static void dmfe_set_filter_mode(struct DEVICE *); +static const struct ethtool_ops netdev_ethtool_ops; +static u16 read_srom_word(long ,int); +static irqreturn_t dmfe_interrupt(int , void *); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void poll_dmfe (struct net_device *dev); +#endif +static void dmfe_descriptor_init(struct dmfe_board_info *, unsigned long); +static void allocate_rx_buffer(struct dmfe_board_info *); +static void update_cr6(u32, unsigned long); +static void send_filter_frame(struct DEVICE *); +static void dm9132_id_table(struct DEVICE *); +static u16 phy_read(unsigned long, u8, u8, u32); +static void phy_write(unsigned long, u8, u8, u16, u32); +static void phy_write_1bit(unsigned long, u32); +static u16 phy_read_1bit(unsigned long); +static u8 dmfe_sense_speed(struct dmfe_board_info *); +static void dmfe_process_mode(struct dmfe_board_info *); +static void dmfe_timer(unsigned long); +static inline u32 cal_CRC(unsigned char *, unsigned int, u8); +static void dmfe_rx_packet(struct DEVICE *, struct dmfe_board_info *); +static void dmfe_free_tx_pkt(struct DEVICE *, struct dmfe_board_info *); +static void dmfe_reuse_skb(struct dmfe_board_info *, struct sk_buff *); +static void dmfe_dynamic_reset(struct DEVICE *); +static void dmfe_free_rxbuffer(struct dmfe_board_info *); +static void dmfe_init_dm910x(struct DEVICE *); +static void dmfe_parse_srom(struct dmfe_board_info *); +static void dmfe_program_DM9801(struct dmfe_board_info *, int); +static void dmfe_program_DM9802(struct dmfe_board_info *); +static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * ); +static void dmfe_set_phyxcer(struct dmfe_board_info *); + +/* DM910X network board routine ---------------------------- */ + +static const struct net_device_ops netdev_ops = { + .ndo_open = dmfe_open, + .ndo_stop = dmfe_stop, + .ndo_start_xmit = dmfe_start_xmit, + .ndo_set_multicast_list = dmfe_set_filter_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = poll_dmfe, +#endif +}; + +/* + * Search DM910X board ,allocate space and register it + */ + +static int __devinit dmfe_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct dmfe_board_info *db; /* board information structure */ + struct net_device *dev; + u32 pci_pmr; + int i, err; + + DMFE_DBUG(0, "dmfe_init_one()", 0); + + if (!printed_version++) + pr_info("%s\n", version); + + /* + * SPARC on-board DM910x chips should be handled by the main + * tulip driver, except for early DM9100s. + */ +#ifdef CONFIG_TULIP_DM910X + if ((ent->driver_data == PCI_DM9100_ID && pdev->revision >= 0x30) || + ent->driver_data == PCI_DM9102_ID) { + struct device_node *dp = pci_device_to_OF_node(pdev); + + if (dp && of_get_property(dp, "local-mac-address", NULL)) { + pr_info("skipping on-board DM910x (use tulip)\n"); + return -ENODEV; + } + } +#endif + + /* Init network device */ + dev = alloc_etherdev(sizeof(*db)); + if (dev == NULL) + return -ENOMEM; + SET_NETDEV_DEV(dev, &pdev->dev); + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + pr_warn("32-bit PCI DMA not available\n"); + err = -ENODEV; + goto err_out_free; + } + + /* Enable Master/IO access, Disable memory access */ + err = pci_enable_device(pdev); + if (err) + goto err_out_free; + + if (!pci_resource_start(pdev, 0)) { + pr_err("I/O base is zero\n"); + err = -ENODEV; + goto err_out_disable; + } + + if (pci_resource_len(pdev, 0) < (CHK_IO_SIZE(pdev)) ) { + pr_err("Allocated I/O size too small\n"); + err = -ENODEV; + goto err_out_disable; + } + +#if 0 /* pci_{enable_device,set_master} sets minimum latency for us now */ + + /* Set Latency Timer 80h */ + /* FIXME: setting values > 32 breaks some SiS 559x stuff. + Need a PCI quirk.. */ + + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80); +#endif + + if (pci_request_regions(pdev, DRV_NAME)) { + pr_err("Failed to request PCI regions\n"); + err = -ENODEV; + goto err_out_disable; + } + + /* Init system & device */ + db = netdev_priv(dev); + + /* Allocate Tx/Rx descriptor memory */ + db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * + DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); + if (!db->desc_pool_ptr) + goto err_out_res; + + db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * + TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); + if (!db->buf_pool_ptr) + goto err_out_free_desc; + + db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; + db->first_tx_desc_dma = db->desc_pool_dma_ptr; + db->buf_pool_start = db->buf_pool_ptr; + db->buf_pool_dma_start = db->buf_pool_dma_ptr; + + db->chip_id = ent->driver_data; + db->ioaddr = pci_resource_start(pdev, 0); + db->chip_revision = pdev->revision; + db->wol_mode = 0; + + db->pdev = pdev; + + dev->base_addr = db->ioaddr; + dev->irq = pdev->irq; + pci_set_drvdata(pdev, dev); + dev->netdev_ops = &netdev_ops; + dev->ethtool_ops = &netdev_ethtool_ops; + netif_carrier_off(dev); + spin_lock_init(&db->lock); + + pci_read_config_dword(pdev, 0x50, &pci_pmr); + pci_pmr &= 0x70000; + if ( (pci_pmr == 0x10000) && (db->chip_revision == 0x31) ) + db->chip_type = 1; /* DM9102A E3 */ + else + db->chip_type = 0; + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((__le16 *) db->srom)[i] = + cpu_to_le16(read_srom_word(db->ioaddr, i)); + + /* Set Node address */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = db->srom[20 + i]; + + err = register_netdev (dev); + if (err) + goto err_out_free_buf; + + dev_info(&dev->dev, "Davicom DM%04lx at pci%s, %pM, irq %d\n", + ent->driver_data >> 16, + pci_name(pdev), dev->dev_addr, dev->irq); + + pci_set_master(pdev); + + return 0; + +err_out_free_buf: + pci_free_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, + db->buf_pool_ptr, db->buf_pool_dma_ptr); +err_out_free_desc: + pci_free_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, + db->desc_pool_ptr, db->desc_pool_dma_ptr); +err_out_res: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_free: + pci_set_drvdata(pdev, NULL); + free_netdev(dev); + + return err; +} + + +static void __devexit dmfe_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct dmfe_board_info *db = netdev_priv(dev); + + DMFE_DBUG(0, "dmfe_remove_one()", 0); + + if (dev) { + + unregister_netdev(dev); + + pci_free_consistent(db->pdev, sizeof(struct tx_desc) * + DESC_ALL_CNT + 0x20, db->desc_pool_ptr, + db->desc_pool_dma_ptr); + pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, + db->buf_pool_ptr, db->buf_pool_dma_ptr); + pci_release_regions(pdev); + free_netdev(dev); /* free board information */ + + pci_set_drvdata(pdev, NULL); + } + + DMFE_DBUG(0, "dmfe_remove_one() exit", 0); +} + + +/* + * Open the interface. + * The interface is opened whenever "ifconfig" actives it. + */ + +static int dmfe_open(struct DEVICE *dev) +{ + int ret; + struct dmfe_board_info *db = netdev_priv(dev); + + DMFE_DBUG(0, "dmfe_open", 0); + + ret = request_irq(dev->irq, dmfe_interrupt, + IRQF_SHARED, dev->name, dev); + if (ret) + return ret; + + /* system variable init */ + db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set; + db->tx_packet_cnt = 0; + db->tx_queue_cnt = 0; + db->rx_avail_cnt = 0; + db->wait_reset = 0; + + db->first_in_callback = 0; + db->NIC_capability = 0xf; /* All capability*/ + db->PHY_reg4 = 0x1e0; + + /* CR6 operation mode decision */ + if ( !chkmode || (db->chip_id == PCI_DM9132_ID) || + (db->chip_revision >= 0x30) ) { + db->cr6_data |= DMFE_TXTH_256; + db->cr0_data = CR0_DEFAULT; + db->dm910x_chk_mode=4; /* Enter the normal mode */ + } else { + db->cr6_data |= CR6_SFT; /* Store & Forward mode */ + db->cr0_data = 0; + db->dm910x_chk_mode = 1; /* Enter the check mode */ + } + + /* Initialize DM910X board */ + dmfe_init_dm910x(dev); + + /* Active System Interface */ + netif_wake_queue(dev); + + /* set and active a timer process */ + init_timer(&db->timer); + db->timer.expires = DMFE_TIMER_WUT + HZ * 2; + db->timer.data = (unsigned long)dev; + db->timer.function = dmfe_timer; + add_timer(&db->timer); + + return 0; +} + + +/* Initialize DM910X board + * Reset DM910X board + * Initialize TX/Rx descriptor chain structure + * Send the set-up frame + * Enable Tx/Rx machine + */ + +static void dmfe_init_dm910x(struct DEVICE *dev) +{ + struct dmfe_board_info *db = netdev_priv(dev); + unsigned long ioaddr = db->ioaddr; + + DMFE_DBUG(0, "dmfe_init_dm910x()", 0); + + /* Reset DM910x MAC controller */ + outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */ + udelay(100); + outl(db->cr0_data, ioaddr + DCR0); + udelay(5); + + /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */ + db->phy_addr = 1; + + /* Parser SROM and media mode */ + dmfe_parse_srom(db); + db->media_mode = dmfe_media_mode; + + /* RESET Phyxcer Chip by GPR port bit 7 */ + outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */ + if (db->chip_id == PCI_DM9009_ID) { + outl(0x80, ioaddr + DCR12); /* Issue RESET signal */ + mdelay(300); /* Delay 300 ms */ + } + outl(0x0, ioaddr + DCR12); /* Clear RESET signal */ + + /* Process Phyxcer Media Mode */ + if ( !(db->media_mode & 0x10) ) /* Force 1M mode */ + dmfe_set_phyxcer(db); + + /* Media Mode Process */ + if ( !(db->media_mode & DMFE_AUTO) ) + db->op_mode = db->media_mode; /* Force Mode */ + + /* Initialize Transmit/Receive decriptor and CR3/4 */ + dmfe_descriptor_init(db, ioaddr); + + /* Init CR6 to program DM910x operation */ + update_cr6(db->cr6_data, ioaddr); + + /* Send setup frame */ + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev); /* DM9132 */ + else + send_filter_frame(dev); /* DM9102/DM9102A */ + + /* Init CR7, interrupt active bit */ + db->cr7_data = CR7_DEFAULT; + outl(db->cr7_data, ioaddr + DCR7); + + /* Init CR15, Tx jabber and Rx watchdog timer */ + outl(db->cr15_data, ioaddr + DCR15); + + /* Enable DM910X Tx/Rx function */ + db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000; + update_cr6(db->cr6_data, ioaddr); +} + + +/* + * Hardware start transmission. + * Send a packet to media from the upper layer. + */ + +static netdev_tx_t dmfe_start_xmit(struct sk_buff *skb, + struct DEVICE *dev) +{ + struct dmfe_board_info *db = netdev_priv(dev); + struct tx_desc *txptr; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_start_xmit", 0); + + /* Too large packet check */ + if (skb->len > MAX_PACKET_SIZE) { + pr_err("big packet = %d\n", (u16)skb->len); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + /* Resource flag check */ + netif_stop_queue(dev); + + spin_lock_irqsave(&db->lock, flags); + + /* No Tx resource check, it never happen nromally */ + if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) { + spin_unlock_irqrestore(&db->lock, flags); + pr_err("No Tx resource %ld\n", db->tx_queue_cnt); + return NETDEV_TX_BUSY; + } + + /* Disable NIC interrupt */ + outl(0, dev->base_addr + DCR7); + + /* transmit this packet */ + txptr = db->tx_insert_ptr; + skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len); + txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); + + /* Point to next transmit free descriptor */ + db->tx_insert_ptr = txptr->next_tx_desc; + + /* Transmit Packet Process */ + if ( (!db->tx_queue_cnt) && (db->tx_packet_cnt < TX_MAX_SEND_CNT) ) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + dev->trans_start = jiffies; /* saved time stamp */ + } else { + db->tx_queue_cnt++; /* queue TX packet */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + } + + /* Tx resource check */ + if ( db->tx_queue_cnt < TX_FREE_DESC_CNT ) + netif_wake_queue(dev); + + /* Restore CR7 to enable interrupt */ + spin_unlock_irqrestore(&db->lock, flags); + outl(db->cr7_data, dev->base_addr + DCR7); + + /* free this SKB */ + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + + +/* + * Stop the interface. + * The interface is stopped when it is brought. + */ + +static int dmfe_stop(struct DEVICE *dev) +{ + struct dmfe_board_info *db = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + + DMFE_DBUG(0, "dmfe_stop", 0); + + /* disable system */ + netif_stop_queue(dev); + + /* deleted timer */ + del_timer_sync(&db->timer); + + /* Reset & stop DM910X board */ + outl(DM910X_RESET, ioaddr + DCR0); + udelay(5); + phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); + + /* free interrupt */ + free_irq(dev->irq, dev); + + /* free allocated rx buffer */ + dmfe_free_rxbuffer(db); + +#if 0 + /* show statistic counter */ + printk("FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", + db->tx_fifo_underrun, db->tx_excessive_collision, + db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier, + db->tx_jabber_timeout, db->reset_count, db->reset_cr8, + db->reset_fatal, db->reset_TXtimeout); +#endif + + return 0; +} + + +/* + * DM9102 insterrupt handler + * receive the packet to upper layer, free the transmitted packet + */ + +static irqreturn_t dmfe_interrupt(int irq, void *dev_id) +{ + struct DEVICE *dev = dev_id; + struct dmfe_board_info *db = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_interrupt()", 0); + + spin_lock_irqsave(&db->lock, flags); + + /* Got DM910X status */ + db->cr5_data = inl(ioaddr + DCR5); + outl(db->cr5_data, ioaddr + DCR5); + if ( !(db->cr5_data & 0xc1) ) { + spin_unlock_irqrestore(&db->lock, flags); + return IRQ_HANDLED; + } + + /* Disable all interrupt in CR7 to solve the interrupt edge problem */ + outl(0, ioaddr + DCR7); + + /* Check system status */ + if (db->cr5_data & 0x2000) { + /* system bus error happen */ + DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data); + db->reset_fatal++; + db->wait_reset = 1; /* Need to RESET */ + spin_unlock_irqrestore(&db->lock, flags); + return IRQ_HANDLED; + } + + /* Received the coming packet */ + if ( (db->cr5_data & 0x40) && db->rx_avail_cnt ) + dmfe_rx_packet(dev, db); + + /* reallocate rx descriptor buffer */ + if (db->rx_avail_cntcr5_data & 0x01) + dmfe_free_tx_pkt(dev, db); + + /* Mode Check */ + if (db->dm910x_chk_mode & 0x2) { + db->dm910x_chk_mode = 0x4; + db->cr6_data |= 0x100; + update_cr6(db->cr6_data, db->ioaddr); + } + + /* Restore CR7 to enable interrupt mask */ + outl(db->cr7_data, ioaddr + DCR7); + + spin_unlock_irqrestore(&db->lock, flags); + return IRQ_HANDLED; +} + + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ + +static void poll_dmfe (struct net_device *dev) +{ + /* disable_irq here is not very nice, but with the lockless + interrupt handler we have no other choice. */ + disable_irq(dev->irq); + dmfe_interrupt (dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +/* + * Free TX resource after TX complete + */ + +static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db) +{ + struct tx_desc *txptr; + unsigned long ioaddr = dev->base_addr; + u32 tdes0; + + txptr = db->tx_remove_ptr; + while(db->tx_packet_cnt) { + tdes0 = le32_to_cpu(txptr->tdes0); + if (tdes0 & 0x80000000) + break; + + /* A packet sent completed */ + db->tx_packet_cnt--; + dev->stats.tx_packets++; + + /* Transmit statistic counter */ + if ( tdes0 != 0x7fffffff ) { + dev->stats.collisions += (tdes0 >> 3) & 0xf; + dev->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; + if (tdes0 & TDES0_ERR_MASK) { + dev->stats.tx_errors++; + + if (tdes0 & 0x0002) { /* UnderRun */ + db->tx_fifo_underrun++; + if ( !(db->cr6_data & CR6_SFT) ) { + db->cr6_data = db->cr6_data | CR6_SFT; + update_cr6(db->cr6_data, db->ioaddr); + } + } + if (tdes0 & 0x0100) + db->tx_excessive_collision++; + if (tdes0 & 0x0200) + db->tx_late_collision++; + if (tdes0 & 0x0400) + db->tx_no_carrier++; + if (tdes0 & 0x0800) + db->tx_loss_carrier++; + if (tdes0 & 0x4000) + db->tx_jabber_timeout++; + } + } + + txptr = txptr->next_tx_desc; + }/* End of while */ + + /* Update TX remove pointer to next */ + db->tx_remove_ptr = txptr; + + /* Send the Tx packet in queue */ + if ( (db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt ) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + db->tx_queue_cnt--; + outl(0x1, ioaddr + DCR1); /* Issue Tx polling */ + dev->trans_start = jiffies; /* saved time stamp */ + } + + /* Resource available check */ + if ( db->tx_queue_cnt < TX_WAKE_DESC_CNT ) + netif_wake_queue(dev); /* Active upper layer, send again */ +} + + +/* + * Calculate the CRC valude of the Rx packet + * flag = 1 : return the reverse CRC (for the received packet CRC) + * 0 : return the normal CRC (for Hash Table index) + */ + +static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) +{ + u32 crc = crc32(~0, Data, Len); + if (flag) crc = ~crc; + return crc; +} + + +/* + * Receive the come packet and pass to upper layer + */ + +static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb, *newskb; + int rxlen; + u32 rdes0; + + rxptr = db->rx_ready_ptr; + + while(db->rx_avail_cnt) { + rdes0 = le32_to_cpu(rxptr->rdes0); + if (rdes0 & 0x80000000) /* packet owner check */ + break; + + db->rx_avail_cnt--; + db->interval_rx_cnt++; + + pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), + RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); + + if ( (rdes0 & 0x300) != 0x300) { + /* A packet without First/Last flag */ + /* reuse this SKB */ + DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } else { + /* A packet with First/Last flag */ + rxlen = ( (rdes0 >> 16) & 0x3fff) - 4; + + /* error summary bit check */ + if (rdes0 & 0x8000) { + /* This is a error packet */ + dev->stats.rx_errors++; + if (rdes0 & 1) + dev->stats.rx_fifo_errors++; + if (rdes0 & 2) + dev->stats.rx_crc_errors++; + if (rdes0 & 0x80) + dev->stats.rx_length_errors++; + } + + if ( !(rdes0 & 0x8000) || + ((db->cr6_data & CR6_PM) && (rxlen>6)) ) { + skb = rxptr->rx_skb_ptr; + + /* Received Packet CRC check need or not */ + if ( (db->dm910x_chk_mode & 1) && + (cal_CRC(skb->data, rxlen, 1) != + (*(u32 *) (skb->data+rxlen) ))) { /* FIXME (?) */ + /* Found a error received packet */ + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + db->dm910x_chk_mode = 3; + } else { + /* Good packet, send to upper layer */ + /* Shorst packet used new SKB */ + if ((rxlen < RX_COPY_SIZE) && + ((newskb = dev_alloc_skb(rxlen + 2)) + != NULL)) { + + skb = newskb; + /* size less than COPY_SIZE, allocate a rxlen SKB */ + skb_reserve(skb, 2); /* 16byte align */ + skb_copy_from_linear_data(rxptr->rx_skb_ptr, + skb_put(skb, rxlen), + rxlen); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } else + skb_put(skb, rxlen); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += rxlen; + } + } else { + /* Reuse SKB buffer when the packet is error */ + DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } + } + + rxptr = rxptr->next_rx_desc; + } + + db->rx_ready_ptr = rxptr; +} + +/* + * Set DM910X multicast address + */ + +static void dmfe_set_filter_mode(struct DEVICE * dev) +{ + struct dmfe_board_info *db = netdev_priv(dev); + unsigned long flags; + int mc_count = netdev_mc_count(dev); + + DMFE_DBUG(0, "dmfe_set_filter_mode()", 0); + spin_lock_irqsave(&db->lock, flags); + + if (dev->flags & IFF_PROMISC) { + DMFE_DBUG(0, "Enable PROM Mode", 0); + db->cr6_data |= CR6_PM | CR6_PBF; + update_cr6(db->cr6_data, db->ioaddr); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + if (dev->flags & IFF_ALLMULTI || mc_count > DMFE_MAX_MULTICAST) { + DMFE_DBUG(0, "Pass all multicast address", mc_count); + db->cr6_data &= ~(CR6_PM | CR6_PBF); + db->cr6_data |= CR6_PAM; + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + DMFE_DBUG(0, "Set multicast address", mc_count); + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev); /* DM9132 */ + else + send_filter_frame(dev); /* DM9102/DM9102A */ + spin_unlock_irqrestore(&db->lock, flags); +} + +/* + * Ethtool interace + */ + +static void dmfe_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct dmfe_board_info *np = netdev_priv(dev); + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + if (np->pdev) + strcpy(info->bus_info, pci_name(np->pdev)); + else + sprintf(info->bus_info, "EISA 0x%lx %d", + dev->base_addr, dev->irq); +} + +static int dmfe_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct dmfe_board_info *db = netdev_priv(dev); + + if (wolinfo->wolopts & (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | + WAKE_ARP | WAKE_MAGICSECURE)) + return -EOPNOTSUPP; + + db->wol_mode = wolinfo->wolopts; + return 0; +} + +static void dmfe_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct dmfe_board_info *db = netdev_priv(dev); + + wolinfo->supported = WAKE_PHY | WAKE_MAGIC; + wolinfo->wolopts = db->wol_mode; +} + + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = dmfe_ethtool_get_drvinfo, + .get_link = ethtool_op_get_link, + .set_wol = dmfe_ethtool_set_wol, + .get_wol = dmfe_ethtool_get_wol, +}; + +/* + * A periodic timer routine + * Dynamic media sense, allocate Rx buffer... + */ + +static void dmfe_timer(unsigned long data) +{ + u32 tmp_cr8; + unsigned char tmp_cr12; + struct DEVICE *dev = (struct DEVICE *) data; + struct dmfe_board_info *db = netdev_priv(dev); + unsigned long flags; + + int link_ok, link_ok_phy; + + DMFE_DBUG(0, "dmfe_timer()", 0); + spin_lock_irqsave(&db->lock, flags); + + /* Media mode process when Link OK before enter this route */ + if (db->first_in_callback == 0) { + db->first_in_callback = 1; + if (db->chip_type && (db->chip_id==PCI_DM9102_ID)) { + db->cr6_data &= ~0x40000; + update_cr6(db->cr6_data, db->ioaddr); + phy_write(db->ioaddr, + db->phy_addr, 0, 0x1000, db->chip_id); + db->cr6_data |= 0x40000; + update_cr6(db->cr6_data, db->ioaddr); + db->timer.expires = DMFE_TIMER_WUT + HZ * 2; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + } + + + /* Operating Mode Check */ + if ( (db->dm910x_chk_mode & 0x1) && + (dev->stats.rx_packets > MAX_CHECK_PACKET) ) + db->dm910x_chk_mode = 0x4; + + /* Dynamic reset DM910X : system error or transmit time-out */ + tmp_cr8 = inl(db->ioaddr + DCR8); + if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) { + db->reset_cr8++; + db->wait_reset = 1; + } + db->interval_rx_cnt = 0; + + /* TX polling kick monitor */ + if ( db->tx_packet_cnt && + time_after(jiffies, dev_trans_start(dev) + DMFE_TX_KICK) ) { + outl(0x1, dev->base_addr + DCR1); /* Tx polling again */ + + /* TX Timeout */ + if (time_after(jiffies, dev_trans_start(dev) + DMFE_TX_TIMEOUT) ) { + db->reset_TXtimeout++; + db->wait_reset = 1; + dev_warn(&dev->dev, "Tx timeout - resetting\n"); + } + } + + if (db->wait_reset) { + DMFE_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt); + db->reset_count++; + dmfe_dynamic_reset(dev); + db->first_in_callback = 0; + db->timer.expires = DMFE_TIMER_WUT; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + /* Link status check, Dynamic media type change */ + if (db->chip_id == PCI_DM9132_ID) + tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */ + else + tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ + + if ( ((db->chip_id == PCI_DM9102_ID) && + (db->chip_revision == 0x30)) || + ((db->chip_id == PCI_DM9132_ID) && + (db->chip_revision == 0x10)) ) { + /* DM9102A Chip */ + if (tmp_cr12 & 2) + link_ok = 0; + else + link_ok = 1; + } + else + /*0x43 is used instead of 0x3 because bit 6 should represent + link status of external PHY */ + link_ok = (tmp_cr12 & 0x43) ? 1 : 0; + + + /* If chip reports that link is failed it could be because external + PHY link status pin is not connected correctly to chip + To be sure ask PHY too. + */ + + /* need a dummy read because of PHY's register latch*/ + phy_read (db->ioaddr, db->phy_addr, 1, db->chip_id); + link_ok_phy = (phy_read (db->ioaddr, + db->phy_addr, 1, db->chip_id) & 0x4) ? 1 : 0; + + if (link_ok_phy != link_ok) { + DMFE_DBUG (0, "PHY and chip report different link status", 0); + link_ok = link_ok | link_ok_phy; + } + + if ( !link_ok && netif_carrier_ok(dev)) { + /* Link Failed */ + DMFE_DBUG(0, "Link Failed", tmp_cr12); + netif_carrier_off(dev); + + /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */ + /* AUTO or force 1M Homerun/Longrun don't need */ + if ( !(db->media_mode & 0x38) ) + phy_write(db->ioaddr, db->phy_addr, + 0, 0x1000, db->chip_id); + + /* AUTO mode, if INT phyxcer link failed, select EXT device */ + if (db->media_mode & DMFE_AUTO) { + /* 10/100M link failed, used 1M Home-Net */ + db->cr6_data|=0x00040000; /* bit18=1, MII */ + db->cr6_data&=~0x00000200; /* bit9=0, HD mode */ + update_cr6(db->cr6_data, db->ioaddr); + } + } else if (!netif_carrier_ok(dev)) { + + DMFE_DBUG(0, "Link link OK", tmp_cr12); + + /* Auto Sense Speed */ + if ( !(db->media_mode & DMFE_AUTO) || !dmfe_sense_speed(db)) { + netif_carrier_on(dev); + SHOW_MEDIA_TYPE(db->op_mode); + } + + dmfe_process_mode(db); + } + + /* HPNA remote command check */ + if (db->HPNA_command & 0xf00) { + db->HPNA_timer--; + if (!db->HPNA_timer) + dmfe_HPNA_remote_cmd_chk(db); + } + + /* Timer active again */ + db->timer.expires = DMFE_TIMER_WUT; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); +} + + +/* + * Dynamic reset the DM910X board + * Stop DM910X board + * Free Tx/Rx allocated memory + * Reset DM910X board + * Re-initialize DM910X board + */ + +static void dmfe_dynamic_reset(struct DEVICE *dev) +{ + struct dmfe_board_info *db = netdev_priv(dev); + + DMFE_DBUG(0, "dmfe_dynamic_reset()", 0); + + /* Sopt MAC controller */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ + update_cr6(db->cr6_data, dev->base_addr); + outl(0, dev->base_addr + DCR7); /* Disable Interrupt */ + outl(inl(dev->base_addr + DCR5), dev->base_addr + DCR5); + + /* Disable upper layer interface */ + netif_stop_queue(dev); + + /* Free Rx Allocate buffer */ + dmfe_free_rxbuffer(db); + + /* system variable init */ + db->tx_packet_cnt = 0; + db->tx_queue_cnt = 0; + db->rx_avail_cnt = 0; + netif_carrier_off(dev); + db->wait_reset = 0; + + /* Re-initialize DM910X board */ + dmfe_init_dm910x(dev); + + /* Restart upper layer interface */ + netif_wake_queue(dev); +} + + +/* + * free all allocated rx buffer + */ + +static void dmfe_free_rxbuffer(struct dmfe_board_info * db) +{ + DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0); + + /* free allocated rx buffer */ + while (db->rx_avail_cnt) { + dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr); + db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc; + db->rx_avail_cnt--; + } +} + + +/* + * Reuse the SK buffer + */ + +static void dmfe_reuse_skb(struct dmfe_board_info *db, struct sk_buff * skb) +{ + struct rx_desc *rxptr = db->rx_insert_ptr; + + if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { + rxptr->rx_skb_ptr = skb; + rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, + skb->data, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + wmb(); + rxptr->rdes0 = cpu_to_le32(0x80000000); + db->rx_avail_cnt++; + db->rx_insert_ptr = rxptr->next_rx_desc; + } else + DMFE_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt); +} + + +/* + * Initialize transmit/Receive descriptor + * Using Chain structure, and allocate Tx/Rx buffer + */ + +static void dmfe_descriptor_init(struct dmfe_board_info *db, unsigned long ioaddr) +{ + struct tx_desc *tmp_tx; + struct rx_desc *tmp_rx; + unsigned char *tmp_buf; + dma_addr_t tmp_tx_dma, tmp_rx_dma; + dma_addr_t tmp_buf_dma; + int i; + + DMFE_DBUG(0, "dmfe_descriptor_init()", 0); + + /* tx descriptor start pointer */ + db->tx_insert_ptr = db->first_tx_desc; + db->tx_remove_ptr = db->first_tx_desc; + outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ + + /* rx descriptor start pointer */ + db->first_rx_desc = (void *)db->first_tx_desc + + sizeof(struct tx_desc) * TX_DESC_CNT; + + db->first_rx_desc_dma = db->first_tx_desc_dma + + sizeof(struct tx_desc) * TX_DESC_CNT; + db->rx_insert_ptr = db->first_rx_desc; + db->rx_ready_ptr = db->first_rx_desc; + outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ + + /* Init Transmit chain */ + tmp_buf = db->buf_pool_start; + tmp_buf_dma = db->buf_pool_dma_start; + tmp_tx_dma = db->first_tx_desc_dma; + for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) { + tmp_tx->tx_buf_ptr = tmp_buf; + tmp_tx->tdes0 = cpu_to_le32(0); + tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */ + tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma); + tmp_tx_dma += sizeof(struct tx_desc); + tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma); + tmp_tx->next_tx_desc = tmp_tx + 1; + tmp_buf = tmp_buf + TX_BUF_ALLOC; + tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC; + } + (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma); + tmp_tx->next_tx_desc = db->first_tx_desc; + + /* Init Receive descriptor chain */ + tmp_rx_dma=db->first_rx_desc_dma; + for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) { + tmp_rx->rdes0 = cpu_to_le32(0); + tmp_rx->rdes1 = cpu_to_le32(0x01000600); + tmp_rx_dma += sizeof(struct rx_desc); + tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma); + tmp_rx->next_rx_desc = tmp_rx + 1; + } + (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma); + tmp_rx->next_rx_desc = db->first_rx_desc; + + /* pre-allocate Rx buffer */ + allocate_rx_buffer(db); +} + + +/* + * Update CR6 value + * Firstly stop DM910X , then written value and start + */ + +static void update_cr6(u32 cr6_data, unsigned long ioaddr) +{ + u32 cr6_tmp; + + cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */ + outl(cr6_tmp, ioaddr + DCR6); + udelay(5); + outl(cr6_data, ioaddr + DCR6); + udelay(5); +} + + +/* + * Send a setup frame for DM9132 + * This setup frame initialize DM910X address filter mode +*/ + +static void dm9132_id_table(struct DEVICE *dev) +{ + struct netdev_hw_addr *ha; + u16 * addrptr; + unsigned long ioaddr = dev->base_addr+0xc0; /* ID Table */ + u32 hash_val; + u16 i, hash_table[4]; + + DMFE_DBUG(0, "dm9132_id_table()", 0); + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + outw(addrptr[0], ioaddr); + ioaddr += 4; + outw(addrptr[1], ioaddr); + ioaddr += 4; + outw(addrptr[2], ioaddr); + ioaddr += 4; + + /* Clear Hash Table */ + memset(hash_table, 0, sizeof(hash_table)); + + /* broadcast address */ + hash_table[3] = 0x8000; + + /* the multicast address in Hash Table : 64 bits */ + netdev_for_each_mc_addr(ha, dev) { + hash_val = cal_CRC((char *) ha->addr, 6, 0) & 0x3f; + hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); + } + + /* Write the hash table to MAC MD table */ + for (i = 0; i < 4; i++, ioaddr += 4) + outw(hash_table[i], ioaddr); +} + + +/* + * Send a setup frame for DM9102/DM9102A + * This setup frame initialize DM910X address filter mode + */ + +static void send_filter_frame(struct DEVICE *dev) +{ + struct dmfe_board_info *db = netdev_priv(dev); + struct netdev_hw_addr *ha; + struct tx_desc *txptr; + u16 * addrptr; + u32 * suptr; + int i; + + DMFE_DBUG(0, "send_filter_frame()", 0); + + txptr = db->tx_insert_ptr; + suptr = (u32 *) txptr->tx_buf_ptr; + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + *suptr++ = addrptr[0]; + *suptr++ = addrptr[1]; + *suptr++ = addrptr[2]; + + /* broadcast address */ + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + + /* fit the multicast address */ + netdev_for_each_mc_addr(ha, dev) { + addrptr = (u16 *) ha->addr; + *suptr++ = addrptr[0]; + *suptr++ = addrptr[1]; + *suptr++ = addrptr[2]; + } + + for (i = netdev_mc_count(dev); i < 14; i++) { + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + } + + /* prepare the setup frame */ + db->tx_insert_ptr = txptr->next_tx_desc; + txptr->tdes1 = cpu_to_le32(0x890000c0); + + /* Resource Check and Send the setup packet */ + if (!db->tx_packet_cnt) { + /* Resource Empty */ + db->tx_packet_cnt++; + txptr->tdes0 = cpu_to_le32(0x80000000); + update_cr6(db->cr6_data | 0x2000, dev->base_addr); + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + update_cr6(db->cr6_data, dev->base_addr); + dev->trans_start = jiffies; + } else + db->tx_queue_cnt++; /* Put in TX queue */ +} + + +/* + * Allocate rx buffer, + * As possible as allocate maxiumn Rx buffer + */ + +static void allocate_rx_buffer(struct dmfe_board_info *db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + + rxptr = db->rx_insert_ptr; + + while(db->rx_avail_cnt < RX_DESC_CNT) { + if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) + break; + rxptr->rx_skb_ptr = skb; /* FIXME (?) */ + rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->data, + RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + wmb(); + rxptr->rdes0 = cpu_to_le32(0x80000000); + rxptr = rxptr->next_rx_desc; + db->rx_avail_cnt++; + } + + db->rx_insert_ptr = rxptr; +} + + +/* + * Read one word data from the serial ROM + */ + +static u16 read_srom_word(long ioaddr, int offset) +{ + int i; + u16 srom_data = 0; + long cr9_ioaddr = ioaddr + DCR9; + + outl(CR9_SROM_READ, cr9_ioaddr); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + /* Send the Read Command 110b */ + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); + + /* Send the offset */ + for (i = 5; i >= 0; i--) { + srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; + SROM_CLK_WRITE(srom_data, cr9_ioaddr); + } + + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + for (i = 16; i > 0; i--) { + outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); + udelay(5); + srom_data = (srom_data << 1) | + ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + udelay(5); + } + + outl(CR9_SROM_READ, cr9_ioaddr); + return srom_data; +} + + +/* + * Auto sense the media mode + */ + +static u8 dmfe_sense_speed(struct dmfe_board_info * db) +{ + u8 ErrFlag = 0; + u16 phy_mode; + + /* CR6 bit18=0, select 10/100M */ + update_cr6( (db->cr6_data & ~0x40000), db->ioaddr); + + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + + if ( (phy_mode & 0x24) == 0x24 ) { + if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ + phy_mode = phy_read(db->ioaddr, + db->phy_addr, 7, db->chip_id) & 0xf000; + else /* DM9102/DM9102A */ + phy_mode = phy_read(db->ioaddr, + db->phy_addr, 17, db->chip_id) & 0xf000; + switch (phy_mode) { + case 0x1000: db->op_mode = DMFE_10MHF; break; + case 0x2000: db->op_mode = DMFE_10MFD; break; + case 0x4000: db->op_mode = DMFE_100MHF; break; + case 0x8000: db->op_mode = DMFE_100MFD; break; + default: db->op_mode = DMFE_10MHF; + ErrFlag = 1; + break; + } + } else { + db->op_mode = DMFE_10MHF; + DMFE_DBUG(0, "Link Failed :", phy_mode); + ErrFlag = 1; + } + + return ErrFlag; +} + + +/* + * Set 10/100 phyxcer capability + * AUTO mode : phyxcer register4 is NIC capability + * Force mode: phyxcer register4 is the force media + */ + +static void dmfe_set_phyxcer(struct dmfe_board_info *db) +{ + u16 phy_reg; + + /* Select 10/100M phyxcer */ + db->cr6_data &= ~0x40000; + update_cr6(db->cr6_data, db->ioaddr); + + /* DM9009 Chip: Phyxcer reg18 bit12=0 */ + if (db->chip_id == PCI_DM9009_ID) { + phy_reg = phy_read(db->ioaddr, + db->phy_addr, 18, db->chip_id) & ~0x1000; + + phy_write(db->ioaddr, + db->phy_addr, 18, phy_reg, db->chip_id); + } + + /* Phyxcer capability setting */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0; + + if (db->media_mode & DMFE_AUTO) { + /* AUTO Mode */ + phy_reg |= db->PHY_reg4; + } else { + /* Force Mode */ + switch(db->media_mode) { + case DMFE_10MHF: phy_reg |= 0x20; break; + case DMFE_10MFD: phy_reg |= 0x40; break; + case DMFE_100MHF: phy_reg |= 0x80; break; + case DMFE_100MFD: phy_reg |= 0x100; break; + } + if (db->chip_id == PCI_DM9009_ID) phy_reg &= 0x61; + } + + /* Write new capability to Phyxcer Reg4 */ + if ( !(phy_reg & 0x01e0)) { + phy_reg|=db->PHY_reg4; + db->media_mode|=DMFE_AUTO; + } + phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); + + /* Restart Auto-Negotiation */ + if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1800, db->chip_id); + if ( !db->chip_type ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id); +} + + +/* + * Process op-mode + * AUTO mode : PHY controller in Auto-negotiation Mode + * Force mode: PHY controller in force mode with HUB + * N-way force capability with SWITCH + */ + +static void dmfe_process_mode(struct dmfe_board_info *db) +{ + u16 phy_reg; + + /* Full Duplex Mode Check */ + if (db->op_mode & 0x4) + db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */ + else + db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */ + + /* Transciver Selection */ + if (db->op_mode & 0x10) /* 1M HomePNA */ + db->cr6_data |= 0x40000;/* External MII select */ + else + db->cr6_data &= ~0x40000;/* Internal 10/100 transciver */ + + update_cr6(db->cr6_data, db->ioaddr); + + /* 10/100M phyxcer force mode need */ + if ( !(db->media_mode & 0x18)) { + /* Forece Mode */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 6, db->chip_id); + if ( !(phy_reg & 0x1) ) { + /* parter without N-Way capability */ + phy_reg = 0x0; + switch(db->op_mode) { + case DMFE_10MHF: phy_reg = 0x0; break; + case DMFE_10MFD: phy_reg = 0x100; break; + case DMFE_100MHF: phy_reg = 0x2000; break; + case DMFE_100MFD: phy_reg = 0x2100; break; + } + phy_write(db->ioaddr, + db->phy_addr, 0, phy_reg, db->chip_id); + if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) + mdelay(20); + phy_write(db->ioaddr, + db->phy_addr, 0, phy_reg, db->chip_id); + } + } +} + + +/* + * Write a word to Phy register + */ + +static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, + u16 phy_data, u32 chip_id) +{ + u16 i; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + ioaddr = iobase + 0x80 + offset * 4; + outw(phy_data, ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send Phy address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, + phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Send register address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, + offset & i ? PHY_DATA_1 : PHY_DATA_0); + + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Write a word data to PHY controller */ + for ( i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, + phy_data & i ? PHY_DATA_1 : PHY_DATA_0); + } +} + + +/* + * Read a word data from phy register + */ + +static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) +{ + int i; + u16 phy_data; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + /* DM9132 Chip */ + ioaddr = iobase + 0x80 + offset * 4; + phy_data = inw(ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Send Phy address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, + phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Send register address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, + offset & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Skip transition state */ + phy_read_1bit(ioaddr); + + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr); + } + } + + return phy_data; +} + + +/* + * Write one bit data to Phy Controller + */ + +static void phy_write_1bit(unsigned long ioaddr, u32 phy_data) +{ + outl(phy_data, ioaddr); /* MII Clock Low */ + udelay(1); + outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ + udelay(1); + outl(phy_data, ioaddr); /* MII Clock Low */ + udelay(1); +} + + +/* + * Read one bit phy data from PHY controller + */ + +static u16 phy_read_1bit(unsigned long ioaddr) +{ + u16 phy_data; + + outl(0x50000, ioaddr); + udelay(1); + phy_data = ( inl(ioaddr) >> 19 ) & 0x1; + outl(0x40000, ioaddr); + udelay(1); + + return phy_data; +} + + +/* + * Parser SROM and media mode + */ + +static void dmfe_parse_srom(struct dmfe_board_info * db) +{ + char * srom = db->srom; + int dmfe_mode, tmp_reg; + + DMFE_DBUG(0, "dmfe_parse_srom() ", 0); + + /* Init CR15 */ + db->cr15_data = CR15_DEFAULT; + + /* Check SROM Version */ + if ( ( (int) srom[18] & 0xff) == SROM_V41_CODE) { + /* SROM V4.01 */ + /* Get NIC support media mode */ + db->NIC_capability = le16_to_cpup((__le16 *) (srom + 34)); + db->PHY_reg4 = 0; + for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) { + switch( db->NIC_capability & tmp_reg ) { + case 0x1: db->PHY_reg4 |= 0x0020; break; + case 0x2: db->PHY_reg4 |= 0x0040; break; + case 0x4: db->PHY_reg4 |= 0x0080; break; + case 0x8: db->PHY_reg4 |= 0x0100; break; + } + } + + /* Media Mode Force or not check */ + dmfe_mode = (le32_to_cpup((__le32 *) (srom + 34)) & + le32_to_cpup((__le32 *) (srom + 36))); + switch(dmfe_mode) { + case 0x4: dmfe_media_mode = DMFE_100MHF; break; /* 100MHF */ + case 0x2: dmfe_media_mode = DMFE_10MFD; break; /* 10MFD */ + case 0x8: dmfe_media_mode = DMFE_100MFD; break; /* 100MFD */ + case 0x100: + case 0x200: dmfe_media_mode = DMFE_1M_HPNA; break;/* HomePNA */ + } + + /* Special Function setting */ + /* VLAN function */ + if ( (SF_mode & 0x1) || (srom[43] & 0x80) ) + db->cr15_data |= 0x40; + + /* Flow Control */ + if ( (SF_mode & 0x2) || (srom[40] & 0x1) ) + db->cr15_data |= 0x400; + + /* TX pause packet */ + if ( (SF_mode & 0x4) || (srom[40] & 0xe) ) + db->cr15_data |= 0x9800; + } + + /* Parse HPNA parameter */ + db->HPNA_command = 1; + + /* Accept remote command or not */ + if (HPNA_rx_cmd == 0) + db->HPNA_command |= 0x8000; + + /* Issue remote command & operation mode */ + if (HPNA_tx_cmd == 1) + switch(HPNA_mode) { /* Issue Remote Command */ + case 0: db->HPNA_command |= 0x0904; break; + case 1: db->HPNA_command |= 0x0a00; break; + case 2: db->HPNA_command |= 0x0506; break; + case 3: db->HPNA_command |= 0x0602; break; + } + else + switch(HPNA_mode) { /* Don't Issue */ + case 0: db->HPNA_command |= 0x0004; break; + case 1: db->HPNA_command |= 0x0000; break; + case 2: db->HPNA_command |= 0x0006; break; + case 3: db->HPNA_command |= 0x0002; break; + } + + /* Check DM9801 or DM9802 present or not */ + db->HPNA_present = 0; + update_cr6(db->cr6_data|0x40000, db->ioaddr); + tmp_reg = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id); + if ( ( tmp_reg & 0xfff0 ) == 0xb900 ) { + /* DM9801 or DM9802 present */ + db->HPNA_timer = 8; + if ( phy_read(db->ioaddr, db->phy_addr, 31, db->chip_id) == 0x4404) { + /* DM9801 HomeRun */ + db->HPNA_present = 1; + dmfe_program_DM9801(db, tmp_reg); + } else { + /* DM9802 LongRun */ + db->HPNA_present = 2; + dmfe_program_DM9802(db); + } + } + +} + + +/* + * Init HomeRun DM9801 + */ + +static void dmfe_program_DM9801(struct dmfe_board_info * db, int HPNA_rev) +{ + uint reg17, reg25; + + if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9801_NOISE_FLOOR; + switch(HPNA_rev) { + case 0xb900: /* DM9801 E3 */ + db->HPNA_command |= 0x1000; + reg25 = phy_read(db->ioaddr, db->phy_addr, 24, db->chip_id); + reg25 = ( (reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + break; + case 0xb901: /* DM9801 E4 */ + reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3; + break; + case 0xb902: /* DM9801 E5 */ + case 0xb903: /* DM9801 E6 */ + default: + db->HPNA_command |= 0x1000; + reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor; + break; + } + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_write(db->ioaddr, db->phy_addr, 17, reg17, db->chip_id); + phy_write(db->ioaddr, db->phy_addr, 25, reg25, db->chip_id); +} + + +/* + * Init HomeRun DM9802 + */ + +static void dmfe_program_DM9802(struct dmfe_board_info * db) +{ + uint phy_reg; + + if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9802_NOISE_FLOOR; + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_reg = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + phy_reg = ( phy_reg & 0xff00) + HPNA_NoiseFloor; + phy_write(db->ioaddr, db->phy_addr, 25, phy_reg, db->chip_id); +} + + +/* + * Check remote HPNA power and speed status. If not correct, + * issue command again. +*/ + +static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * db) +{ + uint phy_reg; + + /* Got remote device status */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0x60; + switch(phy_reg) { + case 0x00: phy_reg = 0x0a00;break; /* LP/LS */ + case 0x20: phy_reg = 0x0900;break; /* LP/HS */ + case 0x40: phy_reg = 0x0600;break; /* HP/LS */ + case 0x60: phy_reg = 0x0500;break; /* HP/HS */ + } + + /* Check remote device status match our setting ot not */ + if ( phy_reg != (db->HPNA_command & 0x0f00) ) { + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, + db->chip_id); + db->HPNA_timer=8; + } else + db->HPNA_timer=600; /* Match, every 10 minutes, check */ +} + + + +static DEFINE_PCI_DEVICE_TABLE(dmfe_pci_tbl) = { + { 0x1282, 0x9132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9132_ID }, + { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID }, + { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID }, + { 0x1282, 0x9009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9009_ID }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); + + +#ifdef CONFIG_PM +static int dmfe_suspend(struct pci_dev *pci_dev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pci_dev); + struct dmfe_board_info *db = netdev_priv(dev); + u32 tmp; + + /* Disable upper layer interface */ + netif_device_detach(dev); + + /* Disable Tx/Rx */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); + update_cr6(db->cr6_data, dev->base_addr); + + /* Disable Interrupt */ + outl(0, dev->base_addr + DCR7); + outl(inl (dev->base_addr + DCR5), dev->base_addr + DCR5); + + /* Fre RX buffers */ + dmfe_free_rxbuffer(db); + + /* Enable WOL */ + pci_read_config_dword(pci_dev, 0x40, &tmp); + tmp &= ~(DMFE_WOL_LINKCHANGE|DMFE_WOL_MAGICPACKET); + + if (db->wol_mode & WAKE_PHY) + tmp |= DMFE_WOL_LINKCHANGE; + if (db->wol_mode & WAKE_MAGIC) + tmp |= DMFE_WOL_MAGICPACKET; + + pci_write_config_dword(pci_dev, 0x40, tmp); + + pci_enable_wake(pci_dev, PCI_D3hot, 1); + pci_enable_wake(pci_dev, PCI_D3cold, 1); + + /* Power down device*/ + pci_save_state(pci_dev); + pci_set_power_state(pci_dev, pci_choose_state (pci_dev, state)); + + return 0; +} + +static int dmfe_resume(struct pci_dev *pci_dev) +{ + struct net_device *dev = pci_get_drvdata(pci_dev); + u32 tmp; + + pci_set_power_state(pci_dev, PCI_D0); + pci_restore_state(pci_dev); + + /* Re-initialize DM910X board */ + dmfe_init_dm910x(dev); + + /* Disable WOL */ + pci_read_config_dword(pci_dev, 0x40, &tmp); + + tmp &= ~(DMFE_WOL_LINKCHANGE | DMFE_WOL_MAGICPACKET); + pci_write_config_dword(pci_dev, 0x40, tmp); + + pci_enable_wake(pci_dev, PCI_D3hot, 0); + pci_enable_wake(pci_dev, PCI_D3cold, 0); + + /* Restart upper layer interface */ + netif_device_attach(dev); + + return 0; +} +#else +#define dmfe_suspend NULL +#define dmfe_resume NULL +#endif + +static struct pci_driver dmfe_driver = { + .name = "dmfe", + .id_table = dmfe_pci_tbl, + .probe = dmfe_init_one, + .remove = __devexit_p(dmfe_remove_one), + .suspend = dmfe_suspend, + .resume = dmfe_resume +}; + +MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); +MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_param(debug, int, 0); +module_param(mode, byte, 0); +module_param(cr6set, int, 0); +module_param(chkmode, byte, 0); +module_param(HPNA_mode, byte, 0); +module_param(HPNA_rx_cmd, byte, 0); +module_param(HPNA_tx_cmd, byte, 0); +module_param(HPNA_NoiseFloor, byte, 0); +module_param(SF_mode, byte, 0); +MODULE_PARM_DESC(debug, "Davicom DM9xxx enable debugging (0-1)"); +MODULE_PARM_DESC(mode, "Davicom DM9xxx: " + "Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA"); + +MODULE_PARM_DESC(SF_mode, "Davicom DM9xxx special function " + "(bit 0: VLAN, bit 1 Flow Control, bit 2: TX pause packet)"); + +/* Description: + * when user used insmod to add module, system invoked init_module() + * to initialize and register. + */ + +static int __init dmfe_init_module(void) +{ + int rc; + + pr_info("%s\n", version); + printed_version = 1; + + DMFE_DBUG(0, "init_module() ", debug); + + if (debug) + dmfe_debug = debug; /* set debug flag */ + if (cr6set) + dmfe_cr6_user_set = cr6set; + + switch(mode) { + case DMFE_10MHF: + case DMFE_100MHF: + case DMFE_10MFD: + case DMFE_100MFD: + case DMFE_1M_HPNA: + dmfe_media_mode = mode; + break; + default:dmfe_media_mode = DMFE_AUTO; + break; + } + + if (HPNA_mode > 4) + HPNA_mode = 0; /* Default: LP/HS */ + if (HPNA_rx_cmd > 1) + HPNA_rx_cmd = 0; /* Default: Ignored remote cmd */ + if (HPNA_tx_cmd > 1) + HPNA_tx_cmd = 0; /* Default: Don't issue remote cmd */ + if (HPNA_NoiseFloor > 15) + HPNA_NoiseFloor = 0; + + rc = pci_register_driver(&dmfe_driver); + if (rc < 0) + return rc; + + return 0; +} + + +/* + * Description: + * when user used rmmod to delete module, system invoked clean_module() + * to un-register all registered services. + */ + +static void __exit dmfe_cleanup_module(void) +{ + DMFE_DBUG(0, "dmfe_clean_module() ", debug); + pci_unregister_driver(&dmfe_driver); +} + +module_init(dmfe_init_module); +module_exit(dmfe_cleanup_module); diff --git a/drivers/net/ethernet/dec/tulip/eeprom.c b/drivers/net/ethernet/dec/tulip/eeprom.c new file mode 100644 index 000000000000..fa5eee925f25 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/eeprom.c @@ -0,0 +1,385 @@ +/* + drivers/net/tulip/eeprom.c + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + Please submit bug reports to http://bugzilla.kernel.org/. +*/ + +#include +#include +#include "tulip.h" +#include +#include + + + +/* Serial EEPROM section. */ +/* The main routine to parse the very complicated SROM structure. + Search www.digital.com for "21X4 SROM" to get details. + This code is very complex, and will require changes to support + additional cards, so I'll be verbose about what is going on. + */ + +/* Known cards that have old-style EEPROMs. */ +static struct eeprom_fixup eeprom_fixups[] __devinitdata = { + {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, + 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, + {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f, + 0x0000, 0x009E, /* 10baseT */ + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0903, 0x006D, /* 100baseTx */ + 0x0905, 0x006D, /* 100baseTx-FD */ }}, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f, + 0x0107, 0x8021, /* 100baseFx */ + 0x0108, 0x8021, /* 100baseFx-FD */ + 0x0100, 0x009E, /* 10baseT */ + 0x0104, 0x009E, /* 10baseT-FD */ + 0x0103, 0x006D, /* 100baseTx */ + 0x0105, 0x006D, /* 100baseTx-FD */ }}, + {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513, + 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ + 0x0000, 0x009E, /* 10baseT */ + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ + 0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}}, + {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F, + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + 0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */ + }}, + {"NetWinder", 0x00, 0x10, 0x57, + /* Default media = MII + * MII block, reset sequence (3) = 0x0821 0x0000 0x0001, capabilities 0x01e1 + */ + { 0x1e00, 0x0000, 0x000b, 0x8f01, 0x0103, 0x0300, 0x0821, 0x000, 0x0001, 0x0000, 0x01e1 } + }, + {"Cobalt Microserver", 0, 0x10, 0xE0, {0x1e00, /* 0 == controller #, 1e == offset */ + 0x0000, /* 0 == high offset, 0 == gap */ + 0x0800, /* Default Autoselect */ + 0x8001, /* 1 leaf, extended type, bogus len */ + 0x0003, /* Type 3 (MII), PHY #0 */ + 0x0400, /* 0 init instr, 4 reset instr */ + 0x0801, /* Set control mode, GP0 output */ + 0x0000, /* Drive GP0 Low (RST is active low) */ + 0x0800, /* control mode, GP0 input (undriven) */ + 0x0000, /* clear control mode */ + 0x7800, /* 100TX FDX + HDX, 10bT FDX + HDX */ + 0x01e0, /* Advertise all above */ + 0x5000, /* FDX all above */ + 0x1800, /* Set fast TTM in 100bt modes */ + 0x0000, /* PHY cannot be unplugged */ + }}, + {NULL}}; + + +static const char *block_name[] __devinitdata = { + "21140 non-MII", + "21140 MII PHY", + "21142 Serial PHY", + "21142 MII PHY", + "21143 SYM PHY", + "21143 reset method" +}; + + +/** + * tulip_build_fake_mediatable - Build a fake mediatable entry. + * @tp: Ptr to the tulip private data. + * + * Some cards like the 3x5 HSC cards (J3514A) do not have a standard + * srom and can not be handled under the fixup routine. These cards + * still need a valid mediatable entry for correct csr12 setup and + * mii handling. + * + * Since this is currently a parisc-linux specific function, the + * #ifdef __hppa__ should completely optimize this function away for + * non-parisc hardware. + */ +static void __devinit tulip_build_fake_mediatable(struct tulip_private *tp) +{ +#ifdef CONFIG_GSC + if (tp->flags & NEEDS_FAKE_MEDIA_TABLE) { + static unsigned char leafdata[] = + { 0x01, /* phy number */ + 0x02, /* gpr setup sequence length */ + 0x02, 0x00, /* gpr setup sequence */ + 0x02, /* phy reset sequence length */ + 0x01, 0x00, /* phy reset sequence */ + 0x00, 0x78, /* media capabilities */ + 0x00, 0xe0, /* nway advertisement */ + 0x00, 0x05, /* fdx bit map */ + 0x00, 0x06 /* ttm bit map */ + }; + + tp->mtable = kmalloc(sizeof(struct mediatable) + + sizeof(struct medialeaf), GFP_KERNEL); + + if (tp->mtable == NULL) + return; /* Horrible, impossible failure. */ + + tp->mtable->defaultmedia = 0x800; + tp->mtable->leafcount = 1; + tp->mtable->csr12dir = 0x3f; /* inputs on bit7 for hsc-pci, bit6 for pci-fx */ + tp->mtable->has_nonmii = 0; + tp->mtable->has_reset = 0; + tp->mtable->has_mii = 1; + tp->mtable->csr15dir = tp->mtable->csr15val = 0; + tp->mtable->mleaf[0].type = 1; + tp->mtable->mleaf[0].media = 11; + tp->mtable->mleaf[0].leafdata = &leafdata[0]; + tp->flags |= HAS_PHY_IRQ; + tp->csr12_shadow = -1; + } +#endif +} + +void __devinit tulip_parse_eeprom(struct net_device *dev) +{ + /* + dev is not registered at this point, so logging messages can't + use dev_ or netdev_ but dev->name is good via a + hack in the caller + */ + + /* The last media info list parsed, for multiport boards. */ + static struct mediatable *last_mediatable; + static unsigned char *last_ee_data; + static int controller_index; + struct tulip_private *tp = netdev_priv(dev); + unsigned char *ee_data = tp->eeprom; + int i; + + tp->mtable = NULL; + /* Detect an old-style (SA only) EEPROM layout: + memcmp(eedata, eedata+16, 8). */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + break; + if (i >= 8) { + if (ee_data[0] == 0xff) { + if (last_mediatable) { + controller_index++; + pr_info("%s: Controller %d of multiport board\n", + dev->name, controller_index); + tp->mtable = last_mediatable; + ee_data = last_ee_data; + goto subsequent_board; + } else + pr_info("%s: Missing EEPROM, this interface may not work correctly!\n", + dev->name); + return; + } + /* Do a fix-up based on the vendor half of the station address prefix. */ + for (i = 0; eeprom_fixups[i].name; i++) { + if (dev->dev_addr[0] == eeprom_fixups[i].addr0 && + dev->dev_addr[1] == eeprom_fixups[i].addr1 && + dev->dev_addr[2] == eeprom_fixups[i].addr2) { + if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) + i++; /* An Accton EN1207, not an outlaw Maxtech. */ + memcpy(ee_data + 26, eeprom_fixups[i].newtable, + sizeof(eeprom_fixups[i].newtable)); + pr_info("%s: Old format EEPROM on '%s' board. Using substitute media control info\n", + dev->name, eeprom_fixups[i].name); + break; + } + } + if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ + pr_info("%s: Old style EEPROM with no media selection information\n", + dev->name); + return; + } + } + + controller_index = 0; + if (ee_data[19] > 1) { /* Multiport board. */ + last_ee_data = ee_data; + } +subsequent_board: + + if (ee_data[27] == 0) { /* No valid media table. */ + tulip_build_fake_mediatable(tp); + } else { + unsigned char *p = (void *)ee_data + ee_data[27]; + unsigned char csr12dir = 0; + int count, new_advertise = 0; + struct mediatable *mtable; + u16 media = get_u16(p); + + p += 2; + if (tp->flags & CSR12_IN_SROM) + csr12dir = *p++; + count = *p++; + + /* there is no phy information, don't even try to build mtable */ + if (count == 0) { + if (tulip_debug > 0) + pr_warn("%s: no phy info, aborting mtable build\n", + dev->name); + return; + } + + mtable = kmalloc(sizeof(struct mediatable) + + count * sizeof(struct medialeaf), + GFP_KERNEL); + if (mtable == NULL) + return; /* Horrible, impossible failure. */ + last_mediatable = tp->mtable = mtable; + mtable->defaultmedia = media; + mtable->leafcount = count; + mtable->csr12dir = csr12dir; + mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0; + mtable->csr15dir = mtable->csr15val = 0; + + pr_info("%s: EEPROM default media type %s\n", + dev->name, + media & 0x0800 ? "Autosense" + : medianame[media & MEDIA_MASK]); + for (i = 0; i < count; i++) { + struct medialeaf *leaf = &mtable->mleaf[i]; + + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ + leaf->type = 0; + leaf->media = p[0] & 0x3f; + leaf->leafdata = p; + if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */ + mtable->has_mii = 1; + p += 4; + } else { + leaf->type = p[1]; + if (p[1] == 0x05) { + mtable->has_reset = i; + leaf->media = p[2] & 0x0f; + } else if (tp->chip_id == DM910X && p[1] == 0x80) { + /* Hack to ignore Davicom delay period block */ + mtable->leafcount--; + count--; + i--; + leaf->leafdata = p + 2; + p += (p[0] & 0x3f) + 1; + continue; + } else if (p[1] & 1) { + int gpr_len, reset_len; + + mtable->has_mii = 1; + leaf->media = 11; + gpr_len=p[3]*2; + reset_len=p[4+gpr_len]*2; + new_advertise |= get_u16(&p[7+gpr_len+reset_len]); + } else { + mtable->has_nonmii = 1; + leaf->media = p[2] & MEDIA_MASK; + /* Davicom's media number for 100BaseTX is strange */ + if (tp->chip_id == DM910X && leaf->media == 1) + leaf->media = 3; + switch (leaf->media) { + case 0: new_advertise |= 0x0020; break; + case 4: new_advertise |= 0x0040; break; + case 3: new_advertise |= 0x0080; break; + case 5: new_advertise |= 0x0100; break; + case 6: new_advertise |= 0x0200; break; + } + if (p[1] == 2 && leaf->media == 0) { + if (p[2] & 0x40) { + u32 base15 = get_unaligned((u16*)&p[7]); + mtable->csr15dir = + (get_unaligned((u16*)&p[9])<<16) + base15; + mtable->csr15val = + (get_unaligned((u16*)&p[11])<<16) + base15; + } else { + mtable->csr15dir = get_unaligned((u16*)&p[3])<<16; + mtable->csr15val = get_unaligned((u16*)&p[5])<<16; + } + } + } + leaf->leafdata = p + 2; + p += (p[0] & 0x3f) + 1; + } + if (tulip_debug > 1 && leaf->media == 11) { + unsigned char *bp = leaf->leafdata; + pr_info("%s: MII interface PHY %d, setup/reset sequences %d/%d long, capabilities %02x %02x\n", + dev->name, + bp[0], bp[1], bp[2 + bp[1]*2], + bp[5 + bp[2 + bp[1]*2]*2], + bp[4 + bp[2 + bp[1]*2]*2]); + } + pr_info("%s: Index #%d - Media %s (#%d) described by a %s (%d) block\n", + dev->name, + i, medianame[leaf->media & 15], leaf->media, + leaf->type < ARRAY_SIZE(block_name) ? block_name[leaf->type] : "", + leaf->type); + } + if (new_advertise) + tp->sym_advertise = new_advertise; + } +} +/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ +#define EE_CS 0x01 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* Data from the Tulip to EEPROM. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* Data from the EEPROM chip. */ +#define EE_ENB (0x4800 | EE_CS) + +/* Delay between EEPROM clock transitions. + Even at 33Mhz current PCI implementations don't overrun the EEPROM clock. + We add a bus turn-around to insure that this remains true. */ +#define eeprom_delay() ioread32(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_READ_CMD (6) + +/* Note: this routine returns extra data bits for size detection. */ +int __devinit tulip_read_eeprom(struct net_device *dev, int location, int addr_len) +{ + int i; + unsigned retval = 0; + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ee_addr = tp->base_addr + CSR9; + int read_cmd = location | (EE_READ_CMD << addr_len); + + /* If location is past the end of what we can address, don't + * read some other location (ie truncate). Just return zero. + */ + if (location > (1 << addr_len) - 1) + return 0; + + iowrite32(EE_ENB & ~EE_CS, ee_addr); + iowrite32(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + iowrite32(EE_ENB | dataval, ee_addr); + eeprom_delay(); + iowrite32(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((ioread32(ee_addr) & EE_DATA_READ) ? 1 : 0); + } + iowrite32(EE_ENB, ee_addr); + eeprom_delay(); + + for (i = 16; i > 0; i--) { + iowrite32(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((ioread32(ee_addr) & EE_DATA_READ) ? 1 : 0); + iowrite32(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + iowrite32(EE_ENB & ~EE_CS, ee_addr); + return (tp->flags & HAS_SWAPPED_SEEPROM) ? swab16(retval) : retval; +} + diff --git a/drivers/net/ethernet/dec/tulip/interrupt.c b/drivers/net/ethernet/dec/tulip/interrupt.c new file mode 100644 index 000000000000..5350d753e0ff --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/interrupt.c @@ -0,0 +1,811 @@ +/* + drivers/net/tulip/interrupt.c + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + Please submit bugs to http://bugzilla.kernel.org/ . + +*/ + +#include +#include "tulip.h" +#include + +int tulip_rx_copybreak; +unsigned int tulip_max_interrupt_work; + +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION +#define MIT_SIZE 15 +#define MIT_TABLE 15 /* We use 0 or max */ + +static unsigned int mit_table[MIT_SIZE+1] = +{ + /* CRS11 21143 hardware Mitigation Control Interrupt + We use only RX mitigation we other techniques for + TX intr. mitigation. + + 31 Cycle Size (timer control) + 30:27 TX timer in 16 * Cycle size + 26:24 TX No pkts before Int. + 23:20 RX timer in Cycle size + 19:17 RX No pkts before Int. + 16 Continues Mode (CM) + */ + + 0x0, /* IM disabled */ + 0x80150000, /* RX time = 1, RX pkts = 2, CM = 1 */ + 0x80150000, + 0x80270000, + 0x80370000, + 0x80490000, + 0x80590000, + 0x80690000, + 0x807B0000, + 0x808B0000, + 0x809D0000, + 0x80AD0000, + 0x80BD0000, + 0x80CF0000, + 0x80DF0000, +// 0x80FF0000 /* RX time = 16, RX pkts = 7, CM = 1 */ + 0x80F10000 /* RX time = 16, RX pkts = 0, CM = 1 */ +}; +#endif + + +int tulip_refill_rx(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int entry; + int refilled = 0; + + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_buffers[entry].skb == NULL) { + struct sk_buff *skb; + dma_addr_t mapping; + + skb = tp->rx_buffers[entry].skb = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) + break; + + mapping = pci_map_single(tp->pdev, skb->data, PKT_BUF_SZ, + PCI_DMA_FROMDEVICE); + tp->rx_buffers[entry].mapping = mapping; + + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[entry].buffer1 = cpu_to_le32(mapping); + refilled++; + } + tp->rx_ring[entry].status = cpu_to_le32(DescOwned); + } + if(tp->chip_id == LC82C168) { + if(((ioread32(tp->base_addr + CSR5)>>17)&0x07) == 4) { + /* Rx stopped due to out of buffers, + * restart it + */ + iowrite32(0x01, tp->base_addr + CSR2); + } + } + return refilled; +} + +#ifdef CONFIG_TULIP_NAPI + +void oom_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = netdev_priv(dev); + napi_schedule(&tp->napi); +} + +int tulip_poll(struct napi_struct *napi, int budget) +{ + struct tulip_private *tp = container_of(napi, struct tulip_private, napi); + struct net_device *dev = tp->dev; + int entry = tp->cur_rx % RX_RING_SIZE; + int work_done = 0; +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION + int received = 0; +#endif + +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION + +/* that one buffer is needed for mit activation; or might be a + bug in the ring buffer code; check later -- JHS*/ + + if (budget >=RX_RING_SIZE) budget--; +#endif + + if (tulip_debug > 4) + netdev_dbg(dev, " In tulip_rx(), entry %d %08x\n", + entry, tp->rx_ring[entry].status); + + do { + if (ioread32(tp->base_addr + CSR5) == 0xffffffff) { + netdev_dbg(dev, " In tulip_poll(), hardware disappeared\n"); + break; + } + /* Acknowledge current RX interrupt sources. */ + iowrite32((RxIntr | RxNoBuf), tp->base_addr + CSR5); + + + /* If we own the next entry, it is a new packet. Send it up. */ + while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { + s32 status = le32_to_cpu(tp->rx_ring[entry].status); + short pkt_len; + + if (tp->dirty_rx + RX_RING_SIZE == tp->cur_rx) + break; + + if (tulip_debug > 5) + netdev_dbg(dev, "In tulip_rx(), entry %d %08x\n", + entry, status); + + if (++work_done >= budget) + goto not_done; + + /* + * Omit the four octet CRC from the length. + * (May not be considered valid until we have + * checked status for RxLengthOver2047 bits) + */ + pkt_len = ((status >> 16) & 0x7ff) - 4; + + /* + * Maximum pkt_len is 1518 (1514 + vlan header) + * Anything higher than this is always invalid + * regardless of RxLengthOver2047 bits + */ + + if ((status & (RxLengthOver2047 | + RxDescCRCError | + RxDescCollisionSeen | + RxDescRunt | + RxDescDescErr | + RxWholePkt)) != RxWholePkt || + pkt_len > 1518) { + if ((status & (RxLengthOver2047 | + RxWholePkt)) != RxWholePkt) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + dev_warn(&dev->dev, + "Oversized Ethernet frame spanned multiple buffers, status %08x!\n", + status); + dev->stats.rx_length_errors++; + } + } else { + /* There was a fatal error. */ + if (tulip_debug > 2) + netdev_dbg(dev, "Receive error, Rx status %08x\n", + status); + dev->stats.rx_errors++; /* end of a packet.*/ + if (pkt_len > 1518 || + (status & RxDescRunt)) + dev->stats.rx_length_errors++; + + if (status & 0x0004) + dev->stats.rx_frame_errors++; + if (status & 0x0002) + dev->stats.rx_crc_errors++; + if (status & 0x0001) + dev->stats.rx_fifo_errors++; + } + } else { + struct sk_buff *skb; + + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < tulip_rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single_for_cpu(tp->pdev, + tp->rx_buffers[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); +#if ! defined(__alpha__) + skb_copy_to_linear_data(skb, tp->rx_buffers[entry].skb->data, + pkt_len); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), + tp->rx_buffers[entry].skb->data, + pkt_len); +#endif + pci_dma_sync_single_for_device(tp->pdev, + tp->rx_buffers[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); + } else { /* Pass up the skb already on the Rx ring. */ + char *temp = skb_put(skb = tp->rx_buffers[entry].skb, + pkt_len); + +#ifndef final_version + if (tp->rx_buffers[entry].mapping != + le32_to_cpu(tp->rx_ring[entry].buffer1)) { + dev_err(&dev->dev, + "Internal fault: The skbuff addresses do not match in tulip_rx: %08x vs. %08llx %p / %p\n", + le32_to_cpu(tp->rx_ring[entry].buffer1), + (unsigned long long)tp->rx_buffers[entry].mapping, + skb->head, temp); + } +#endif + + pci_unmap_single(tp->pdev, tp->rx_buffers[entry].mapping, + PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + + tp->rx_buffers[entry].skb = NULL; + tp->rx_buffers[entry].mapping = 0; + } + skb->protocol = eth_type_trans(skb, dev); + + netif_receive_skb(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION + received++; +#endif + + entry = (++tp->cur_rx) % RX_RING_SIZE; + if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/4) + tulip_refill_rx(dev); + + } + + /* New ack strategy... irq does not ack Rx any longer + hopefully this helps */ + + /* Really bad things can happen here... If new packet arrives + * and an irq arrives (tx or just due to occasionally unset + * mask), it will be acked by irq handler, but new thread + * is not scheduled. It is major hole in design. + * No idea how to fix this if "playing with fire" will fail + * tomorrow (night 011029). If it will not fail, we won + * finally: amount of IO did not increase at all. */ + } while ((ioread32(tp->base_addr + CSR5) & RxIntr)); + + #ifdef CONFIG_TULIP_NAPI_HW_MITIGATION + + /* We use this simplistic scheme for IM. It's proven by + real life installations. We can have IM enabled + continuesly but this would cause unnecessary latency. + Unfortunely we can't use all the NET_RX_* feedback here. + This would turn on IM for devices that is not contributing + to backlog congestion with unnecessary latency. + + We monitor the device RX-ring and have: + + HW Interrupt Mitigation either ON or OFF. + + ON: More then 1 pkt received (per intr.) OR we are dropping + OFF: Only 1 pkt received + + Note. We only use min and max (0, 15) settings from mit_table */ + + + if( tp->flags & HAS_INTR_MITIGATION) { + if( received > 1 ) { + if( ! tp->mit_on ) { + tp->mit_on = 1; + iowrite32(mit_table[MIT_TABLE], tp->base_addr + CSR11); + } + } + else { + if( tp->mit_on ) { + tp->mit_on = 0; + iowrite32(0, tp->base_addr + CSR11); + } + } + } + +#endif /* CONFIG_TULIP_NAPI_HW_MITIGATION */ + + tulip_refill_rx(dev); + + /* If RX ring is not full we are out of memory. */ + if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) + goto oom; + + /* Remove us from polling list and enable RX intr. */ + + napi_complete(napi); + iowrite32(tulip_tbl[tp->chip_id].valid_intrs, tp->base_addr+CSR7); + + /* The last op happens after poll completion. Which means the following: + * 1. it can race with disabling irqs in irq handler + * 2. it can race with dise/enabling irqs in other poll threads + * 3. if an irq raised after beginning loop, it will be immediately + * triggered here. + * + * Summarizing: the logic results in some redundant irqs both + * due to races in masking and due to too late acking of already + * processed irqs. But it must not result in losing events. + */ + + return work_done; + + not_done: + if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/2 || + tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) + tulip_refill_rx(dev); + + if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) + goto oom; + + return work_done; + + oom: /* Executed with RX ints disabled */ + + /* Start timer, stop polling, but do not enable rx interrupts. */ + mod_timer(&tp->oom_timer, jiffies+1); + + /* Think: timer_pending() was an explicit signature of bug. + * Timer can be pending now but fired and completed + * before we did napi_complete(). See? We would lose it. */ + + /* remove ourselves from the polling list */ + napi_complete(napi); + + return work_done; +} + +#else /* CONFIG_TULIP_NAPI */ + +static int tulip_rx(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int entry = tp->cur_rx % RX_RING_SIZE; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int received = 0; + + if (tulip_debug > 4) + netdev_dbg(dev, "In tulip_rx(), entry %d %08x\n", + entry, tp->rx_ring[entry].status); + /* If we own the next entry, it is a new packet. Send it up. */ + while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { + s32 status = le32_to_cpu(tp->rx_ring[entry].status); + short pkt_len; + + if (tulip_debug > 5) + netdev_dbg(dev, "In tulip_rx(), entry %d %08x\n", + entry, status); + if (--rx_work_limit < 0) + break; + + /* + Omit the four octet CRC from the length. + (May not be considered valid until we have + checked status for RxLengthOver2047 bits) + */ + pkt_len = ((status >> 16) & 0x7ff) - 4; + /* + Maximum pkt_len is 1518 (1514 + vlan header) + Anything higher than this is always invalid + regardless of RxLengthOver2047 bits + */ + + if ((status & (RxLengthOver2047 | + RxDescCRCError | + RxDescCollisionSeen | + RxDescRunt | + RxDescDescErr | + RxWholePkt)) != RxWholePkt || + pkt_len > 1518) { + if ((status & (RxLengthOver2047 | + RxWholePkt)) != RxWholePkt) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + netdev_warn(dev, + "Oversized Ethernet frame spanned multiple buffers, status %08x!\n", + status); + dev->stats.rx_length_errors++; + } + } else { + /* There was a fatal error. */ + if (tulip_debug > 2) + netdev_dbg(dev, "Receive error, Rx status %08x\n", + status); + dev->stats.rx_errors++; /* end of a packet.*/ + if (pkt_len > 1518 || + (status & RxDescRunt)) + dev->stats.rx_length_errors++; + if (status & 0x0004) + dev->stats.rx_frame_errors++; + if (status & 0x0002) + dev->stats.rx_crc_errors++; + if (status & 0x0001) + dev->stats.rx_fifo_errors++; + } + } else { + struct sk_buff *skb; + + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < tulip_rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single_for_cpu(tp->pdev, + tp->rx_buffers[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); +#if ! defined(__alpha__) + skb_copy_to_linear_data(skb, tp->rx_buffers[entry].skb->data, + pkt_len); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), + tp->rx_buffers[entry].skb->data, + pkt_len); +#endif + pci_dma_sync_single_for_device(tp->pdev, + tp->rx_buffers[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); + } else { /* Pass up the skb already on the Rx ring. */ + char *temp = skb_put(skb = tp->rx_buffers[entry].skb, + pkt_len); + +#ifndef final_version + if (tp->rx_buffers[entry].mapping != + le32_to_cpu(tp->rx_ring[entry].buffer1)) { + dev_err(&dev->dev, + "Internal fault: The skbuff addresses do not match in tulip_rx: %08x vs. %Lx %p / %p\n", + le32_to_cpu(tp->rx_ring[entry].buffer1), + (long long)tp->rx_buffers[entry].mapping, + skb->head, temp); + } +#endif + + pci_unmap_single(tp->pdev, tp->rx_buffers[entry].mapping, + PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + + tp->rx_buffers[entry].skb = NULL; + tp->rx_buffers[entry].mapping = 0; + } + skb->protocol = eth_type_trans(skb, dev); + + netif_rx(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + received++; + entry = (++tp->cur_rx) % RX_RING_SIZE; + } + return received; +} +#endif /* CONFIG_TULIP_NAPI */ + +static inline unsigned int phy_interrupt (struct net_device *dev) +{ +#ifdef __hppa__ + struct tulip_private *tp = netdev_priv(dev); + int csr12 = ioread32(tp->base_addr + CSR12) & 0xff; + + if (csr12 != tp->csr12_shadow) { + /* ack interrupt */ + iowrite32(csr12 | 0x02, tp->base_addr + CSR12); + tp->csr12_shadow = csr12; + /* do link change stuff */ + spin_lock(&tp->lock); + tulip_check_duplex(dev); + spin_unlock(&tp->lock); + /* clear irq ack bit */ + iowrite32(csr12 & ~0x02, tp->base_addr + CSR12); + + return 1; + } +#endif + + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +irqreturn_t tulip_interrupt(int irq, void *dev_instance) +{ + struct net_device *dev = (struct net_device *)dev_instance; + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int csr5; + int missed; + int rx = 0; + int tx = 0; + int oi = 0; + int maxrx = RX_RING_SIZE; + int maxtx = TX_RING_SIZE; + int maxoi = TX_RING_SIZE; +#ifdef CONFIG_TULIP_NAPI + int rxd = 0; +#else + int entry; +#endif + unsigned int work_count = tulip_max_interrupt_work; + unsigned int handled = 0; + + /* Let's see whether the interrupt really is for us */ + csr5 = ioread32(ioaddr + CSR5); + + if (tp->flags & HAS_PHY_IRQ) + handled = phy_interrupt (dev); + + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) + return IRQ_RETVAL(handled); + + tp->nir++; + + do { + +#ifdef CONFIG_TULIP_NAPI + + if (!rxd && (csr5 & (RxIntr | RxNoBuf))) { + rxd++; + /* Mask RX intrs and add the device to poll list. */ + iowrite32(tulip_tbl[tp->chip_id].valid_intrs&~RxPollInt, ioaddr + CSR7); + napi_schedule(&tp->napi); + + if (!(csr5&~(AbnormalIntr|NormalIntr|RxPollInt|TPLnkPass))) + break; + } + + /* Acknowledge the interrupt sources we handle here ASAP + the poll function does Rx and RxNoBuf acking */ + + iowrite32(csr5 & 0x0001ff3f, ioaddr + CSR5); + +#else + /* Acknowledge all of the current interrupt sources ASAP. */ + iowrite32(csr5 & 0x0001ffff, ioaddr + CSR5); + + + if (csr5 & (RxIntr | RxNoBuf)) { + rx += tulip_rx(dev); + tulip_refill_rx(dev); + } + +#endif /* CONFIG_TULIP_NAPI */ + + if (tulip_debug > 4) + netdev_dbg(dev, "interrupt csr5=%#8.8x new csr5=%#8.8x\n", + csr5, ioread32(ioaddr + CSR5)); + + + if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) { + unsigned int dirty_tx; + + spin_lock(&tp->lock); + + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = le32_to_cpu(tp->tx_ring[entry].status); + + if (status < 0) + break; /* It still has not been Txed */ + + /* Check for Rx filter setup frames. */ + if (tp->tx_buffers[entry].skb == NULL) { + /* test because dummy frames not mapped */ + if (tp->tx_buffers[entry].mapping) + pci_unmap_single(tp->pdev, + tp->tx_buffers[entry].mapping, + sizeof(tp->setup_frame), + PCI_DMA_TODEVICE); + continue; + } + + if (status & 0x8000) { + /* There was an major error, log it. */ +#ifndef final_version + if (tulip_debug > 1) + netdev_dbg(dev, "Transmit error, Tx status %08x\n", + status); +#endif + dev->stats.tx_errors++; + if (status & 0x4104) + dev->stats.tx_aborted_errors++; + if (status & 0x0C00) + dev->stats.tx_carrier_errors++; + if (status & 0x0200) + dev->stats.tx_window_errors++; + if (status & 0x0002) + dev->stats.tx_fifo_errors++; + if ((status & 0x0080) && tp->full_duplex == 0) + dev->stats.tx_heartbeat_errors++; + } else { + dev->stats.tx_bytes += + tp->tx_buffers[entry].skb->len; + dev->stats.collisions += (status >> 3) & 15; + dev->stats.tx_packets++; + } + + pci_unmap_single(tp->pdev, tp->tx_buffers[entry].mapping, + tp->tx_buffers[entry].skb->len, + PCI_DMA_TODEVICE); + + /* Free the original skb. */ + dev_kfree_skb_irq(tp->tx_buffers[entry].skb); + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = 0; + tx++; + } + +#ifndef final_version + if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { + dev_err(&dev->dev, + "Out-of-sync dirty pointer, %d vs. %d\n", + dirty_tx, tp->cur_tx); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) + netif_wake_queue(dev); + + tp->dirty_tx = dirty_tx; + if (csr5 & TxDied) { + if (tulip_debug > 2) + dev_warn(&dev->dev, + "The transmitter stopped. CSR5 is %x, CSR6 %x, new CSR6 %x\n", + csr5, ioread32(ioaddr + CSR6), + tp->csr6); + tulip_restart_rxtx(tp); + } + spin_unlock(&tp->lock); + } + + /* Log errors. */ + if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 == 0xffffffff) + break; + if (csr5 & TxJabber) + dev->stats.tx_errors++; + if (csr5 & TxFIFOUnderflow) { + if ((tp->csr6 & 0xC000) != 0xC000) + tp->csr6 += 0x4000; /* Bump up the Tx threshold */ + else + tp->csr6 |= 0x00200000; /* Store-n-forward. */ + /* Restart the transmit process. */ + tulip_restart_rxtx(tp); + iowrite32(0, ioaddr + CSR1); + } + if (csr5 & (RxDied | RxNoBuf)) { + if (tp->flags & COMET_MAC_ADDR) { + iowrite32(tp->mc_filter[0], ioaddr + 0xAC); + iowrite32(tp->mc_filter[1], ioaddr + 0xB0); + } + } + if (csr5 & RxDied) { /* Missed a Rx frame. */ + dev->stats.rx_missed_errors += ioread32(ioaddr + CSR8) & 0xffff; + dev->stats.rx_errors++; + tulip_start_rxtx(tp); + } + /* + * NB: t21142_lnk_change() does a del_timer_sync(), so be careful if this + * call is ever done under the spinlock + */ + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { + if (tp->link_change) + (tp->link_change)(dev, csr5); + } + if (csr5 & SystemError) { + int error = (csr5 >> 23) & 7; + /* oops, we hit a PCI error. The code produced corresponds + * to the reason: + * 0 - parity error + * 1 - master abort + * 2 - target abort + * Note that on parity error, we should do a software reset + * of the chip to get it back into a sane state (according + * to the 21142/3 docs that is). + * -- rmk + */ + dev_err(&dev->dev, + "(%lu) System Error occurred (%d)\n", + tp->nir, error); + } + /* Clear all error sources, included undocumented ones! */ + iowrite32(0x0800f7ba, ioaddr + CSR5); + oi++; + } + if (csr5 & TimerInt) { + + if (tulip_debug > 2) + dev_err(&dev->dev, + "Re-enabling interrupts, %08x\n", + csr5); + iowrite32(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + tp->ttimer = 0; + oi++; + } + if (tx > maxtx || rx > maxrx || oi > maxoi) { + if (tulip_debug > 1) + dev_warn(&dev->dev, "Too much work during an interrupt, csr5=0x%08x. (%lu) (%d,%d,%d)\n", + csr5, tp->nir, tx, rx, oi); + + /* Acknowledge all interrupt sources. */ + iowrite32(0x8001ffff, ioaddr + CSR5); + if (tp->flags & HAS_INTR_MITIGATION) { + /* Josip Loncaric at ICASE did extensive experimentation + to develop a good interrupt mitigation setting.*/ + iowrite32(0x8b240000, ioaddr + CSR11); + } else if (tp->chip_id == LC82C168) { + /* the LC82C168 doesn't have a hw timer.*/ + iowrite32(0x00, ioaddr + CSR7); + mod_timer(&tp->timer, RUN_AT(HZ/50)); + } else { + /* Mask all interrupting sources, set timer to + re-enable. */ + iowrite32(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt, ioaddr + CSR7); + iowrite32(0x0012, ioaddr + CSR11); + } + break; + } + + work_count--; + if (work_count == 0) + break; + + csr5 = ioread32(ioaddr + CSR5); + +#ifdef CONFIG_TULIP_NAPI + if (rxd) + csr5 &= ~RxPollInt; + } while ((csr5 & (TxNoBuf | + TxDied | + TxIntr | + TimerInt | + /* Abnormal intr. */ + RxDied | + TxFIFOUnderflow | + TxJabber | + TPLnkFail | + SystemError )) != 0); +#else + } while ((csr5 & (NormalIntr|AbnormalIntr)) != 0); + + tulip_refill_rx(dev); + + /* check if the card is in suspend mode */ + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_buffers[entry].skb == NULL) { + if (tulip_debug > 1) + dev_warn(&dev->dev, + "in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n", + tp->nir, tp->cur_rx, tp->ttimer, rx); + if (tp->chip_id == LC82C168) { + iowrite32(0x00, ioaddr + CSR7); + mod_timer(&tp->timer, RUN_AT(HZ/50)); + } else { + if (tp->ttimer == 0 || (ioread32(ioaddr + CSR11) & 0xffff) == 0) { + if (tulip_debug > 1) + dev_warn(&dev->dev, + "in rx suspend mode: (%lu) set timer\n", + tp->nir); + iowrite32(tulip_tbl[tp->chip_id].valid_intrs | TimerInt, + ioaddr + CSR7); + iowrite32(TimerInt, ioaddr + CSR5); + iowrite32(12, ioaddr + CSR11); + tp->ttimer = 1; + } + } + } +#endif /* CONFIG_TULIP_NAPI */ + + if ((missed = ioread32(ioaddr + CSR8) & 0x1ffff)) { + dev->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed; + } + + if (tulip_debug > 4) + netdev_dbg(dev, "exiting interrupt, csr5=%#04x\n", + ioread32(ioaddr + CSR5)); + + return IRQ_HANDLED; +} diff --git a/drivers/net/ethernet/dec/tulip/media.c b/drivers/net/ethernet/dec/tulip/media.c new file mode 100644 index 000000000000..4bd13922875d --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/media.c @@ -0,0 +1,556 @@ +/* + drivers/net/tulip/media.c + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + + Please submit bugs to http://bugzilla.kernel.org/ . +*/ + +#include +#include +#include +#include +#include +#include "tulip.h" + + +/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues or future 66Mhz PCI. */ +#define mdio_delay() ioread32(mdio_addr) + +/* Read and write the MII registers using software-generated serial + MDIO protocol. It is just different enough from the EEPROM protocol + to not share code. The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ +#define MDIO_ENB_IN 0x40000 +#define MDIO_DATA_READ 0x80000 + +static const unsigned char comet_miireg2offset[32] = { + 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8, 0xCC, 0, 0,0,0,0, 0,0,0,0, + 0,0xD0,0,0, 0,0,0,0, 0,0,0,0, 0, 0xD4, 0xD8, 0xDC, }; + + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. + See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management functions") + or DP83840A data sheet for more details. + */ + +int tulip_mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct tulip_private *tp = netdev_priv(dev); + int i; + int read_cmd = (0xf6 << 10) | ((phy_id & 0x1f) << 5) | location; + int retval = 0; + void __iomem *ioaddr = tp->base_addr; + void __iomem *mdio_addr = ioaddr + CSR9; + unsigned long flags; + + if (location & ~0x1f) + return 0xffff; + + if (tp->chip_id == COMET && phy_id == 30) { + if (comet_miireg2offset[location]) + return ioread32(ioaddr + comet_miireg2offset[location]); + return 0xffff; + } + + spin_lock_irqsave(&tp->mii_lock, flags); + if (tp->chip_id == LC82C168) { + iowrite32(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + ioread32(ioaddr + 0xA0); + ioread32(ioaddr + 0xA0); + for (i = 1000; i >= 0; --i) { + barrier(); + if ( ! ((retval = ioread32(ioaddr + 0xA0)) & 0x80000000)) + break; + } + spin_unlock_irqrestore(&tp->mii_lock, flags); + return retval & 0xffff; + } + + /* Establish sync by sending at least 32 logic ones. */ + for (i = 32; i >= 0; i--) { + iowrite32(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + iowrite32(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + + iowrite32(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + iowrite32(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + iowrite32(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((ioread32(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + iowrite32(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + + spin_unlock_irqrestore(&tp->mii_lock, flags); + return (retval>>1) & 0xffff; +} + +void tulip_mdio_write(struct net_device *dev, int phy_id, int location, int val) +{ + struct tulip_private *tp = netdev_priv(dev); + int i; + int cmd = (0x5002 << 16) | ((phy_id & 0x1f) << 23) | (location<<18) | (val & 0xffff); + void __iomem *ioaddr = tp->base_addr; + void __iomem *mdio_addr = ioaddr + CSR9; + unsigned long flags; + + if (location & ~0x1f) + return; + + if (tp->chip_id == COMET && phy_id == 30) { + if (comet_miireg2offset[location]) + iowrite32(val, ioaddr + comet_miireg2offset[location]); + return; + } + + spin_lock_irqsave(&tp->mii_lock, flags); + if (tp->chip_id == LC82C168) { + iowrite32(cmd, ioaddr + 0xA0); + for (i = 1000; i >= 0; --i) { + barrier(); + if ( ! (ioread32(ioaddr + 0xA0) & 0x80000000)) + break; + } + spin_unlock_irqrestore(&tp->mii_lock, flags); + return; + } + + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + iowrite32(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + iowrite32(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + iowrite32(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + iowrite32(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + iowrite32(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + iowrite32(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + + spin_unlock_irqrestore(&tp->mii_lock, flags); +} + + +/* Set up the transceiver control registers for the selected media type. */ +void tulip_select_media(struct net_device *dev, int startup) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + struct mediatable *mtable = tp->mtable; + u32 new_csr6; + int i; + + if (mtable) { + struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; + unsigned char *p = mleaf->leafdata; + switch (mleaf->type) { + case 0: /* 21140 non-MII xcvr. */ + if (tulip_debug > 1) + netdev_dbg(dev, "Using a 21140 non-MII transceiver with control setting %02x\n", + p[1]); + dev->if_port = p[0]; + if (startup) + iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12); + iowrite32(p[1], ioaddr + CSR12); + new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); + break; + case 2: case 4: { + u16 setup[5]; + u32 csr13val, csr14val, csr15dir, csr15val; + for (i = 0; i < 5; i++) + setup[i] = get_u16(&p[i*2 + 1]); + + dev->if_port = p[0] & MEDIA_MASK; + if (tulip_media_cap[dev->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + + if (startup && mtable->has_reset) { + struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset]; + unsigned char *rst = rleaf->leafdata; + if (tulip_debug > 1) + netdev_dbg(dev, "Resetting the transceiver\n"); + for (i = 0; i < rst[0]; i++) + iowrite32(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15); + } + if (tulip_debug > 1) + netdev_dbg(dev, "21143 non-MII %s transceiver control %04x/%04x\n", + medianame[dev->if_port], + setup[0], setup[1]); + if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ + csr13val = setup[0]; + csr14val = setup[1]; + csr15dir = (setup[3]<<16) | setup[2]; + csr15val = (setup[4]<<16) | setup[2]; + iowrite32(0, ioaddr + CSR13); + iowrite32(csr14val, ioaddr + CSR14); + iowrite32(csr15dir, ioaddr + CSR15); /* Direction */ + iowrite32(csr15val, ioaddr + CSR15); /* Data */ + iowrite32(csr13val, ioaddr + CSR13); + } else { + csr13val = 1; + csr14val = 0; + csr15dir = (setup[0]<<16) | 0x0008; + csr15val = (setup[1]<<16) | 0x0008; + if (dev->if_port <= 4) + csr14val = t21142_csr14[dev->if_port]; + if (startup) { + iowrite32(0, ioaddr + CSR13); + iowrite32(csr14val, ioaddr + CSR14); + } + iowrite32(csr15dir, ioaddr + CSR15); /* Direction */ + iowrite32(csr15val, ioaddr + CSR15); /* Data */ + if (startup) iowrite32(csr13val, ioaddr + CSR13); + } + if (tulip_debug > 1) + netdev_dbg(dev, "Setting CSR15 to %08x/%08x\n", + csr15dir, csr15val); + if (mleaf->type == 4) + new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); + else + new_csr6 = 0x82420000; + break; + } + case 1: case 3: { + int phy_num = p[0]; + int init_length = p[1]; + u16 *misc_info, tmp_info; + + dev->if_port = 11; + new_csr6 = 0x020E0000; + if (mleaf->type == 3) { /* 21142 */ + u16 *init_sequence = (u16*)(p+2); + u16 *reset_sequence = &((u16*)(p+3))[init_length]; + int reset_length = p[2 + init_length*2]; + misc_info = reset_sequence + reset_length; + if (startup) { + int timeout = 10; /* max 1 ms */ + for (i = 0; i < reset_length; i++) + iowrite32(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); + + /* flush posted writes */ + ioread32(ioaddr + CSR15); + + /* Sect 3.10.3 in DP83840A.pdf (p39) */ + udelay(500); + + /* Section 4.2 in DP83840A.pdf (p43) */ + /* and IEEE 802.3 "22.2.4.1.1 Reset" */ + while (timeout-- && + (tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET)) + udelay(100); + } + for (i = 0; i < init_length; i++) + iowrite32(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); + + ioread32(ioaddr + CSR15); /* flush posted writes */ + } else { + u8 *init_sequence = p + 2; + u8 *reset_sequence = p + 3 + init_length; + int reset_length = p[2 + init_length]; + misc_info = (u16*)(reset_sequence + reset_length); + if (startup) { + int timeout = 10; /* max 1 ms */ + iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12); + for (i = 0; i < reset_length; i++) + iowrite32(reset_sequence[i], ioaddr + CSR12); + + /* flush posted writes */ + ioread32(ioaddr + CSR12); + + /* Sect 3.10.3 in DP83840A.pdf (p39) */ + udelay(500); + + /* Section 4.2 in DP83840A.pdf (p43) */ + /* and IEEE 802.3 "22.2.4.1.1 Reset" */ + while (timeout-- && + (tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET)) + udelay(100); + } + for (i = 0; i < init_length; i++) + iowrite32(init_sequence[i], ioaddr + CSR12); + + ioread32(ioaddr + CSR12); /* flush posted writes */ + } + + tmp_info = get_u16(&misc_info[1]); + if (tmp_info) + tp->advertising[phy_num] = tmp_info | 1; + if (tmp_info && startup < 2) { + if (tp->mii_advertise == 0) + tp->mii_advertise = tp->advertising[phy_num]; + if (tulip_debug > 1) + netdev_dbg(dev, " Advertising %04x on MII %d\n", + tp->mii_advertise, + tp->phys[phy_num]); + tulip_mdio_write(dev, tp->phys[phy_num], 4, tp->mii_advertise); + } + break; + } + case 5: case 6: { + u16 setup[5]; + + new_csr6 = 0; /* FIXME */ + + for (i = 0; i < 5; i++) + setup[i] = get_u16(&p[i*2 + 1]); + + if (startup && mtable->has_reset) { + struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset]; + unsigned char *rst = rleaf->leafdata; + if (tulip_debug > 1) + netdev_dbg(dev, "Resetting the transceiver\n"); + for (i = 0; i < rst[0]; i++) + iowrite32(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15); + } + + break; + } + default: + netdev_dbg(dev, " Invalid media table selection %d\n", + mleaf->type); + new_csr6 = 0x020E0000; + } + if (tulip_debug > 1) + netdev_dbg(dev, "Using media type %s, CSR12 is %02x\n", + medianame[dev->if_port], + ioread32(ioaddr + CSR12) & 0xff); + } else if (tp->chip_id == LC82C168) { + if (startup && ! tp->medialock) + dev->if_port = tp->mii_cnt ? 11 : 0; + if (tulip_debug > 1) + netdev_dbg(dev, "PNIC PHY status is %3.3x, media %s\n", + ioread32(ioaddr + 0xB8), + medianame[dev->if_port]); + if (tp->mii_cnt) { + new_csr6 = 0x810C0000; + iowrite32(0x0001, ioaddr + CSR15); + iowrite32(0x0201B07A, ioaddr + 0xB8); + } else if (startup) { + /* Start with 10mbps to do autonegotiation. */ + iowrite32(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + iowrite32(0x0001B078, ioaddr + 0xB8); + iowrite32(0x0201B078, ioaddr + 0xB8); + } else if (dev->if_port == 3 || dev->if_port == 5) { + iowrite32(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + /* Trigger autonegotiation. */ + iowrite32(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8); + } else { + iowrite32(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + iowrite32(0x1F078, ioaddr + 0xB8); + } + } else { /* Unknown chip type with no media table. */ + if (tp->default_port == 0) + dev->if_port = tp->mii_cnt ? 11 : 3; + if (tulip_media_cap[dev->if_port] & MediaIsMII) { + new_csr6 = 0x020E0000; + } else if (tulip_media_cap[dev->if_port] & MediaIsFx) { + new_csr6 = 0x02860000; + } else + new_csr6 = 0x03860000; + if (tulip_debug > 1) + netdev_dbg(dev, "No media description table, assuming %s transceiver, CSR12 %02x\n", + medianame[dev->if_port], + ioread32(ioaddr + CSR12)); + } + + tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); + + mdelay(1); +} + +/* + Check the MII negotiated duplex and change the CSR6 setting if + required. + Return 0 if everything is OK. + Return < 0 if the transceiver is missing or has no link beat. + */ +int tulip_check_duplex(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + unsigned int bmsr, lpa, negotiated, new_csr6; + + bmsr = tulip_mdio_read(dev, tp->phys[0], MII_BMSR); + lpa = tulip_mdio_read(dev, tp->phys[0], MII_LPA); + if (tulip_debug > 1) + dev_info(&dev->dev, "MII status %04x, Link partner report %04x\n", + bmsr, lpa); + if (bmsr == 0xffff) + return -2; + if ((bmsr & BMSR_LSTATUS) == 0) { + int new_bmsr = tulip_mdio_read(dev, tp->phys[0], MII_BMSR); + if ((new_bmsr & BMSR_LSTATUS) == 0) { + if (tulip_debug > 1) + dev_info(&dev->dev, + "No link beat on the MII interface, status %04x\n", + new_bmsr); + return -1; + } + } + negotiated = lpa & tp->advertising[0]; + tp->full_duplex = mii_duplex(tp->full_duplex_lock, negotiated); + + new_csr6 = tp->csr6; + + if (negotiated & LPA_100) new_csr6 &= ~TxThreshold; + else new_csr6 |= TxThreshold; + if (tp->full_duplex) new_csr6 |= FullDuplex; + else new_csr6 &= ~FullDuplex; + + if (new_csr6 != tp->csr6) { + tp->csr6 = new_csr6; + tulip_restart_rxtx(tp); + + if (tulip_debug > 0) + dev_info(&dev->dev, + "Setting %s-duplex based on MII#%d link partner capability of %04x\n", + tp->full_duplex ? "full" : "half", + tp->phys[0], lpa); + return 1; + } + + return 0; +} + +void __devinit tulip_find_mii (struct net_device *dev, int board_idx) +{ + struct tulip_private *tp = netdev_priv(dev); + int phyn, phy_idx = 0; + int mii_reg0; + int mii_advert; + unsigned int to_advert, new_bmcr, ane_switch; + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, + but takes much time. */ + for (phyn = 1; phyn <= 32 && phy_idx < sizeof (tp->phys); phyn++) { + int phy = phyn & 0x1f; + int mii_status = tulip_mdio_read (dev, phy, MII_BMSR); + if ((mii_status & 0x8301) == 0x8001 || + ((mii_status & BMSR_100BASE4) == 0 && + (mii_status & 0x7800) != 0)) { + /* preserve Becker logic, gain indentation level */ + } else { + continue; + } + + mii_reg0 = tulip_mdio_read (dev, phy, MII_BMCR); + mii_advert = tulip_mdio_read (dev, phy, MII_ADVERTISE); + ane_switch = 0; + + /* if not advertising at all, gen an + * advertising value from the capability + * bits in BMSR + */ + if ((mii_advert & ADVERTISE_ALL) == 0) { + unsigned int tmpadv = tulip_mdio_read (dev, phy, MII_BMSR); + mii_advert = ((tmpadv >> 6) & 0x3e0) | 1; + } + + if (tp->mii_advertise) { + tp->advertising[phy_idx] = + to_advert = tp->mii_advertise; + } else if (tp->advertising[phy_idx]) { + to_advert = tp->advertising[phy_idx]; + } else { + tp->advertising[phy_idx] = + tp->mii_advertise = + to_advert = mii_advert; + } + + tp->phys[phy_idx++] = phy; + + pr_info("tulip%d: MII transceiver #%d config %04x status %04x advertising %04x\n", + board_idx, phy, mii_reg0, mii_status, mii_advert); + + /* Fixup for DLink with miswired PHY. */ + if (mii_advert != to_advert) { + pr_debug("tulip%d: Advertising %04x on PHY %d, previously advertising %04x\n", + board_idx, to_advert, phy, mii_advert); + tulip_mdio_write (dev, phy, 4, to_advert); + } + + /* Enable autonegotiation: some boards default to off. */ + if (tp->default_port == 0) { + new_bmcr = mii_reg0 | BMCR_ANENABLE; + if (new_bmcr != mii_reg0) { + new_bmcr |= BMCR_ANRESTART; + ane_switch = 1; + } + } + /* ...or disable nway, if forcing media */ + else { + new_bmcr = mii_reg0 & ~BMCR_ANENABLE; + if (new_bmcr != mii_reg0) + ane_switch = 1; + } + + /* clear out bits we never want at this point */ + new_bmcr &= ~(BMCR_CTST | BMCR_FULLDPLX | BMCR_ISOLATE | + BMCR_PDOWN | BMCR_SPEED100 | BMCR_LOOPBACK | + BMCR_RESET); + + if (tp->full_duplex) + new_bmcr |= BMCR_FULLDPLX; + if (tulip_media_cap[tp->default_port] & MediaIs100) + new_bmcr |= BMCR_SPEED100; + + if (new_bmcr != mii_reg0) { + /* some phys need the ANE switch to + * happen before forced media settings + * will "take." However, we write the + * same value twice in order not to + * confuse the sane phys. + */ + if (ane_switch) { + tulip_mdio_write (dev, phy, MII_BMCR, new_bmcr); + udelay (10); + } + tulip_mdio_write (dev, phy, MII_BMCR, new_bmcr); + } + } + tp->mii_cnt = phy_idx; + if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) { + pr_info("tulip%d: ***WARNING***: No MII transceiver found!\n", + board_idx); + tp->phys[0] = 1; + } +} diff --git a/drivers/net/ethernet/dec/tulip/pnic.c b/drivers/net/ethernet/dec/tulip/pnic.c new file mode 100644 index 000000000000..52d898bdbeb4 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/pnic.c @@ -0,0 +1,173 @@ +/* + drivers/net/tulip/pnic.c + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + + Please submit bugs to http://bugzilla.kernel.org/ . +*/ + +#include +#include +#include +#include "tulip.h" + + +void pnic_do_nway(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + u32 phy_reg = ioread32(ioaddr + 0xB8); + u32 new_csr6 = tp->csr6 & ~0x40C40200; + + if (phy_reg & 0x78000000) { /* Ignore baseT4 */ + if (phy_reg & 0x20000000) dev->if_port = 5; + else if (phy_reg & 0x40000000) dev->if_port = 3; + else if (phy_reg & 0x10000000) dev->if_port = 4; + else if (phy_reg & 0x08000000) dev->if_port = 0; + tp->nwayset = 1; + new_csr6 = (dev->if_port & 1) ? 0x01860000 : 0x00420000; + iowrite32(0x32 | (dev->if_port & 1), ioaddr + CSR12); + if (dev->if_port & 1) + iowrite32(0x1F868, ioaddr + 0xB8); + if (phy_reg & 0x30000000) { + tp->full_duplex = 1; + new_csr6 |= 0x00000200; + } + if (tulip_debug > 1) + netdev_dbg(dev, "PNIC autonegotiated status %08x, %s\n", + phy_reg, medianame[dev->if_port]); + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + /* Restart Tx */ + tulip_restart_rxtx(tp); + dev->trans_start = jiffies; + } + } +} + +void pnic_lnk_change(struct net_device *dev, int csr5) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int phy_reg = ioread32(ioaddr + 0xB8); + + if (tulip_debug > 1) + netdev_dbg(dev, "PNIC link changed state %08x, CSR5 %08x\n", + phy_reg, csr5); + if (ioread32(ioaddr + CSR5) & TPLnkFail) { + iowrite32((ioread32(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7); + /* If we use an external MII, then we mustn't use the + * internal negotiation. + */ + if (tulip_media_cap[dev->if_port] & MediaIsMII) + return; + if (! tp->nwayset || time_after(jiffies, dev_trans_start(dev) + 1*HZ)) { + tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff); + iowrite32(tp->csr6, ioaddr + CSR6); + iowrite32(0x30, ioaddr + CSR12); + iowrite32(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + dev->trans_start = jiffies; + } + } else if (ioread32(ioaddr + CSR5) & TPLnkPass) { + if (tulip_media_cap[dev->if_port] & MediaIsMII) { + spin_lock(&tp->lock); + tulip_check_duplex(dev); + spin_unlock(&tp->lock); + } else { + pnic_do_nway(dev); + } + iowrite32((ioread32(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7); + } +} + +void pnic_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int next_tick = 60*HZ; + + if(!ioread32(ioaddr + CSR7)) { + /* the timer was called due to a work overflow + * in the interrupt handler. Skip the connection + * checks, the nic is definitively speaking with + * his link partner. + */ + goto too_good_connection; + } + + if (tulip_media_cap[dev->if_port] & MediaIsMII) { + spin_lock_irq(&tp->lock); + if (tulip_check_duplex(dev) > 0) + next_tick = 3*HZ; + spin_unlock_irq(&tp->lock); + } else { + int csr12 = ioread32(ioaddr + CSR12); + int new_csr6 = tp->csr6 & ~0x40C40200; + int phy_reg = ioread32(ioaddr + 0xB8); + int csr5 = ioread32(ioaddr + CSR5); + + if (tulip_debug > 1) + netdev_dbg(dev, "PNIC timer PHY status %08x, %s CSR5 %08x\n", + phy_reg, medianame[dev->if_port], csr5); + if (phy_reg & 0x04000000) { /* Remote link fault */ + iowrite32(0x0201F078, ioaddr + 0xB8); + next_tick = 1*HZ; + tp->nwayset = 0; + } else if (phy_reg & 0x78000000) { /* Ignore baseT4 */ + pnic_do_nway(dev); + next_tick = 60*HZ; + } else if (csr5 & TPLnkFail) { /* 100baseTx link beat */ + if (tulip_debug > 1) + netdev_dbg(dev, "%s link beat failed, CSR12 %04x, CSR5 %08x, PHY %03x\n", + medianame[dev->if_port], + csr12, + ioread32(ioaddr + CSR5), + ioread32(ioaddr + 0xB8)); + next_tick = 3*HZ; + if (tp->medialock) { + } else if (tp->nwayset && (dev->if_port & 1)) { + next_tick = 1*HZ; + } else if (dev->if_port == 0) { + dev->if_port = 3; + iowrite32(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + iowrite32(0x1F868, ioaddr + 0xB8); + } else { + dev->if_port = 0; + iowrite32(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + iowrite32(0x1F078, ioaddr + 0xB8); + } + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + /* Restart Tx */ + tulip_restart_rxtx(tp); + dev->trans_start = jiffies; + if (tulip_debug > 1) + dev_info(&dev->dev, + "Changing PNIC configuration to %s %s-duplex, CSR6 %08x\n", + medianame[dev->if_port], + tp->full_duplex ? "full" : "half", + new_csr6); + } + } + } +too_good_connection: + mod_timer(&tp->timer, RUN_AT(next_tick)); + if(!ioread32(ioaddr + CSR7)) { + if (tulip_debug > 1) + dev_info(&dev->dev, "sw timer wakeup\n"); + disable_irq(dev->irq); + tulip_refill_rx(dev); + enable_irq(dev->irq); + iowrite32(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + } +} diff --git a/drivers/net/ethernet/dec/tulip/pnic2.c b/drivers/net/ethernet/dec/tulip/pnic2.c new file mode 100644 index 000000000000..93358ee4d830 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/pnic2.c @@ -0,0 +1,406 @@ +/* + drivers/net/tulip/pnic2.c + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + Modified to hep support PNIC_II by Kevin B. Hendricks + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + + Please submit bugs to http://bugzilla.kernel.org/ . +*/ + + +/* Understanding the PNIC_II - everything is this file is based + * on the PNIC_II_PDF datasheet which is sorely lacking in detail + * + * As I understand things, here are the registers and bits that + * explain the masks and constants used in this file that are + * either different from the 21142/3 or important for basic operation. + * + * + * CSR 6 (mask = 0xfe3bd1fd of bits not to change) + * ----- + * Bit 24 - SCR + * Bit 23 - PCS + * Bit 22 - TTM (Trasmit Threshold Mode) + * Bit 18 - Port Select + * Bit 13 - Start - 1, Stop - 0 Transmissions + * Bit 11:10 - Loop Back Operation Mode + * Bit 9 - Full Duplex mode (Advertise 10BaseT-FD is CSR14<7> is set) + * Bit 1 - Start - 1, Stop - 0 Receive + * + * + * CSR 14 (mask = 0xfff0ee39 of bits not to change) + * ------ + * Bit 19 - PAUSE-Pause + * Bit 18 - Advertise T4 + * Bit 17 - Advertise 100baseTx-FD + * Bit 16 - Advertise 100baseTx-HD + * Bit 12 - LTE - Link Test Enable + * Bit 7 - ANE - Auto Negotiate Enable + * Bit 6 - HDE - Advertise 10baseT-HD + * Bit 2 - Reset to Power down - kept as 1 for normal operation + * Bit 1 - Loop Back enable for 10baseT MCC + * + * + * CSR 12 + * ------ + * Bit 25 - Partner can do T4 + * Bit 24 - Partner can do 100baseTx-FD + * Bit 23 - Partner can do 100baseTx-HD + * Bit 22 - Partner can do 10baseT-FD + * Bit 21 - Partner can do 10baseT-HD + * Bit 15 - LPN is 1 if all above bits are valid other wise 0 + * Bit 14:12 - autonegotiation state (write 001 to start autonegotiate) + * Bit 3 - Autopolarity state + * Bit 2 - LS10B - link state of 10baseT 0 - good, 1 - failed + * Bit 1 - LS100B - link state of 100baseT 0 - good, 1 - failed + * + * + * Data Port Selection Info + *------------------------- + * + * CSR14<7> CSR6<18> CSR6<22> CSR6<23> CSR6<24> MODE/PORT + * 1 0 0 (X) 0 (X) 1 NWAY + * 0 0 1 0 (X) 0 10baseT + * 0 1 0 1 1 (X) 100baseT + * + * + */ + + + +#include "tulip.h" +#include + + +void pnic2_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 3) + dev_info(&dev->dev, "PNIC2 negotiation status %08x\n", + ioread32(ioaddr + CSR12)); + + if (next_tick) { + mod_timer(&tp->timer, RUN_AT(next_tick)); + } +} + + +void pnic2_start_nway(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int csr14; + int csr12; + + /* set up what to advertise during the negotiation */ + + /* load in csr14 and mask off bits not to touch + * comment at top of file explains mask value + */ + csr14 = (ioread32(ioaddr + CSR14) & 0xfff0ee39); + + /* bit 17 - advetise 100baseTx-FD */ + if (tp->sym_advertise & 0x0100) csr14 |= 0x00020000; + + /* bit 16 - advertise 100baseTx-HD */ + if (tp->sym_advertise & 0x0080) csr14 |= 0x00010000; + + /* bit 6 - advertise 10baseT-HD */ + if (tp->sym_advertise & 0x0020) csr14 |= 0x00000040; + + /* Now set bit 12 Link Test Enable, Bit 7 Autonegotiation Enable + * and bit 0 Don't PowerDown 10baseT + */ + csr14 |= 0x00001184; + + if (tulip_debug > 1) + netdev_dbg(dev, "Restarting PNIC2 autonegotiation, csr14=%08x\n", + csr14); + + /* tell pnic2_lnk_change we are doing an nway negotiation */ + dev->if_port = 0; + tp->nway = tp->mediasense = 1; + tp->nwayset = tp->lpar = 0; + + /* now we have to set up csr6 for NWAY state */ + + tp->csr6 = ioread32(ioaddr + CSR6); + if (tulip_debug > 1) + netdev_dbg(dev, "On Entry to Nway, csr6=%08x\n", tp->csr6); + + /* mask off any bits not to touch + * comment at top of file explains mask value + */ + tp->csr6 = tp->csr6 & 0xfe3bd1fd; + + /* don't forget that bit 9 is also used for advertising */ + /* advertise 10baseT-FD for the negotiation (bit 9) */ + if (tp->sym_advertise & 0x0040) tp->csr6 |= 0x00000200; + + /* set bit 24 for nway negotiation mode ... + * see Data Port Selection comment at top of file + * and "Stop" - reset both Transmit (bit 13) and Receive (bit 1) + */ + tp->csr6 |= 0x01000000; + iowrite32(csr14, ioaddr + CSR14); + iowrite32(tp->csr6, ioaddr + CSR6); + udelay(100); + + /* all set up so now force the negotiation to begin */ + + /* read in current values and mask off all but the + * Autonegotiation bits 14:12. Writing a 001 to those bits + * should start the autonegotiation + */ + csr12 = (ioread32(ioaddr + CSR12) & 0xffff8fff); + csr12 |= 0x1000; + iowrite32(csr12, ioaddr + CSR12); +} + + + +void pnic2_lnk_change(struct net_device *dev, int csr5) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int csr14; + + /* read the staus register to find out what is up */ + int csr12 = ioread32(ioaddr + CSR12); + + if (tulip_debug > 1) + dev_info(&dev->dev, + "PNIC2 link status interrupt %08x, CSR5 %x, %08x\n", + csr12, csr5, ioread32(ioaddr + CSR14)); + + /* If NWay finished and we have a negotiated partner capability. + * check bits 14:12 for bit pattern 101 - all is good + */ + if (tp->nway && !tp->nwayset) { + + /* we did an auto negotiation */ + + if ((csr12 & 0x7000) == 0x5000) { + + /* negotiation ended successfully */ + + /* get the link partners reply and mask out all but + * bits 24-21 which show the partners capabilities + * and match those to what we advertised + * + * then begin to interpret the results of the negotiation. + * Always go in this order : (we are ignoring T4 for now) + * 100baseTx-FD, 100baseTx-HD, 10baseT-FD, 10baseT-HD + */ + + int negotiated = ((csr12 >> 16) & 0x01E0) & tp->sym_advertise; + tp->lpar = (csr12 >> 16); + tp->nwayset = 1; + + if (negotiated & 0x0100) dev->if_port = 5; + else if (negotiated & 0x0080) dev->if_port = 3; + else if (negotiated & 0x0040) dev->if_port = 4; + else if (negotiated & 0x0020) dev->if_port = 0; + else { + if (tulip_debug > 1) + dev_info(&dev->dev, + "funny autonegotiate result csr12 %08x advertising %04x\n", + csr12, tp->sym_advertise); + tp->nwayset = 0; + /* so check if 100baseTx link state is okay */ + if ((csr12 & 2) == 0 && (tp->sym_advertise & 0x0180)) + dev->if_port = 3; + } + + /* now record the duplex that was negotiated */ + tp->full_duplex = 0; + if ((dev->if_port == 4) || (dev->if_port == 5)) + tp->full_duplex = 1; + + if (tulip_debug > 1) { + if (tp->nwayset) + dev_info(&dev->dev, + "Switching to %s based on link negotiation %04x & %04x = %04x\n", + medianame[dev->if_port], + tp->sym_advertise, tp->lpar, + negotiated); + } + + /* remember to turn off bit 7 - autonegotiate + * enable so we can properly end nway mode and + * set duplex (ie. use csr6<9> again) + */ + csr14 = (ioread32(ioaddr + CSR14) & 0xffffff7f); + iowrite32(csr14,ioaddr + CSR14); + + + /* now set the data port and operating mode + * (see the Data Port Selection comments at + * the top of the file + */ + + /* get current csr6 and mask off bits not to touch */ + /* see comment at top of file */ + + tp->csr6 = (ioread32(ioaddr + CSR6) & 0xfe3bd1fd); + + /* so if using if_port 3 or 5 then select the 100baseT + * port else select the 10baseT port. + * See the Data Port Selection table at the top + * of the file which was taken from the PNIC_II.PDF + * datasheet + */ + if (dev->if_port & 1) tp->csr6 |= 0x01840000; + else tp->csr6 |= 0x00400000; + + /* now set the full duplex bit appropriately */ + if (tp->full_duplex) tp->csr6 |= 0x00000200; + + iowrite32(1, ioaddr + CSR13); + + if (tulip_debug > 2) + netdev_dbg(dev, "Setting CSR6 %08x/%x CSR12 %08x\n", + tp->csr6, + ioread32(ioaddr + CSR6), + ioread32(ioaddr + CSR12)); + + /* now the following actually writes out the + * new csr6 values + */ + tulip_start_rxtx(tp); + + return; + + } else { + dev_info(&dev->dev, + "Autonegotiation failed, using %s, link beat status %04x\n", + medianame[dev->if_port], csr12); + + /* remember to turn off bit 7 - autonegotiate + * enable so we don't forget + */ + csr14 = (ioread32(ioaddr + CSR14) & 0xffffff7f); + iowrite32(csr14,ioaddr + CSR14); + + /* what should we do when autonegotiate fails? + * should we try again or default to baseline + * case. I just don't know. + * + * for now default to some baseline case + */ + + dev->if_port = 0; + tp->nway = 0; + tp->nwayset = 1; + + /* set to 10baseTx-HD - see Data Port Selection + * comment given at the top of the file + */ + tp->csr6 = (ioread32(ioaddr + CSR6) & 0xfe3bd1fd); + tp->csr6 |= 0x00400000; + + tulip_restart_rxtx(tp); + + return; + + } + } + + if ((tp->nwayset && (csr5 & 0x08000000) && + (dev->if_port == 3 || dev->if_port == 5) && + (csr12 & 2) == 2) || (tp->nway && (csr5 & (TPLnkFail)))) { + + /* Link blew? Maybe restart NWay. */ + + if (tulip_debug > 2) + netdev_dbg(dev, "Ugh! Link blew?\n"); + + del_timer_sync(&tp->timer); + pnic2_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + + return; + } + + + if (dev->if_port == 3 || dev->if_port == 5) { + + /* we are at 100mb and a potential link change occurred */ + + if (tulip_debug > 1) + dev_info(&dev->dev, "PNIC2 %s link beat %s\n", + medianame[dev->if_port], + (csr12 & 2) ? "failed" : "good"); + + /* check 100 link beat */ + + tp->nway = 0; + tp->nwayset = 1; + + /* if failed then try doing an nway to get in sync */ + if ((csr12 & 2) && ! tp->medialock) { + del_timer_sync(&tp->timer); + pnic2_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } + + return; + } + + if (dev->if_port == 0 || dev->if_port == 4) { + + /* we are at 10mb and a potential link change occurred */ + + if (tulip_debug > 1) + dev_info(&dev->dev, "PNIC2 %s link beat %s\n", + medianame[dev->if_port], + (csr12 & 4) ? "failed" : "good"); + + + tp->nway = 0; + tp->nwayset = 1; + + /* if failed, try doing an nway to get in sync */ + if ((csr12 & 4) && ! tp->medialock) { + del_timer_sync(&tp->timer); + pnic2_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } + + return; + } + + + if (tulip_debug > 1) + dev_info(&dev->dev, "PNIC2 Link Change Default?\n"); + + /* if all else fails default to trying 10baseT-HD */ + dev->if_port = 0; + + /* make sure autonegotiate enable is off */ + csr14 = (ioread32(ioaddr + CSR14) & 0xffffff7f); + iowrite32(csr14,ioaddr + CSR14); + + /* set to 10baseTx-HD - see Data Port Selection + * comment given at the top of the file + */ + tp->csr6 = (ioread32(ioaddr + CSR6) & 0xfe3bd1fd); + tp->csr6 |= 0x00400000; + + tulip_restart_rxtx(tp); +} + diff --git a/drivers/net/ethernet/dec/tulip/timer.c b/drivers/net/ethernet/dec/tulip/timer.c new file mode 100644 index 000000000000..2017faf2d0e6 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/timer.c @@ -0,0 +1,179 @@ +/* + drivers/net/tulip/timer.c + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + + Please submit bugs to http://bugzilla.kernel.org/ . +*/ + + +#include "tulip.h" + + +void tulip_media_task(struct work_struct *work) +{ + struct tulip_private *tp = + container_of(work, struct tulip_private, media_work); + struct net_device *dev = tp->dev; + void __iomem *ioaddr = tp->base_addr; + u32 csr12 = ioread32(ioaddr + CSR12); + int next_tick = 2*HZ; + unsigned long flags; + + if (tulip_debug > 2) { + netdev_dbg(dev, "Media selection tick, %s, status %08x mode %08x SIA %08x %08x %08x %08x\n", + medianame[dev->if_port], + ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR6), + csr12, ioread32(ioaddr + CSR13), + ioread32(ioaddr + CSR14), ioread32(ioaddr + CSR15)); + } + switch (tp->chip_id) { + case DC21140: + case DC21142: + case MX98713: + case COMPEX9881: + case DM910X: + default: { + struct medialeaf *mleaf; + unsigned char *p; + if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ + /* Not much that can be done. + Assume this a generic MII or SYM transceiver. */ + next_tick = 60*HZ; + if (tulip_debug > 2) + netdev_dbg(dev, "network media monitor CSR6 %08x CSR12 0x%02x\n", + ioread32(ioaddr + CSR6), + csr12 & 0xff); + break; + } + mleaf = &tp->mtable->mleaf[tp->cur_index]; + p = mleaf->leafdata; + switch (mleaf->type) { + case 0: case 4: { + /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */ + int offset = mleaf->type == 4 ? 5 : 2; + s8 bitnum = p[offset]; + if (p[offset+1] & 0x80) { + if (tulip_debug > 1) + netdev_dbg(dev, "Transceiver monitor tick CSR12=%#02x, no media sense\n", + csr12); + if (mleaf->type == 4) { + if (mleaf->media == 3 && (csr12 & 0x02)) + goto select_next_media; + } + break; + } + if (tulip_debug > 2) + netdev_dbg(dev, "Transceiver monitor tick: CSR12=%#02x bit %d is %d, expecting %d\n", + csr12, (bitnum >> 1) & 7, + (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, + (bitnum >= 0)); + /* Check that the specified bit has the proper value. */ + if ((bitnum < 0) != + ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { + if (tulip_debug > 2) + netdev_dbg(dev, "Link beat detected for %s\n", + medianame[mleaf->media & MEDIA_MASK]); + if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */ + goto actually_mii; + netif_carrier_on(dev); + break; + } + netif_carrier_off(dev); + if (tp->medialock) + break; + select_next_media: + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + dev->if_port = tp->mtable->mleaf[tp->cur_index].media; + if (tulip_media_cap[dev->if_port] & MediaIsFD) + goto select_next_media; /* Skip FD entries. */ + if (tulip_debug > 1) + netdev_dbg(dev, "No link beat on media %s, trying transceiver type %s\n", + medianame[mleaf->media & MEDIA_MASK], + medianame[tp->mtable->mleaf[tp->cur_index].media]); + tulip_select_media(dev, 0); + /* Restart the transmit process. */ + tulip_restart_rxtx(tp); + next_tick = (24*HZ)/10; + break; + } + case 1: case 3: /* 21140, 21142 MII */ + actually_mii: + if (tulip_check_duplex(dev) < 0) { + netif_carrier_off(dev); + next_tick = 3*HZ; + } else { + netif_carrier_on(dev); + next_tick = 60*HZ; + } + break; + case 2: /* 21142 serial block has no link beat. */ + default: + break; + } + } + break; + } + + + spin_lock_irqsave(&tp->lock, flags); + if (tp->timeout_recovery) { + tulip_tx_timeout_complete(tp, ioaddr); + tp->timeout_recovery = 0; + } + spin_unlock_irqrestore(&tp->lock, flags); + + /* mod_timer synchronizes us with potential add_timer calls + * from interrupts. + */ + mod_timer(&tp->timer, RUN_AT(next_tick)); +} + + +void mxic_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 3) { + dev_info(&dev->dev, "MXIC negotiation status %08x\n", + ioread32(ioaddr + CSR12)); + } + if (next_tick) { + mod_timer(&tp->timer, RUN_AT(next_tick)); + } +} + + +void comet_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = netdev_priv(dev); + int next_tick = 60*HZ; + + if (tulip_debug > 1) + netdev_dbg(dev, "Comet link status %04x partner capability %04x\n", + tulip_mdio_read(dev, tp->phys[0], 1), + tulip_mdio_read(dev, tp->phys[0], 5)); + /* mod_timer synchronizes us with potential add_timer calls + * from interrupts. + */ + if (tulip_check_duplex(dev) < 0) + { netif_carrier_off(dev); } + else + { netif_carrier_on(dev); } + mod_timer(&tp->timer, RUN_AT(next_tick)); +} + diff --git a/drivers/net/ethernet/dec/tulip/tulip.h b/drivers/net/ethernet/dec/tulip/tulip.h new file mode 100644 index 000000000000..9db528967da9 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/tulip.h @@ -0,0 +1,573 @@ +/* + drivers/net/tulip/tulip.h + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + + Please submit bugs to http://bugzilla.kernel.org/ . +*/ + +#ifndef __NET_TULIP_H__ +#define __NET_TULIP_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* undefine, or define to various debugging levels (>4 == obscene levels) */ +#define TULIP_DEBUG 1 + +#ifdef CONFIG_TULIP_MMIO +#define TULIP_BAR 1 /* CBMA */ +#else +#define TULIP_BAR 0 /* CBIO */ +#endif + + + +struct tulip_chip_table { + char *chip_name; + int io_size; + int valid_intrs; /* CSR7 interrupt enable settings */ + int flags; + void (*media_timer) (unsigned long); + work_func_t media_task; +}; + + +enum tbl_flag { + HAS_MII = 0x00001, + HAS_MEDIA_TABLE = 0x00002, + CSR12_IN_SROM = 0x00004, + ALWAYS_CHECK_MII = 0x00008, + HAS_ACPI = 0x00010, + MC_HASH_ONLY = 0x00020, /* Hash-only multicast filter. */ + HAS_PNICNWAY = 0x00080, + HAS_NWAY = 0x00040, /* Uses internal NWay xcvr. */ + HAS_INTR_MITIGATION = 0x00100, + IS_ASIX = 0x00200, + HAS_8023X = 0x00400, + COMET_MAC_ADDR = 0x00800, + HAS_PCI_MWI = 0x01000, + HAS_PHY_IRQ = 0x02000, + HAS_SWAPPED_SEEPROM = 0x04000, + NEEDS_FAKE_MEDIA_TABLE = 0x08000, + COMET_PM = 0x10000, +}; + + +/* chip types. careful! order is VERY IMPORTANT here, as these + * are used throughout the driver as indices into arrays */ +/* Note 21142 == 21143. */ +enum chips { + DC21040 = 0, + DC21041 = 1, + DC21140 = 2, + DC21142 = 3, DC21143 = 3, + LC82C168, + MX98713, + MX98715, + MX98725, + AX88140, + PNIC2, + COMET, + COMPEX9881, + I21145, + DM910X, + CONEXANT, +}; + + +enum MediaIs { + MediaIsFD = 1, + MediaAlwaysFD = 2, + MediaIsMII = 4, + MediaIsFx = 8, + MediaIs100 = 16 +}; + + +/* Offsets to the Command and Status Registers, "CSRs". All accesses + must be longword instructions and quadword aligned. */ +enum tulip_offsets { + CSR0 = 0, + CSR1 = 0x08, + CSR2 = 0x10, + CSR3 = 0x18, + CSR4 = 0x20, + CSR5 = 0x28, + CSR6 = 0x30, + CSR7 = 0x38, + CSR8 = 0x40, + CSR9 = 0x48, + CSR10 = 0x50, + CSR11 = 0x58, + CSR12 = 0x60, + CSR13 = 0x68, + CSR14 = 0x70, + CSR15 = 0x78, + CSR18 = 0x88, + CSR19 = 0x8c, + CSR20 = 0x90, + CSR27 = 0xAC, + CSR28 = 0xB0, +}; + +/* register offset and bits for CFDD PCI config reg */ +enum pci_cfg_driver_reg { + CFDD = 0x40, + CFDD_Sleep = (1 << 31), + CFDD_Snooze = (1 << 30), +}; + +#define RxPollInt (RxIntr|RxNoBuf|RxDied|RxJabber) + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { + TimerInt = 0x800, + SystemError = 0x2000, + TPLnkFail = 0x1000, + TPLnkPass = 0x10, + NormalIntr = 0x10000, + AbnormalIntr = 0x8000, + RxJabber = 0x200, + RxDied = 0x100, + RxNoBuf = 0x80, + RxIntr = 0x40, + TxFIFOUnderflow = 0x20, + RxErrIntr = 0x10, + TxJabber = 0x08, + TxNoBuf = 0x04, + TxDied = 0x02, + TxIntr = 0x01, +}; + +/* bit mask for CSR5 TX/RX process state */ +#define CSR5_TS 0x00700000 +#define CSR5_RS 0x000e0000 + +enum tulip_mode_bits { + TxThreshold = (1 << 22), + FullDuplex = (1 << 9), + TxOn = 0x2000, + AcceptBroadcast = 0x0100, + AcceptAllMulticast = 0x0080, + AcceptAllPhys = 0x0040, + AcceptRunt = 0x0008, + RxOn = 0x0002, + RxTx = (TxOn | RxOn), +}; + + +enum tulip_busconfig_bits { + MWI = (1 << 24), + MRL = (1 << 23), + MRM = (1 << 21), + CALShift = 14, + BurstLenShift = 8, +}; + + +/* The Tulip Rx and Tx buffer descriptors. */ +struct tulip_rx_desc { + __le32 status; + __le32 length; + __le32 buffer1; + __le32 buffer2; +}; + + +struct tulip_tx_desc { + __le32 status; + __le32 length; + __le32 buffer1; + __le32 buffer2; /* We use only buffer 1. */ +}; + + +enum desc_status_bits { + DescOwned = 0x80000000, + DescWholePkt = 0x60000000, + DescEndPkt = 0x40000000, + DescStartPkt = 0x20000000, + DescEndRing = 0x02000000, + DescUseLink = 0x01000000, + + /* + * Error summary flag is logical or of 'CRC Error', 'Collision Seen', + * 'Frame Too Long', 'Runt' and 'Descriptor Error' flags generated + * within tulip chip. + */ + RxDescErrorSummary = 0x8000, + RxDescCRCError = 0x0002, + RxDescCollisionSeen = 0x0040, + + /* + * 'Frame Too Long' flag is set if packet length including CRC exceeds + * 1518. However, a full sized VLAN tagged frame is 1522 bytes + * including CRC. + * + * The tulip chip does not block oversized frames, and if this flag is + * set on a receive descriptor it does not indicate the frame has been + * truncated. The receive descriptor also includes the actual length. + * Therefore we can safety ignore this flag and check the length + * ourselves. + */ + RxDescFrameTooLong = 0x0080, + RxDescRunt = 0x0800, + RxDescDescErr = 0x4000, + RxWholePkt = 0x00000300, + /* + * Top three bits of 14 bit frame length (status bits 27-29) should + * never be set as that would make frame over 2047 bytes. The Receive + * Watchdog flag (bit 4) may indicate the length is over 2048 and the + * length field is invalid. + */ + RxLengthOver2047 = 0x38000010 +}; + + +enum t21143_csr6_bits { + csr6_sc = (1<<31), + csr6_ra = (1<<30), + csr6_ign_dest_msb = (1<<26), + csr6_mbo = (1<<25), + csr6_scr = (1<<24), /* scramble mode flag: can't be set */ + csr6_pcs = (1<<23), /* Enables PCS functions (symbol mode requires csr6_ps be set) default is set */ + csr6_ttm = (1<<22), /* Transmit Threshold Mode, set for 10baseT, 0 for 100BaseTX */ + csr6_sf = (1<<21), /* Store and forward. If set ignores TR bits */ + csr6_hbd = (1<<19), /* Heart beat disable. Disables SQE function in 10baseT */ + csr6_ps = (1<<18), /* Port Select. 0 (defualt) = 10baseT, 1 = 100baseTX: can't be set */ + csr6_ca = (1<<17), /* Collision Offset Enable. If set uses special algorithm in low collision situations */ + csr6_trh = (1<<15), /* Transmit Threshold high bit */ + csr6_trl = (1<<14), /* Transmit Threshold low bit */ + + /*************************************************************** + * This table shows transmit threshold values based on media * + * and these two registers (from PNIC1 & 2 docs) Note: this is * + * all meaningless if sf is set. * + ***************************************************************/ + + /*********************************** + * (trh,trl) * 100BaseTX * 10BaseT * + *********************************** + * (0,0) * 128 * 72 * + * (0,1) * 256 * 96 * + * (1,0) * 512 * 128 * + * (1,1) * 1024 * 160 * + ***********************************/ + + csr6_fc = (1<<12), /* Forces a collision in next transmission (for testing in loopback mode) */ + csr6_om_int_loop = (1<<10), /* internal (FIFO) loopback flag */ + csr6_om_ext_loop = (1<<11), /* external (PMD) loopback flag */ + /* set both and you get (PHY) loopback */ + csr6_fd = (1<<9), /* Full duplex mode, disables hearbeat, no loopback */ + csr6_pm = (1<<7), /* Pass All Multicast */ + csr6_pr = (1<<6), /* Promiscuous mode */ + csr6_sb = (1<<5), /* Start(1)/Stop(0) backoff counter */ + csr6_if = (1<<4), /* Inverse Filtering, rejects only addresses in address table: can't be set */ + csr6_pb = (1<<3), /* Pass Bad Frames, (1) causes even bad frames to be passed on */ + csr6_ho = (1<<2), /* Hash-only filtering mode: can't be set */ + csr6_hp = (1<<0), /* Hash/Perfect Receive Filtering Mode: can't be set */ + + csr6_mask_capture = (csr6_sc | csr6_ca), + csr6_mask_defstate = (csr6_mask_capture | csr6_mbo), + csr6_mask_hdcap = (csr6_mask_defstate | csr6_hbd | csr6_ps), + csr6_mask_hdcaptt = (csr6_mask_hdcap | csr6_trh | csr6_trl), + csr6_mask_fullcap = (csr6_mask_hdcaptt | csr6_fd), + csr6_mask_fullpromisc = (csr6_pr | csr6_pm), + csr6_mask_filters = (csr6_hp | csr6_ho | csr6_if), + csr6_mask_100bt = (csr6_scr | csr6_pcs | csr6_hbd), +}; + +enum tulip_comet_csr13_bits { +/* The LINKOFFE and LINKONE work in conjunction with LSCE, i.e. they + * determine which link status transition wakes up if LSCE is + * enabled */ + comet_csr13_linkoffe = (1 << 17), + comet_csr13_linkone = (1 << 16), + comet_csr13_wfre = (1 << 10), + comet_csr13_mpre = (1 << 9), + comet_csr13_lsce = (1 << 8), + comet_csr13_wfr = (1 << 2), + comet_csr13_mpr = (1 << 1), + comet_csr13_lsc = (1 << 0), +}; + +enum tulip_comet_csr18_bits { + comet_csr18_pmes_sticky = (1 << 24), + comet_csr18_pm_mode = (1 << 19), + comet_csr18_apm_mode = (1 << 18), + comet_csr18_d3a = (1 << 7) +}; + +enum tulip_comet_csr20_bits { + comet_csr20_pmes = (1 << 15), +}; + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ + +#define TX_RING_SIZE 32 +#define RX_RING_SIZE 128 +#define MEDIA_MASK 31 + +/* The receiver on the DC21143 rev 65 can fail to close the last + * receive descriptor in certain circumstances (see errata) when + * using MWI. This can only occur if the receive buffer ends on + * a cache line boundary, so the "+ 4" below ensures it doesn't. + */ +#define PKT_BUF_SZ (1536 + 4) /* Size of each temporary Rx buffer. */ + +#define TULIP_MIN_CACHE_LINE 8 /* in units of 32-bit words */ + +#if defined(__sparc__) || defined(__hppa__) +/* The UltraSparc PCI controllers will disconnect at every 64-byte + * crossing anyways so it makes no sense to tell Tulip to burst + * any more than that. + */ +#define TULIP_MAX_CACHE_LINE 16 /* in units of 32-bit words */ +#else +#define TULIP_MAX_CACHE_LINE 32 /* in units of 32-bit words */ +#endif + + +/* Ring-wrap flag in length field, use for last ring entry. + 0x01000000 means chain on buffer2 address, + 0x02000000 means use the ring start address in CSR2/3. + Note: Some work-alike chips do not function correctly in chained mode. + The ASIX chip works only in chained mode. + Thus we indicates ring mode, but always write the 'next' field for + chained mode as well. +*/ +#define DESC_RING_WRAP 0x02000000 + + +#define EEPROM_SIZE 512 /* 2 << EEPROM_ADDRLEN */ + + +#define RUN_AT(x) (jiffies + (x)) + +#define get_u16(ptr) get_unaligned_le16((ptr)) + +struct medialeaf { + u8 type; + u8 media; + unsigned char *leafdata; +}; + + +struct mediatable { + u16 defaultmedia; + u8 leafcount; + u8 csr12dir; /* General purpose pin directions. */ + unsigned has_mii:1; + unsigned has_nonmii:1; + unsigned has_reset:6; + u32 csr15dir; + u32 csr15val; /* 21143 NWay setting. */ + struct medialeaf mleaf[0]; +}; + + +struct mediainfo { + struct mediainfo *next; + int info_type; + int index; + unsigned char *info; +}; + +struct ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + + +struct tulip_private { + const char *product_name; + struct net_device *next_module; + struct tulip_rx_desc *rx_ring; + struct tulip_tx_desc *tx_ring; + dma_addr_t rx_ring_dma; + dma_addr_t tx_ring_dma; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct ring_info tx_buffers[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct ring_info rx_buffers[RX_RING_SIZE]; + u16 setup_frame[96]; /* Pseudo-Tx frame to init address table. */ + int chip_id; + int revision; + int flags; + struct napi_struct napi; + struct timer_list timer; /* Media selection timer. */ + struct timer_list oom_timer; /* Out of memory timer. */ + u32 mc_filter[2]; + spinlock_t lock; + spinlock_t mii_lock; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + +#ifdef CONFIG_TULIP_NAPI_HW_MITIGATION + int mit_on; +#endif + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int full_duplex_lock:1; + unsigned int fake_addr:1; /* Multiport board faked address. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int media2:4; /* Secondary monitored media port. */ + unsigned int medialock:1; /* Don't sense media type. */ + unsigned int mediasense:1; /* Media sensing in progress. */ + unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */ + unsigned int timeout_recovery:1; + unsigned int csr0; /* CSR0 setting. */ + unsigned int csr6; /* Current CSR6 control settings. */ + unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ + void (*link_change) (struct net_device * dev, int csr5); + struct ethtool_wolinfo wolinfo; /* WOL settings */ + u16 sym_advertise, mii_advertise; /* NWay capabilities advertised. */ + u16 lpar; /* 21143 Link partner ability. */ + u16 advertising[4]; + signed char phys[4], mii_cnt; /* MII device addresses. */ + struct mediatable *mtable; + int cur_index; /* Current media index. */ + int saved_if_port; + struct pci_dev *pdev; + int ttimer; + int susp_rx; + unsigned long nir; + void __iomem *base_addr; + int csr12_shadow; + int pad0; /* Used for 8-byte alignment */ + struct work_struct media_work; + struct net_device *dev; +}; + + +struct eeprom_fixup { + char *name; + unsigned char addr0; + unsigned char addr1; + unsigned char addr2; + u16 newtable[32]; /* Max length below. */ +}; + + +/* 21142.c */ +extern u16 t21142_csr14[]; +void t21142_media_task(struct work_struct *work); +void t21142_start_nway(struct net_device *dev); +void t21142_lnk_change(struct net_device *dev, int csr5); + + +/* PNIC2.c */ +void pnic2_lnk_change(struct net_device *dev, int csr5); +void pnic2_timer(unsigned long data); +void pnic2_start_nway(struct net_device *dev); +void pnic2_lnk_change(struct net_device *dev, int csr5); + +/* eeprom.c */ +void tulip_parse_eeprom(struct net_device *dev); +int tulip_read_eeprom(struct net_device *dev, int location, int addr_len); + +/* interrupt.c */ +extern unsigned int tulip_max_interrupt_work; +extern int tulip_rx_copybreak; +irqreturn_t tulip_interrupt(int irq, void *dev_instance); +int tulip_refill_rx(struct net_device *dev); +#ifdef CONFIG_TULIP_NAPI +int tulip_poll(struct napi_struct *napi, int budget); +#endif + + +/* media.c */ +int tulip_mdio_read(struct net_device *dev, int phy_id, int location); +void tulip_mdio_write(struct net_device *dev, int phy_id, int location, int value); +void tulip_select_media(struct net_device *dev, int startup); +int tulip_check_duplex(struct net_device *dev); +void tulip_find_mii (struct net_device *dev, int board_idx); + +/* pnic.c */ +void pnic_do_nway(struct net_device *dev); +void pnic_lnk_change(struct net_device *dev, int csr5); +void pnic_timer(unsigned long data); + +/* timer.c */ +void tulip_media_task(struct work_struct *work); +void mxic_timer(unsigned long data); +void comet_timer(unsigned long data); + +/* tulip_core.c */ +extern int tulip_debug; +extern const char * const medianame[]; +extern const char tulip_media_cap[]; +extern struct tulip_chip_table tulip_tbl[]; +void oom_timer(unsigned long data); +extern u8 t21040_csr13[]; + +static inline void tulip_start_rxtx(struct tulip_private *tp) +{ + void __iomem *ioaddr = tp->base_addr; + iowrite32(tp->csr6 | RxTx, ioaddr + CSR6); + barrier(); + (void) ioread32(ioaddr + CSR6); /* mmio sync */ +} + +static inline void tulip_stop_rxtx(struct tulip_private *tp) +{ + void __iomem *ioaddr = tp->base_addr; + u32 csr6 = ioread32(ioaddr + CSR6); + + if (csr6 & RxTx) { + unsigned i=1300/10; + iowrite32(csr6 & ~RxTx, ioaddr + CSR6); + barrier(); + /* wait until in-flight frame completes. + * Max time @ 10BT: 1500*8b/10Mbps == 1200us (+ 100us margin) + * Typically expect this loop to end in < 50 us on 100BT. + */ + while (--i && (ioread32(ioaddr + CSR5) & (CSR5_TS|CSR5_RS))) + udelay(10); + + if (!i) + netdev_dbg(tp->dev, "tulip_stop_rxtx() failed (CSR5 0x%x CSR6 0x%x)\n", + ioread32(ioaddr + CSR5), + ioread32(ioaddr + CSR6)); + } +} + +static inline void tulip_restart_rxtx(struct tulip_private *tp) +{ + tulip_stop_rxtx(tp); + udelay(5); + tulip_start_rxtx(tp); +} + +static inline void tulip_tx_timeout_complete(struct tulip_private *tp, void __iomem *ioaddr) +{ + /* Stop and restart the chip's Tx processes. */ + tulip_restart_rxtx(tp); + /* Trigger an immediate transmit demand. */ + iowrite32(0, ioaddr + CSR1); + + tp->dev->stats.tx_errors++; +} + +#endif /* __NET_TULIP_H__ */ diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c new file mode 100644 index 000000000000..1246998a677c --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -0,0 +1,2011 @@ +/* tulip_core.c: A DEC 21x4x-family ethernet driver for Linux. + + Copyright 2000,2001 The Linux Kernel Team + Written/copyright 1994-2001 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} + for more information on this driver. + + Please submit bugs to http://bugzilla.kernel.org/ . +*/ + +#define pr_fmt(fmt) "tulip: " fmt + +#define DRV_NAME "tulip" +#ifdef CONFIG_TULIP_NAPI +#define DRV_VERSION "1.1.15-NAPI" /* Keep at least for test */ +#else +#define DRV_VERSION "1.1.15" +#endif +#define DRV_RELDATE "Feb 27, 2007" + + +#include +#include +#include +#include "tulip.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SPARC +#include +#endif + +static char version[] __devinitdata = + "Linux Tulip driver version " DRV_VERSION " (" DRV_RELDATE ")\n"; + +/* A few user-configurable values. */ + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static unsigned int max_interrupt_work = 25; + +#define MAX_UNITS 8 +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS]; +static int options[MAX_UNITS]; +static int mtu[MAX_UNITS]; /* Jumbo MTU for interfaces. */ + +/* The possible media types that can be set in options[] are: */ +const char * const medianame[32] = { + "10baseT", "10base2", "AUI", "100baseTx", + "10baseT-FDX", "100baseTx-FDX", "100baseT4", "100baseFx", + "100baseFx-FDX", "MII 10baseT", "MII 10baseT-FDX", "MII", + "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FDX", "MII 100baseT4", + "MII 100baseFx-HDX", "MII 100baseFx-FDX", "Home-PNA 1Mbps", "Invalid-19", + "","","","", "","","","", "","","","Transceiver reset", +}; + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \ + defined(CONFIG_SPARC) || defined(__ia64__) || \ + defined(__sh__) || defined(__mips__) +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif + +/* + Set the bus performance register. + Typical: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Warning: many older 486 systems are broken and require setting 0x00A04800 + 8 longword cache alignment, 8 longword burst. + ToDo: Non-Intel setting could be better. +*/ + +#if defined(__alpha__) || defined(__ia64__) +static int csr0 = 0x01A00000 | 0xE000; +#elif defined(__i386__) || defined(__powerpc__) || defined(__x86_64__) +static int csr0 = 0x01A00000 | 0x8000; +#elif defined(CONFIG_SPARC) || defined(__hppa__) +/* The UltraSparc PCI controllers will disconnect at every 64-byte + * crossing anyways so it makes no sense to tell Tulip to burst + * any more than that. + */ +static int csr0 = 0x01A00000 | 0x9000; +#elif defined(__arm__) || defined(__sh__) +static int csr0 = 0x01A00000 | 0x4800; +#elif defined(__mips__) +static int csr0 = 0x00200000 | 0x4000; +#else +#warning Processor architecture undefined! +static int csr0 = 0x00A00000 | 0x4800; +#endif + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) + + +MODULE_AUTHOR("The Linux Kernel Team"); +MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +module_param(tulip_debug, int, 0); +module_param(max_interrupt_work, int, 0); +module_param(rx_copybreak, int, 0); +module_param(csr0, int, 0); +module_param_array(options, int, NULL, 0); +module_param_array(full_duplex, int, NULL, 0); + +#ifdef TULIP_DEBUG +int tulip_debug = TULIP_DEBUG; +#else +int tulip_debug = 1; +#endif + +static void tulip_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = netdev_priv(dev); + + if (netif_running(dev)) + schedule_work(&tp->media_work); +} + +/* + * This table use during operation for capabilities and media timer. + * + * It is indexed via the values in 'enum chips' + */ + +struct tulip_chip_table tulip_tbl[] = { + { }, /* placeholder for array, slot unused currently */ + { }, /* placeholder for array, slot unused currently */ + + /* DC21140 */ + { "Digital DS21140 Tulip", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_PCI_MWI, tulip_timer, + tulip_media_task }, + + /* DC21142, DC21143 */ + { "Digital DS21142/43 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY + | HAS_INTR_MITIGATION | HAS_PCI_MWI, tulip_timer, t21142_media_task }, + + /* LC82C168 */ + { "Lite-On 82c168 PNIC", 256, 0x0001fbef, + HAS_MII | HAS_PNICNWAY, pnic_timer, }, + + /* MX98713 */ + { "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer, }, + + /* MX98715 */ + { "Macronix 98715 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer, }, + + /* MX98725 */ + { "Macronix 98725 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer, }, + + /* AX88140 */ + { "ASIX AX88140", 128, 0x0001fbff, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY + | IS_ASIX, tulip_timer, tulip_media_task }, + + /* PNIC2 */ + { "Lite-On PNIC-II", 256, 0x0801fbff, + HAS_MII | HAS_NWAY | HAS_8023X | HAS_PCI_MWI, pnic2_timer, }, + + /* COMET */ + { "ADMtek Comet", 256, 0x0001abef, + HAS_MII | MC_HASH_ONLY | COMET_MAC_ADDR, comet_timer, }, + + /* COMPEX9881 */ + { "Compex 9881 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer, }, + + /* I21145 */ + { "Intel DS21145 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI + | HAS_NWAY | HAS_PCI_MWI, tulip_timer, tulip_media_task }, + + /* DM910X */ +#ifdef CONFIG_TULIP_DM910X + { "Davicom DM9102/DM9102A", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_ACPI, + tulip_timer, tulip_media_task }, +#else + { NULL }, +#endif + + /* RS7112 */ + { "Conexant LANfinity", 256, 0x0001ebef, + HAS_MII | HAS_ACPI, tulip_timer, tulip_media_task }, + +}; + + +static DEFINE_PCI_DEVICE_TABLE(tulip_pci_tbl) = { + { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 }, + { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21143 }, + { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 }, + { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 }, + { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, +/* { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },*/ + { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 }, + { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 }, + { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1317, 0x0985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1317, 0x1985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1317, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x13D1, 0xAB02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x13D1, 0xAB03, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x13D1, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x104A, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x104A, 0x2774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1259, 0xa120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 }, + { 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 }, +#ifdef CONFIG_TULIP_DM910X + { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X }, + { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X }, +#endif + { 0x1113, 0x1216, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, + { 0x1113, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1186, 0x1541, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1186, 0x1561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1186, 0x1591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x14f1, 0x1803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CONEXANT }, + { 0x1626, 0x8410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1737, 0xAB09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1737, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x17B3, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x10b7, 0x9300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* 3Com 3CSOHO100B-TX */ + { 0x14ea, 0xab08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Planex FNW-3602-TX */ + { 0x1414, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Microsoft MN-120 */ + { 0x1414, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { } /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci, tulip_pci_tbl); + + +/* A full-duplex map for media types. */ +const char tulip_media_cap[32] = +{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20, 28,31,0,0, }; + +static void tulip_tx_timeout(struct net_device *dev); +static void tulip_init_ring(struct net_device *dev); +static void tulip_free_ring(struct net_device *dev); +static netdev_tx_t tulip_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static int tulip_open(struct net_device *dev); +static int tulip_close(struct net_device *dev); +static void tulip_up(struct net_device *dev); +static void tulip_down(struct net_device *dev); +static struct net_device_stats *tulip_get_stats(struct net_device *dev); +static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void set_rx_mode(struct net_device *dev); +static void tulip_set_wolopts(struct pci_dev *pdev, u32 wolopts); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void poll_tulip(struct net_device *dev); +#endif + +static void tulip_set_power_state (struct tulip_private *tp, + int sleep, int snooze) +{ + if (tp->flags & HAS_ACPI) { + u32 tmp, newtmp; + pci_read_config_dword (tp->pdev, CFDD, &tmp); + newtmp = tmp & ~(CFDD_Sleep | CFDD_Snooze); + if (sleep) + newtmp |= CFDD_Sleep; + else if (snooze) + newtmp |= CFDD_Snooze; + if (tmp != newtmp) + pci_write_config_dword (tp->pdev, CFDD, newtmp); + } + +} + + +static void tulip_up(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int next_tick = 3*HZ; + u32 reg; + int i; + +#ifdef CONFIG_TULIP_NAPI + napi_enable(&tp->napi); +#endif + + /* Wake the chip from sleep/snooze mode. */ + tulip_set_power_state (tp, 0, 0); + + /* Disable all WOL events */ + pci_enable_wake(tp->pdev, PCI_D3hot, 0); + pci_enable_wake(tp->pdev, PCI_D3cold, 0); + tulip_set_wolopts(tp->pdev, 0); + + /* On some chip revs we must set the MII/SYM port before the reset!? */ + if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) + iowrite32(0x00040000, ioaddr + CSR6); + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + iowrite32(0x00000001, ioaddr + CSR0); + pci_read_config_dword(tp->pdev, PCI_COMMAND, ®); /* flush write */ + udelay(100); + + /* Deassert reset. + Wait the specified 50 PCI cycles after a reset by initializing + Tx and Rx queues and the address filter list. */ + iowrite32(tp->csr0, ioaddr + CSR0); + pci_read_config_dword(tp->pdev, PCI_COMMAND, ®); /* flush write */ + udelay(100); + + if (tulip_debug > 1) + netdev_dbg(dev, "tulip_up(), irq==%d\n", dev->irq); + + iowrite32(tp->rx_ring_dma, ioaddr + CSR3); + iowrite32(tp->tx_ring_dma, ioaddr + CSR4); + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + if (tp->flags & MC_HASH_ONLY) { + u32 addr_low = get_unaligned_le32(dev->dev_addr); + u32 addr_high = get_unaligned_le16(dev->dev_addr + 4); + if (tp->chip_id == AX88140) { + iowrite32(0, ioaddr + CSR13); + iowrite32(addr_low, ioaddr + CSR14); + iowrite32(1, ioaddr + CSR13); + iowrite32(addr_high, ioaddr + CSR14); + } else if (tp->flags & COMET_MAC_ADDR) { + iowrite32(addr_low, ioaddr + 0xA4); + iowrite32(addr_high, ioaddr + 0xA8); + iowrite32(0, ioaddr + CSR27); + iowrite32(0, ioaddr + CSR28); + } + } else { + /* This is set_rx_mode(), but without starting the transmitter. */ + u16 *eaddrs = (u16 *)dev->dev_addr; + u16 *setup_frm = &tp->setup_frame[15*6]; + dma_addr_t mapping; + + /* 21140 bug: you must add the broadcast address. */ + memset(tp->setup_frame, 0xff, sizeof(tp->setup_frame)); + /* Fill the final entry of the table with our physical address. */ + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; + + mapping = pci_map_single(tp->pdev, tp->setup_frame, + sizeof(tp->setup_frame), + PCI_DMA_TODEVICE); + tp->tx_buffers[tp->cur_tx].skb = NULL; + tp->tx_buffers[tp->cur_tx].mapping = mapping; + + /* Put the setup frame on the Tx list. */ + tp->tx_ring[tp->cur_tx].length = cpu_to_le32(0x08000000 | 192); + tp->tx_ring[tp->cur_tx].buffer1 = cpu_to_le32(mapping); + tp->tx_ring[tp->cur_tx].status = cpu_to_le32(DescOwned); + + tp->cur_tx++; + } + + tp->saved_if_port = dev->if_port; + if (dev->if_port == 0) + dev->if_port = tp->default_port; + + /* Allow selecting a default media. */ + i = 0; + if (tp->mtable == NULL) + goto media_picked; + if (dev->if_port) { + int looking_for = tulip_media_cap[dev->if_port] & MediaIsMII ? 11 : + (dev->if_port == 12 ? 0 : dev->if_port); + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == looking_for) { + dev_info(&dev->dev, + "Using user-specified media %s\n", + medianame[dev->if_port]); + goto media_picked; + } + } + if ((tp->mtable->defaultmedia & 0x0800) == 0) { + int looking_for = tp->mtable->defaultmedia & MEDIA_MASK; + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == looking_for) { + dev_info(&dev->dev, + "Using EEPROM-set media %s\n", + medianame[looking_for]); + goto media_picked; + } + } + /* Start sensing first non-full-duplex media. */ + for (i = tp->mtable->leafcount - 1; + (tulip_media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) + ; +media_picked: + + tp->csr6 = 0; + tp->cur_index = i; + tp->nwayset = 0; + + if (dev->if_port) { + if (tp->chip_id == DC21143 && + (tulip_media_cap[dev->if_port] & MediaIsMII)) { + /* We must reset the media CSRs when we force-select MII mode. */ + iowrite32(0x0000, ioaddr + CSR13); + iowrite32(0x0000, ioaddr + CSR14); + iowrite32(0x0008, ioaddr + CSR15); + } + tulip_select_media(dev, 1); + } else if (tp->chip_id == DC21142) { + if (tp->mii_cnt) { + tulip_select_media(dev, 1); + if (tulip_debug > 1) + dev_info(&dev->dev, + "Using MII transceiver %d, status %04x\n", + tp->phys[0], + tulip_mdio_read(dev, tp->phys[0], 1)); + iowrite32(csr6_mask_defstate, ioaddr + CSR6); + tp->csr6 = csr6_mask_hdcap; + dev->if_port = 11; + iowrite32(0x0000, ioaddr + CSR13); + iowrite32(0x0000, ioaddr + CSR14); + } else + t21142_start_nway(dev); + } else if (tp->chip_id == PNIC2) { + /* for initial startup advertise 10/100 Full and Half */ + tp->sym_advertise = 0x01E0; + /* enable autonegotiate end interrupt */ + iowrite32(ioread32(ioaddr+CSR5)| 0x00008010, ioaddr + CSR5); + iowrite32(ioread32(ioaddr+CSR7)| 0x00008010, ioaddr + CSR7); + pnic2_start_nway(dev); + } else if (tp->chip_id == LC82C168 && ! tp->medialock) { + if (tp->mii_cnt) { + dev->if_port = 11; + tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0); + iowrite32(0x0001, ioaddr + CSR15); + } else if (ioread32(ioaddr + CSR5) & TPLnkPass) + pnic_do_nway(dev); + else { + /* Start with 10mbps to do autonegotiation. */ + iowrite32(0x32, ioaddr + CSR12); + tp->csr6 = 0x00420000; + iowrite32(0x0001B078, ioaddr + 0xB8); + iowrite32(0x0201B078, ioaddr + 0xB8); + next_tick = 1*HZ; + } + } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) && + ! tp->medialock) { + dev->if_port = 0; + tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0); + iowrite32(0x0f370000 | ioread16(ioaddr + 0x80), ioaddr + 0x80); + } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) { + /* Provided by BOLO, Macronix - 12/10/1998. */ + dev->if_port = 0; + tp->csr6 = 0x01a80200; + iowrite32(0x0f370000 | ioread16(ioaddr + 0x80), ioaddr + 0x80); + iowrite32(0x11000 | ioread16(ioaddr + 0xa0), ioaddr + 0xa0); + } else if (tp->chip_id == COMET || tp->chip_id == CONEXANT) { + /* Enable automatic Tx underrun recovery. */ + iowrite32(ioread32(ioaddr + 0x88) | 1, ioaddr + 0x88); + dev->if_port = tp->mii_cnt ? 11 : 0; + tp->csr6 = 0x00040000; + } else if (tp->chip_id == AX88140) { + tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100; + } else + tulip_select_media(dev, 1); + + /* Start the chip's Tx to process setup frame. */ + tulip_stop_rxtx(tp); + barrier(); + udelay(5); + iowrite32(tp->csr6 | TxOn, ioaddr + CSR6); + + /* Enable interrupts by setting the interrupt mask. */ + iowrite32(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); + iowrite32(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + tulip_start_rxtx(tp); + iowrite32(0, ioaddr + CSR2); /* Rx poll demand */ + + if (tulip_debug > 2) { + netdev_dbg(dev, "Done tulip_up(), CSR0 %08x, CSR5 %08x CSR6 %08x\n", + ioread32(ioaddr + CSR0), + ioread32(ioaddr + CSR5), + ioread32(ioaddr + CSR6)); + } + + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +#ifdef CONFIG_TULIP_NAPI + init_timer(&tp->oom_timer); + tp->oom_timer.data = (unsigned long)dev; + tp->oom_timer.function = oom_timer; +#endif +} + +static int +tulip_open(struct net_device *dev) +{ + int retval; + + tulip_init_ring (dev); + + retval = request_irq(dev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev); + if (retval) + goto free_ring; + + tulip_up (dev); + + netif_start_queue (dev); + + return 0; + +free_ring: + tulip_free_ring (dev); + return retval; +} + + +static void tulip_tx_timeout(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + unsigned long flags; + + spin_lock_irqsave (&tp->lock, flags); + + if (tulip_media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + dev_warn(&dev->dev, + "Transmit timeout using MII device\n"); + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 || + tp->chip_id == MX98713 || tp->chip_id == COMPEX9881 || + tp->chip_id == DM910X) { + dev_warn(&dev->dev, + "21140 transmit timed out, status %08x, SIA %08x %08x %08x %08x, resetting...\n", + ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR12), + ioread32(ioaddr + CSR13), ioread32(ioaddr + CSR14), + ioread32(ioaddr + CSR15)); + tp->timeout_recovery = 1; + schedule_work(&tp->media_work); + goto out_unlock; + } else if (tp->chip_id == PNIC2) { + dev_warn(&dev->dev, + "PNIC2 transmit timed out, status %08x, CSR6/7 %08x / %08x CSR12 %08x, resetting...\n", + (int)ioread32(ioaddr + CSR5), + (int)ioread32(ioaddr + CSR6), + (int)ioread32(ioaddr + CSR7), + (int)ioread32(ioaddr + CSR12)); + } else { + dev_warn(&dev->dev, + "Transmit timed out, status %08x, CSR12 %08x, resetting...\n", + ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR12)); + dev->if_port = 0; + } + +#if defined(way_too_many_messages) + if (tulip_debug > 3) { + int i; + for (i = 0; i < RX_RING_SIZE; i++) { + u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); + int j; + printk(KERN_DEBUG + "%2d: %08x %08x %08x %08x %02x %02x %02x\n", + i, + (unsigned int)tp->rx_ring[i].status, + (unsigned int)tp->rx_ring[i].length, + (unsigned int)tp->rx_ring[i].buffer1, + (unsigned int)tp->rx_ring[i].buffer2, + buf[0], buf[1], buf[2]); + for (j = 0; buf[j] != 0xee && j < 1600; j++) + if (j < 100) + pr_cont(" %02x", buf[j]); + pr_cont(" j=%d\n", j); + } + printk(KERN_DEBUG " Rx ring %p: ", tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + pr_cont(" %08x", (unsigned int)tp->rx_ring[i].status); + printk(KERN_DEBUG " Tx ring %p: ", tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + pr_cont(" %08x", (unsigned int)tp->tx_ring[i].status); + pr_cont("\n"); + } +#endif + + tulip_tx_timeout_complete(tp, ioaddr); + +out_unlock: + spin_unlock_irqrestore (&tp->lock, flags); + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue (dev); +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void tulip_init_ring(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int i; + + tp->susp_rx = 0; + tp->ttimer = 0; + tp->nir = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + tp->rx_ring[i].status = 0x00000000; + tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ); + tp->rx_ring[i].buffer2 = cpu_to_le32(tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * (i + 1)); + tp->rx_buffers[i].skb = NULL; + tp->rx_buffers[i].mapping = 0; + } + /* Mark the last entry as wrapping the ring. */ + tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | DESC_RING_WRAP); + tp->rx_ring[i-1].buffer2 = cpu_to_le32(tp->rx_ring_dma); + + for (i = 0; i < RX_RING_SIZE; i++) { + dma_addr_t mapping; + + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); + tp->rx_buffers[i].skb = skb; + if (skb == NULL) + break; + mapping = pci_map_single(tp->pdev, skb->data, + PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + tp->rx_buffers[i].mapping = mapping; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[i].status = cpu_to_le32(DescOwned); /* Owned by Tulip chip */ + tp->rx_ring[i].buffer1 = cpu_to_le32(mapping); + } + tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_buffers[i].skb = NULL; + tp->tx_buffers[i].mapping = 0; + tp->tx_ring[i].status = 0x00000000; + tp->tx_ring[i].buffer2 = cpu_to_le32(tp->tx_ring_dma + sizeof(struct tulip_tx_desc) * (i + 1)); + } + tp->tx_ring[i-1].buffer2 = cpu_to_le32(tp->tx_ring_dma); +} + +static netdev_tx_t +tulip_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int entry; + u32 flag; + dma_addr_t mapping; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + tp->tx_buffers[entry].skb = skb; + mapping = pci_map_single(tp->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE); + tp->tx_buffers[entry].mapping = mapping; + tp->tx_ring[entry].buffer1 = cpu_to_le32(mapping); + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = 0x60000000; /* No interrupt */ + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = 0xe0000000; /* Tx-done intr. */ + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = 0x60000000; /* No Tx-done intr. */ + } else { /* Leave room for set_rx_mode() to fill entries. */ + flag = 0xe0000000; /* Tx-done intr. */ + netif_stop_queue(dev); + } + if (entry == TX_RING_SIZE-1) + flag = 0xe0000000 | DESC_RING_WRAP; + + tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag); + /* if we were using Transmit Automatic Polling, we would need a + * wmb() here. */ + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); + wmb(); + + tp->cur_tx++; + + /* Trigger an immediate transmit demand. */ + iowrite32(0, tp->base_addr + CSR1); + + spin_unlock_irqrestore(&tp->lock, flags); + + return NETDEV_TX_OK; +} + +static void tulip_clean_tx_ring(struct tulip_private *tp) +{ + unsigned int dirty_tx; + + for (dirty_tx = tp->dirty_tx ; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = le32_to_cpu(tp->tx_ring[entry].status); + + if (status < 0) { + tp->dev->stats.tx_errors++; /* It wasn't Txed */ + tp->tx_ring[entry].status = 0; + } + + /* Check for Tx filter setup frames. */ + if (tp->tx_buffers[entry].skb == NULL) { + /* test because dummy frames not mapped */ + if (tp->tx_buffers[entry].mapping) + pci_unmap_single(tp->pdev, + tp->tx_buffers[entry].mapping, + sizeof(tp->setup_frame), + PCI_DMA_TODEVICE); + continue; + } + + pci_unmap_single(tp->pdev, tp->tx_buffers[entry].mapping, + tp->tx_buffers[entry].skb->len, + PCI_DMA_TODEVICE); + + /* Free the original skb. */ + dev_kfree_skb_irq(tp->tx_buffers[entry].skb); + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = 0; + } +} + +static void tulip_down (struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + unsigned long flags; + + cancel_work_sync(&tp->media_work); + +#ifdef CONFIG_TULIP_NAPI + napi_disable(&tp->napi); +#endif + + del_timer_sync (&tp->timer); +#ifdef CONFIG_TULIP_NAPI + del_timer_sync (&tp->oom_timer); +#endif + spin_lock_irqsave (&tp->lock, flags); + + /* Disable interrupts by clearing the interrupt mask. */ + iowrite32 (0x00000000, ioaddr + CSR7); + + /* Stop the Tx and Rx processes. */ + tulip_stop_rxtx(tp); + + /* prepare receive buffers */ + tulip_refill_rx(dev); + + /* release any unconsumed transmit buffers */ + tulip_clean_tx_ring(tp); + + if (ioread32(ioaddr + CSR6) != 0xffffffff) + dev->stats.rx_missed_errors += ioread32(ioaddr + CSR8) & 0xffff; + + spin_unlock_irqrestore (&tp->lock, flags); + + init_timer(&tp->timer); + tp->timer.data = (unsigned long)dev; + tp->timer.function = tulip_tbl[tp->chip_id].media_timer; + + dev->if_port = tp->saved_if_port; + + /* Leave the driver in snooze, not sleep, mode. */ + tulip_set_power_state (tp, 0, 1); +} + +static void tulip_free_ring (struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + int i; + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_buffers[i].skb; + dma_addr_t mapping = tp->rx_buffers[i].mapping; + + tp->rx_buffers[i].skb = NULL; + tp->rx_buffers[i].mapping = 0; + + tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */ + tp->rx_ring[i].length = 0; + /* An invalid address. */ + tp->rx_ring[i].buffer1 = cpu_to_le32(0xBADF00D0); + if (skb) { + pci_unmap_single(tp->pdev, mapping, PKT_BUF_SZ, + PCI_DMA_FROMDEVICE); + dev_kfree_skb (skb); + } + } + + for (i = 0; i < TX_RING_SIZE; i++) { + struct sk_buff *skb = tp->tx_buffers[i].skb; + + if (skb != NULL) { + pci_unmap_single(tp->pdev, tp->tx_buffers[i].mapping, + skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb (skb); + } + tp->tx_buffers[i].skb = NULL; + tp->tx_buffers[i].mapping = 0; + } +} + +static int tulip_close (struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + + netif_stop_queue (dev); + + tulip_down (dev); + + if (tulip_debug > 1) + netdev_dbg(dev, "Shutting down ethercard, status was %02x\n", + ioread32 (ioaddr + CSR5)); + + free_irq (dev->irq, dev); + + tulip_free_ring (dev); + + return 0; +} + +static struct net_device_stats *tulip_get_stats(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + + if (netif_running(dev)) { + unsigned long flags; + + spin_lock_irqsave (&tp->lock, flags); + + dev->stats.rx_missed_errors += ioread32(ioaddr + CSR8) & 0xffff; + + spin_unlock_irqrestore(&tp->lock, flags); + } + + return &dev->stats; +} + + +static void tulip_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct tulip_private *np = netdev_priv(dev); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pdev)); +} + + +static int tulip_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct tulip_private *tp = netdev_priv(dev); + + if (wolinfo->wolopts & (~tp->wolinfo.supported)) + return -EOPNOTSUPP; + + tp->wolinfo.wolopts = wolinfo->wolopts; + device_set_wakeup_enable(&tp->pdev->dev, tp->wolinfo.wolopts); + return 0; +} + +static void tulip_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct tulip_private *tp = netdev_priv(dev); + + wolinfo->supported = tp->wolinfo.supported; + wolinfo->wolopts = tp->wolinfo.wolopts; + return; +} + + +static const struct ethtool_ops ops = { + .get_drvinfo = tulip_get_drvinfo, + .set_wol = tulip_ethtool_set_wol, + .get_wol = tulip_ethtool_get_wol, +}; + +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int private_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + struct mii_ioctl_data *data = if_mii(rq); + const unsigned int phy_idx = 0; + int phy = tp->phys[phy_idx] & 0x1f; + unsigned int regnum = data->reg_num; + + switch (cmd) { + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + if (tp->mii_cnt) + data->phy_id = phy; + else if (tp->flags & HAS_NWAY) + data->phy_id = 32; + else if (tp->chip_id == COMET) + data->phy_id = 1; + else + return -ENODEV; + + case SIOCGMIIREG: /* Read MII PHY register. */ + if (data->phy_id == 32 && (tp->flags & HAS_NWAY)) { + int csr12 = ioread32 (ioaddr + CSR12); + int csr14 = ioread32 (ioaddr + CSR14); + switch (regnum) { + case 0: + if (((csr14<<5) & 0x1000) || + (dev->if_port == 5 && tp->nwayset)) + data->val_out = 0x1000; + else + data->val_out = (tulip_media_cap[dev->if_port]&MediaIs100 ? 0x2000 : 0) + | (tulip_media_cap[dev->if_port]&MediaIsFD ? 0x0100 : 0); + break; + case 1: + data->val_out = + 0x1848 + + ((csr12&0x7000) == 0x5000 ? 0x20 : 0) + + ((csr12&0x06) == 6 ? 0 : 4); + data->val_out |= 0x6048; + break; + case 4: + /* Advertised value, bogus 10baseTx-FD value from CSR6. */ + data->val_out = + ((ioread32(ioaddr + CSR6) >> 3) & 0x0040) + + ((csr14 >> 1) & 0x20) + 1; + data->val_out |= ((csr14 >> 9) & 0x03C0); + break; + case 5: data->val_out = tp->lpar; break; + default: data->val_out = 0; break; + } + } else { + data->val_out = tulip_mdio_read (dev, data->phy_id & 0x1f, regnum); + } + return 0; + + case SIOCSMIIREG: /* Write MII PHY register. */ + if (regnum & ~0x1f) + return -EINVAL; + if (data->phy_id == phy) { + u16 value = data->val_in; + switch (regnum) { + case 0: /* Check for autonegotiation on or reset. */ + tp->full_duplex_lock = (value & 0x9000) ? 0 : 1; + if (tp->full_duplex_lock) + tp->full_duplex = (value & 0x0100) ? 1 : 0; + break; + case 4: + tp->advertising[phy_idx] = + tp->mii_advertise = data->val_in; + break; + } + } + if (data->phy_id == 32 && (tp->flags & HAS_NWAY)) { + u16 value = data->val_in; + if (regnum == 0) { + if ((value & 0x1200) == 0x1200) { + if (tp->chip_id == PNIC2) { + pnic2_start_nway (dev); + } else { + t21142_start_nway (dev); + } + } + } else if (regnum == 4) + tp->sym_advertise = value; + } else { + tulip_mdio_write (dev, data->phy_id & 0x1f, regnum, data->val_in); + } + return 0; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + + +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling tp->setup_frame. This is non-deterministic + when re-entered but still correct. */ + +#undef set_bit_le +#define set_bit_le(i,p) do { ((char *)(p))[(i)/8] |= (1<<((i)%8)); } while(0) + +static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + u16 hash_table[32]; + struct netdev_hw_addr *ha; + int i; + u16 *eaddrs; + + memset(hash_table, 0, sizeof(hash_table)); + set_bit_le(255, hash_table); /* Broadcast entry */ + /* This should work on big-endian machines as well. */ + netdev_for_each_mc_addr(ha, dev) { + int index = ether_crc_le(ETH_ALEN, ha->addr) & 0x1ff; + + set_bit_le(index, hash_table); + } + for (i = 0; i < 32; i++) { + *setup_frm++ = hash_table[i]; + *setup_frm++ = hash_table[i]; + } + setup_frm = &tp->setup_frame[13*6]; + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +} + +static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + struct netdev_hw_addr *ha; + u16 *eaddrs; + + /* We have <= 14 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + netdev_for_each_mc_addr(ha, dev) { + eaddrs = (u16 *) ha->addr; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + } + /* Fill the unused entries with the broadcast address. */ + memset(setup_frm, 0xff, (15 - netdev_mc_count(dev)) * 12); + setup_frm = &tp->setup_frame[15*6]; + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +} + + +static void set_rx_mode(struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int csr6; + + csr6 = ioread32(ioaddr + CSR6) & ~0x00D5; + + tp->csr6 &= ~0x00D5; + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + tp->csr6 |= AcceptAllMulticast | AcceptAllPhys; + csr6 |= AcceptAllMulticast | AcceptAllPhys; + } else if ((netdev_mc_count(dev) > 1000) || + (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well -- accept all multicasts. */ + tp->csr6 |= AcceptAllMulticast; + csr6 |= AcceptAllMulticast; + } else if (tp->flags & MC_HASH_ONLY) { + /* Some work-alikes have only a 64-entry hash filter table. */ + /* Should verify correctness on big-endian/__powerpc__ */ + struct netdev_hw_addr *ha; + if (netdev_mc_count(dev) > 64) { + /* Arbitrary non-effective limit. */ + tp->csr6 |= AcceptAllMulticast; + csr6 |= AcceptAllMulticast; + } else { + u32 mc_filter[2] = {0, 0}; /* Multicast hash filter */ + int filterbit; + netdev_for_each_mc_addr(ha, dev) { + if (tp->flags & COMET_MAC_ADDR) + filterbit = ether_crc_le(ETH_ALEN, + ha->addr); + else + filterbit = ether_crc(ETH_ALEN, + ha->addr) >> 26; + filterbit &= 0x3f; + mc_filter[filterbit >> 5] |= 1 << (filterbit & 31); + if (tulip_debug > 2) + dev_info(&dev->dev, + "Added filter for %pM %08x bit %d\n", + ha->addr, + ether_crc(ETH_ALEN, ha->addr), + filterbit); + } + if (mc_filter[0] == tp->mc_filter[0] && + mc_filter[1] == tp->mc_filter[1]) + ; /* No change. */ + else if (tp->flags & IS_ASIX) { + iowrite32(2, ioaddr + CSR13); + iowrite32(mc_filter[0], ioaddr + CSR14); + iowrite32(3, ioaddr + CSR13); + iowrite32(mc_filter[1], ioaddr + CSR14); + } else if (tp->flags & COMET_MAC_ADDR) { + iowrite32(mc_filter[0], ioaddr + CSR27); + iowrite32(mc_filter[1], ioaddr + CSR28); + } + tp->mc_filter[0] = mc_filter[0]; + tp->mc_filter[1] = mc_filter[1]; + } + } else { + unsigned long flags; + u32 tx_flags = 0x08000000 | 192; + + /* Note that only the low-address shortword of setup_frame is valid! + The values are doubled for big-endian architectures. */ + if (netdev_mc_count(dev) > 14) { + /* Must use a multicast hash table. */ + build_setup_frame_hash(tp->setup_frame, dev); + tx_flags = 0x08400000 | 192; + } else { + build_setup_frame_perfect(tp->setup_frame, dev); + } + + spin_lock_irqsave(&tp->lock, flags); + + if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { + /* Same setup recently queued, we need not add it. */ + } else { + unsigned int entry; + int dummy = -1; + + /* Now add this frame to the Tx list. */ + + entry = tp->cur_tx++ % TX_RING_SIZE; + + if (entry != 0) { + /* Avoid a chip errata by prefixing a dummy entry. */ + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = 0; + tp->tx_ring[entry].length = + (entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0; + tp->tx_ring[entry].buffer1 = 0; + /* Must set DescOwned later to avoid race with chip */ + dummy = entry; + entry = tp->cur_tx++ % TX_RING_SIZE; + + } + + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = + pci_map_single(tp->pdev, tp->setup_frame, + sizeof(tp->setup_frame), + PCI_DMA_TODEVICE); + /* Put the setup frame on the Tx list. */ + if (entry == TX_RING_SIZE-1) + tx_flags |= DESC_RING_WRAP; /* Wrap ring. */ + tp->tx_ring[entry].length = cpu_to_le32(tx_flags); + tp->tx_ring[entry].buffer1 = + cpu_to_le32(tp->tx_buffers[entry].mapping); + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); + if (dummy >= 0) + tp->tx_ring[dummy].status = cpu_to_le32(DescOwned); + if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) + netif_stop_queue(dev); + + /* Trigger an immediate transmit demand. */ + iowrite32(0, ioaddr + CSR1); + } + + spin_unlock_irqrestore(&tp->lock, flags); + } + + iowrite32(csr6, ioaddr + CSR6); +} + +#ifdef CONFIG_TULIP_MWI +static void __devinit tulip_mwi_config (struct pci_dev *pdev, + struct net_device *dev) +{ + struct tulip_private *tp = netdev_priv(dev); + u8 cache; + u16 pci_command; + u32 csr0; + + if (tulip_debug > 3) + netdev_dbg(dev, "tulip_mwi_config()\n"); + + tp->csr0 = csr0 = 0; + + /* if we have any cache line size at all, we can do MRM and MWI */ + csr0 |= MRM | MWI; + + /* Enable MWI in the standard PCI command bit. + * Check for the case where MWI is desired but not available + */ + pci_try_set_mwi(pdev); + + /* read result from hardware (in case bit refused to enable) */ + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + if ((csr0 & MWI) && (!(pci_command & PCI_COMMAND_INVALIDATE))) + csr0 &= ~MWI; + + /* if cache line size hardwired to zero, no MWI */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); + if ((csr0 & MWI) && (cache == 0)) { + csr0 &= ~MWI; + pci_clear_mwi(pdev); + } + + /* assign per-cacheline-size cache alignment and + * burst length values + */ + switch (cache) { + case 8: + csr0 |= MRL | (1 << CALShift) | (16 << BurstLenShift); + break; + case 16: + csr0 |= MRL | (2 << CALShift) | (16 << BurstLenShift); + break; + case 32: + csr0 |= MRL | (3 << CALShift) | (32 << BurstLenShift); + break; + default: + cache = 0; + break; + } + + /* if we have a good cache line size, we by now have a good + * csr0, so save it and exit + */ + if (cache) + goto out; + + /* we don't have a good csr0 or cache line size, disable MWI */ + if (csr0 & MWI) { + pci_clear_mwi(pdev); + csr0 &= ~MWI; + } + + /* sane defaults for burst length and cache alignment + * originally from de4x5 driver + */ + csr0 |= (8 << BurstLenShift) | (1 << CALShift); + +out: + tp->csr0 = csr0; + if (tulip_debug > 2) + netdev_dbg(dev, "MWI config cacheline=%d, csr0=%08x\n", + cache, csr0); +} +#endif + +/* + * Chips that have the MRM/reserved bit quirk and the burst quirk. That + * is the DM910X and the on chip ULi devices + */ + +static int tulip_uli_dm_quirk(struct pci_dev *pdev) +{ + if (pdev->vendor == 0x1282 && pdev->device == 0x9102) + return 1; + return 0; +} + +static const struct net_device_ops tulip_netdev_ops = { + .ndo_open = tulip_open, + .ndo_start_xmit = tulip_start_xmit, + .ndo_tx_timeout = tulip_tx_timeout, + .ndo_stop = tulip_close, + .ndo_get_stats = tulip_get_stats, + .ndo_do_ioctl = private_ioctl, + .ndo_set_multicast_list = set_rx_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = poll_tulip, +#endif +}; + +DEFINE_PCI_DEVICE_TABLE(early_486_chipsets) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424) }, + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496) }, + { }, +}; + +static int __devinit tulip_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct tulip_private *tp; + /* See note below on the multiport cards. */ + static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; + static int last_irq; + static int multiport_cnt; /* For four-port boards w/one EEPROM */ + int i, irq; + unsigned short sum; + unsigned char *ee_data; + struct net_device *dev; + void __iomem *ioaddr; + static int board_idx = -1; + int chip_idx = ent->driver_data; + const char *chip_name = tulip_tbl[chip_idx].chip_name; + unsigned int eeprom_missing = 0; + unsigned int force_csr0 = 0; + +#ifndef MODULE + if (tulip_debug > 0) + printk_once(KERN_INFO "%s", version); +#endif + + board_idx++; + + /* + * Lan media wire a tulip chip to a wan interface. Needs a very + * different driver (lmc driver) + */ + + if (pdev->subsystem_vendor == PCI_VENDOR_ID_LMC) { + pr_err("skipping LMC card\n"); + return -ENODEV; + } else if (pdev->subsystem_vendor == PCI_VENDOR_ID_SBE && + (pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_T3E3 || + pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_2T3E3_P0 || + pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_2T3E3_P1)) { + pr_err("skipping SBE T3E3 port\n"); + return -ENODEV; + } + + /* + * DM910x chips should be handled by the dmfe driver, except + * on-board chips on SPARC systems. Also, early DM9100s need + * software CRC which only the dmfe driver supports. + */ + +#ifdef CONFIG_TULIP_DM910X + if (chip_idx == DM910X) { + struct device_node *dp; + + if (pdev->vendor == 0x1282 && pdev->device == 0x9100 && + pdev->revision < 0x30) { + pr_info("skipping early DM9100 with Crc bug (use dmfe)\n"); + return -ENODEV; + } + + dp = pci_device_to_OF_node(pdev); + if (!(dp && of_get_property(dp, "local-mac-address", NULL))) { + pr_info("skipping DM910x expansion card (use dmfe)\n"); + return -ENODEV; + } + } +#endif + + /* + * Looks for early PCI chipsets where people report hangs + * without the workarounds being on. + */ + + /* 1. Intel Saturn. Switch to 8 long words burst, 8 long word cache + aligned. Aries might need this too. The Saturn errata are not + pretty reading but thankfully it's an old 486 chipset. + + 2. The dreaded SiS496 486 chipset. Same workaround as Intel + Saturn. + */ + + if (pci_dev_present(early_486_chipsets)) { + csr0 = MRL | MRM | (8 << BurstLenShift) | (1 << CALShift); + force_csr0 = 1; + } + + /* bugfix: the ASIX must have a burst limit or horrible things happen. */ + if (chip_idx == AX88140) { + if ((csr0 & 0x3f00) == 0) + csr0 |= 0x2000; + } + + /* PNIC doesn't have MWI/MRL/MRM... */ + if (chip_idx == LC82C168) + csr0 &= ~0xfff10000; /* zero reserved bits 31:20, 16 */ + + /* DM9102A has troubles with MRM & clear reserved bits 24:22, 20, 16, 7:1 */ + if (tulip_uli_dm_quirk(pdev)) { + csr0 &= ~0x01f100ff; +#if defined(CONFIG_SPARC) + csr0 = (csr0 & ~0xff00) | 0xe000; +#endif + } + /* + * And back to business + */ + + i = pci_enable_device(pdev); + if (i) { + pr_err("Cannot enable tulip board #%d, aborting\n", board_idx); + return i; + } + + /* The chip will fail to enter a low-power state later unless + * first explicitly commanded into D0 */ + if (pci_set_power_state(pdev, PCI_D0)) { + pr_notice("Failed to set power state to D0\n"); + } + + irq = pdev->irq; + + /* alloc_etherdev ensures aligned and zeroed private structures */ + dev = alloc_etherdev (sizeof (*tp)); + if (!dev) { + pr_err("ether device alloc failed, aborting\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + if (pci_resource_len (pdev, 0) < tulip_tbl[chip_idx].io_size) { + pr_err("%s: I/O region (0x%llx@0x%llx) too small, aborting\n", + pci_name(pdev), + (unsigned long long)pci_resource_len (pdev, 0), + (unsigned long long)pci_resource_start (pdev, 0)); + goto err_out_free_netdev; + } + + /* grab all resources from both PIO and MMIO regions, as we + * don't want anyone else messing around with our hardware */ + if (pci_request_regions (pdev, DRV_NAME)) + goto err_out_free_netdev; + + ioaddr = pci_iomap(pdev, TULIP_BAR, tulip_tbl[chip_idx].io_size); + + if (!ioaddr) + goto err_out_free_res; + + /* + * initialize private data structure 'tp' + * it is zeroed and aligned in alloc_etherdev + */ + tp = netdev_priv(dev); + tp->dev = dev; + + tp->rx_ring = pci_alloc_consistent(pdev, + sizeof(struct tulip_rx_desc) * RX_RING_SIZE + + sizeof(struct tulip_tx_desc) * TX_RING_SIZE, + &tp->rx_ring_dma); + if (!tp->rx_ring) + goto err_out_mtable; + tp->tx_ring = (struct tulip_tx_desc *)(tp->rx_ring + RX_RING_SIZE); + tp->tx_ring_dma = tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * RX_RING_SIZE; + + tp->chip_id = chip_idx; + tp->flags = tulip_tbl[chip_idx].flags; + + tp->wolinfo.supported = 0; + tp->wolinfo.wolopts = 0; + /* COMET: Enable power management only for AN983B */ + if (chip_idx == COMET ) { + u32 sig; + pci_read_config_dword (pdev, 0x80, &sig); + if (sig == 0x09811317) { + tp->flags |= COMET_PM; + tp->wolinfo.supported = WAKE_PHY | WAKE_MAGIC; + pr_info("%s: Enabled WOL support for AN983B\n", + __func__); + } + } + tp->pdev = pdev; + tp->base_addr = ioaddr; + tp->revision = pdev->revision; + tp->csr0 = csr0; + spin_lock_init(&tp->lock); + spin_lock_init(&tp->mii_lock); + init_timer(&tp->timer); + tp->timer.data = (unsigned long)dev; + tp->timer.function = tulip_tbl[tp->chip_id].media_timer; + + INIT_WORK(&tp->media_work, tulip_tbl[tp->chip_id].media_task); + + dev->base_addr = (unsigned long)ioaddr; + +#ifdef CONFIG_TULIP_MWI + if (!force_csr0 && (tp->flags & HAS_PCI_MWI)) + tulip_mwi_config (pdev, dev); +#endif + + /* Stop the chip's Tx and Rx processes. */ + tulip_stop_rxtx(tp); + + pci_set_master(pdev); + +#ifdef CONFIG_GSC + if (pdev->subsystem_vendor == PCI_VENDOR_ID_HP) { + switch (pdev->subsystem_device) { + default: + break; + case 0x1061: + case 0x1062: + case 0x1063: + case 0x1098: + case 0x1099: + case 0x10EE: + tp->flags |= HAS_SWAPPED_SEEPROM | NEEDS_FAKE_MEDIA_TABLE; + chip_name = "GSC DS21140 Tulip"; + } + } +#endif + + /* Clear the missed-packet counter. */ + ioread32(ioaddr + CSR8); + + /* The station address ROM is read byte serially. The register must + be polled, waiting for the value to be read bit serially from the + EEPROM. + */ + ee_data = tp->eeprom; + memset(ee_data, 0, sizeof(tp->eeprom)); + sum = 0; + if (chip_idx == LC82C168) { + for (i = 0; i < 3; i++) { + int value, boguscnt = 100000; + iowrite32(0x600 | i, ioaddr + 0x98); + do { + value = ioread32(ioaddr + CSR9); + } while (value < 0 && --boguscnt > 0); + put_unaligned_le16(value, ((__le16 *)dev->dev_addr) + i); + sum += value & 0xffff; + } + } else if (chip_idx == COMET) { + /* No need to read the EEPROM. */ + put_unaligned_le32(ioread32(ioaddr + 0xA4), dev->dev_addr); + put_unaligned_le16(ioread32(ioaddr + 0xA8), dev->dev_addr + 4); + for (i = 0; i < 6; i ++) + sum += dev->dev_addr[i]; + } else { + /* A serial EEPROM interface, we read now and sort it out later. */ + int sa_offset = 0; + int ee_addr_size = tulip_read_eeprom(dev, 0xff, 8) & 0x40000 ? 8 : 6; + int ee_max_addr = ((1 << ee_addr_size) - 1) * sizeof(u16); + + if (ee_max_addr > sizeof(tp->eeprom)) + ee_max_addr = sizeof(tp->eeprom); + + for (i = 0; i < ee_max_addr ; i += sizeof(u16)) { + u16 data = tulip_read_eeprom(dev, i/2, ee_addr_size); + ee_data[i] = data & 0xff; + ee_data[i + 1] = data >> 8; + } + + /* DEC now has a specification (see Notes) but early board makers + just put the address in the first EEPROM locations. */ + /* This does memcmp(ee_data, ee_data+16, 8) */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + if (chip_idx == CONEXANT) { + /* Check that the tuple type and length is correct. */ + if (ee_data[0x198] == 0x04 && ee_data[0x199] == 6) + sa_offset = 0x19A; + } else if (ee_data[0] == 0xff && ee_data[1] == 0xff && + ee_data[2] == 0) { + sa_offset = 2; /* Grrr, damn Matrox boards. */ + multiport_cnt = 4; + } +#ifdef CONFIG_MIPS_COBALT + if ((pdev->bus->number == 0) && + ((PCI_SLOT(pdev->devfn) == 7) || + (PCI_SLOT(pdev->devfn) == 12))) { + /* Cobalt MAC address in first EEPROM locations. */ + sa_offset = 0; + /* Ensure our media table fixup get's applied */ + memcpy(ee_data + 16, ee_data, 8); + } +#endif +#ifdef CONFIG_GSC + /* Check to see if we have a broken srom */ + if (ee_data[0] == 0x61 && ee_data[1] == 0x10) { + /* pci_vendor_id and subsystem_id are swapped */ + ee_data[0] = ee_data[2]; + ee_data[1] = ee_data[3]; + ee_data[2] = 0x61; + ee_data[3] = 0x10; + + /* HSC-PCI boards need to be byte-swaped and shifted + * up 1 word. This shift needs to happen at the end + * of the MAC first because of the 2 byte overlap. + */ + for (i = 4; i >= 0; i -= 2) { + ee_data[17 + i + 3] = ee_data[17 + i]; + ee_data[16 + i + 5] = ee_data[16 + i]; + } + } +#endif + + for (i = 0; i < 6; i ++) { + dev->dev_addr[i] = ee_data[i + sa_offset]; + sum += ee_data[i + sa_offset]; + } + } + /* Lite-On boards have the address byte-swapped. */ + if ((dev->dev_addr[0] == 0xA0 || + dev->dev_addr[0] == 0xC0 || + dev->dev_addr[0] == 0x02) && + dev->dev_addr[1] == 0x00) + for (i = 0; i < 6; i+=2) { + char tmp = dev->dev_addr[i]; + dev->dev_addr[i] = dev->dev_addr[i+1]; + dev->dev_addr[i+1] = tmp; + } + /* On the Zynx 315 Etherarray and other multiport boards only the + first Tulip has an EEPROM. + On Sparc systems the mac address is held in the OBP property + "local-mac-address". + The addresses of the subsequent ports are derived from the first. + Many PCI BIOSes also incorrectly report the IRQ line, so we correct + that here as well. */ + if (sum == 0 || sum == 6*0xff) { +#if defined(CONFIG_SPARC) + struct device_node *dp = pci_device_to_OF_node(pdev); + const unsigned char *addr; + int len; +#endif + eeprom_missing = 1; + for (i = 0; i < 5; i++) + dev->dev_addr[i] = last_phys_addr[i]; + dev->dev_addr[i] = last_phys_addr[i] + 1; +#if defined(CONFIG_SPARC) + addr = of_get_property(dp, "local-mac-address", &len); + if (addr && len == 6) + memcpy(dev->dev_addr, addr, 6); +#endif +#if defined(__i386__) || defined(__x86_64__) /* Patch up x86 BIOS bug. */ + if (last_irq) + irq = last_irq; +#endif + } + + for (i = 0; i < 6; i++) + last_phys_addr[i] = dev->dev_addr[i]; + last_irq = irq; + dev->irq = irq; + + /* The lower four bits are the media type. */ + if (board_idx >= 0 && board_idx < MAX_UNITS) { + if (options[board_idx] & MEDIA_MASK) + tp->default_port = options[board_idx] & MEDIA_MASK; + if ((options[board_idx] & FullDuplex) || full_duplex[board_idx] > 0) + tp->full_duplex = 1; + if (mtu[board_idx] > 0) + dev->mtu = mtu[board_idx]; + } + if (dev->mem_start & MEDIA_MASK) + tp->default_port = dev->mem_start & MEDIA_MASK; + if (tp->default_port) { + pr_info(DRV_NAME "%d: Transceiver selection forced to %s\n", + board_idx, medianame[tp->default_port & MEDIA_MASK]); + tp->medialock = 1; + if (tulip_media_cap[tp->default_port] & MediaAlwaysFD) + tp->full_duplex = 1; + } + if (tp->full_duplex) + tp->full_duplex_lock = 1; + + if (tulip_media_cap[tp->default_port] & MediaIsMII) { + static const u16 media2advert[] = { + 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 + }; + tp->mii_advertise = media2advert[tp->default_port - 9]; + tp->mii_advertise |= (tp->flags & HAS_8023X); /* Matching bits! */ + } + + if (tp->flags & HAS_MEDIA_TABLE) { + sprintf(dev->name, DRV_NAME "%d", board_idx); /* hack */ + tulip_parse_eeprom(dev); + strcpy(dev->name, "eth%d"); /* un-hack */ + } + + if ((tp->flags & ALWAYS_CHECK_MII) || + (tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tp->flags & HAS_MII))) { + if (tp->mtable && tp->mtable->has_mii) { + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == 11) { + tp->cur_index = i; + tp->saved_if_port = dev->if_port; + tulip_select_media(dev, 2); + dev->if_port = tp->saved_if_port; + break; + } + } + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs + later, but takes much time. */ + tulip_find_mii (dev, board_idx); + } + + /* The Tulip-specific entries in the device structure. */ + dev->netdev_ops = &tulip_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; +#ifdef CONFIG_TULIP_NAPI + netif_napi_add(dev, &tp->napi, tulip_poll, 16); +#endif + SET_ETHTOOL_OPS(dev, &ops); + + if (register_netdev(dev)) + goto err_out_free_ring; + + pci_set_drvdata(pdev, dev); + + dev_info(&dev->dev, +#ifdef CONFIG_TULIP_MMIO + "%s rev %d at MMIO %#llx,%s %pM, IRQ %d\n", +#else + "%s rev %d at Port %#llx,%s %pM, IRQ %d\n", +#endif + chip_name, pdev->revision, + (unsigned long long)pci_resource_start(pdev, TULIP_BAR), + eeprom_missing ? " EEPROM not present," : "", + dev->dev_addr, irq); + + if (tp->chip_id == PNIC2) + tp->link_change = pnic2_lnk_change; + else if (tp->flags & HAS_NWAY) + tp->link_change = t21142_lnk_change; + else if (tp->flags & HAS_PNICNWAY) + tp->link_change = pnic_lnk_change; + + /* Reset the xcvr interface and turn on heartbeat. */ + switch (chip_idx) { + case DC21140: + case DM910X: + default: + if (tp->mtable) + iowrite32(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); + break; + case DC21142: + if (tp->mii_cnt || tulip_media_cap[dev->if_port] & MediaIsMII) { + iowrite32(csr6_mask_defstate, ioaddr + CSR6); + iowrite32(0x0000, ioaddr + CSR13); + iowrite32(0x0000, ioaddr + CSR14); + iowrite32(csr6_mask_hdcap, ioaddr + CSR6); + } else + t21142_start_nway(dev); + break; + case PNIC2: + /* just do a reset for sanity sake */ + iowrite32(0x0000, ioaddr + CSR13); + iowrite32(0x0000, ioaddr + CSR14); + break; + case LC82C168: + if ( ! tp->mii_cnt) { + tp->nway = 1; + tp->nwayset = 0; + iowrite32(csr6_ttm | csr6_ca, ioaddr + CSR6); + iowrite32(0x30, ioaddr + CSR12); + iowrite32(0x0001F078, ioaddr + CSR6); + iowrite32(0x0201F078, ioaddr + CSR6); /* Turn on autonegotiation. */ + } + break; + case MX98713: + case COMPEX9881: + iowrite32(0x00000000, ioaddr + CSR6); + iowrite32(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ + iowrite32(0x00000001, ioaddr + CSR13); + break; + case MX98715: + case MX98725: + iowrite32(0x01a80000, ioaddr + CSR6); + iowrite32(0xFFFFFFFF, ioaddr + CSR14); + iowrite32(0x00001000, ioaddr + CSR12); + break; + case COMET: + /* No initialization necessary. */ + break; + } + + /* put the chip in snooze mode until opened */ + tulip_set_power_state (tp, 0, 1); + + return 0; + +err_out_free_ring: + pci_free_consistent (pdev, + sizeof (struct tulip_rx_desc) * RX_RING_SIZE + + sizeof (struct tulip_tx_desc) * TX_RING_SIZE, + tp->rx_ring, tp->rx_ring_dma); + +err_out_mtable: + kfree (tp->mtable); + pci_iounmap(pdev, ioaddr); + +err_out_free_res: + pci_release_regions (pdev); + +err_out_free_netdev: + free_netdev (dev); + return -ENODEV; +} + + +/* set the registers according to the given wolopts */ +static void tulip_set_wolopts (struct pci_dev *pdev, u32 wolopts) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + + if (tp->flags & COMET_PM) { + + unsigned int tmp; + + tmp = ioread32(ioaddr + CSR18); + tmp &= ~(comet_csr18_pmes_sticky | comet_csr18_apm_mode | comet_csr18_d3a); + tmp |= comet_csr18_pm_mode; + iowrite32(tmp, ioaddr + CSR18); + + /* Set the Wake-up Control/Status Register to the given WOL options*/ + tmp = ioread32(ioaddr + CSR13); + tmp &= ~(comet_csr13_linkoffe | comet_csr13_linkone | comet_csr13_wfre | comet_csr13_lsce | comet_csr13_mpre); + if (wolopts & WAKE_MAGIC) + tmp |= comet_csr13_mpre; + if (wolopts & WAKE_PHY) + tmp |= comet_csr13_linkoffe | comet_csr13_linkone | comet_csr13_lsce; + /* Clear the event flags */ + tmp |= comet_csr13_wfr | comet_csr13_mpr | comet_csr13_lsc; + iowrite32(tmp, ioaddr + CSR13); + } +} + +#ifdef CONFIG_PM + + +static int tulip_suspend (struct pci_dev *pdev, pm_message_t state) +{ + pci_power_t pstate; + struct net_device *dev = pci_get_drvdata(pdev); + struct tulip_private *tp = netdev_priv(dev); + + if (!dev) + return -EINVAL; + + if (!netif_running(dev)) + goto save_state; + + tulip_down(dev); + + netif_device_detach(dev); + free_irq(dev->irq, dev); + +save_state: + pci_save_state(pdev); + pci_disable_device(pdev); + pstate = pci_choose_state(pdev, state); + if (state.event == PM_EVENT_SUSPEND && pstate != PCI_D0) { + int rc; + + tulip_set_wolopts(pdev, tp->wolinfo.wolopts); + rc = pci_enable_wake(pdev, pstate, tp->wolinfo.wolopts); + if (rc) + pr_err("pci_enable_wake failed (%d)\n", rc); + } + pci_set_power_state(pdev, pstate); + + return 0; +} + + +static int tulip_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tulip_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->base_addr; + int retval; + unsigned int tmp; + + if (!dev) + return -EINVAL; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + if (!netif_running(dev)) + return 0; + + if ((retval = pci_enable_device(pdev))) { + pr_err("pci_enable_device failed in resume\n"); + return retval; + } + + if ((retval = request_irq(dev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev))) { + pr_err("request_irq failed in resume\n"); + return retval; + } + + if (tp->flags & COMET_PM) { + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + /* Clear the PMES flag */ + tmp = ioread32(ioaddr + CSR20); + tmp |= comet_csr20_pmes; + iowrite32(tmp, ioaddr + CSR20); + + /* Disable all wake-up events */ + tulip_set_wolopts(pdev, 0); + } + netif_device_attach(dev); + + if (netif_running(dev)) + tulip_up(dev); + + return 0; +} + +#endif /* CONFIG_PM */ + + +static void __devexit tulip_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct tulip_private *tp; + + if (!dev) + return; + + tp = netdev_priv(dev); + unregister_netdev(dev); + pci_free_consistent (pdev, + sizeof (struct tulip_rx_desc) * RX_RING_SIZE + + sizeof (struct tulip_tx_desc) * TX_RING_SIZE, + tp->rx_ring, tp->rx_ring_dma); + kfree (tp->mtable); + pci_iounmap(pdev, tp->base_addr); + free_netdev (dev); + pci_release_regions (pdev); + pci_set_drvdata (pdev, NULL); + + /* pci_power_off (pdev, -1); */ +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ + +static void poll_tulip (struct net_device *dev) +{ + /* disable_irq here is not very nice, but with the lockless + interrupt handler we have no other choice. */ + disable_irq(dev->irq); + tulip_interrupt (dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +static struct pci_driver tulip_driver = { + .name = DRV_NAME, + .id_table = tulip_pci_tbl, + .probe = tulip_init_one, + .remove = __devexit_p(tulip_remove_one), +#ifdef CONFIG_PM + .suspend = tulip_suspend, + .resume = tulip_resume, +#endif /* CONFIG_PM */ +}; + + +static int __init tulip_init (void) +{ +#ifdef MODULE + pr_info("%s", version); +#endif + + /* copy module parms into globals */ + tulip_rx_copybreak = rx_copybreak; + tulip_max_interrupt_work = max_interrupt_work; + + /* probe for and init boards */ + return pci_register_driver(&tulip_driver); +} + + +static void __exit tulip_cleanup (void) +{ + pci_unregister_driver (&tulip_driver); +} + + +module_init(tulip_init); +module_exit(tulip_cleanup); diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c new file mode 100644 index 000000000000..9e63f406f72d --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -0,0 +1,1850 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "uli526x" +#define DRV_VERSION "0.9.3" +#define DRV_RELDATE "2005-7-29" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +/* Board/System/Debug information/definition ---------------- */ +#define PCI_ULI5261_ID 0x526110B9 /* ULi M5261 ID*/ +#define PCI_ULI5263_ID 0x526310B9 /* ULi M5263 ID*/ + +#define ULI526X_IO_SIZE 0x100 +#define TX_DESC_CNT 0x20 /* Allocated Tx descriptors */ +#define RX_DESC_CNT 0x30 /* Allocated Rx descriptors */ +#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ +#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ +#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) +#define TX_BUF_ALLOC 0x600 +#define RX_ALLOC_SIZE 0x620 +#define ULI526X_RESET 1 +#define CR0_DEFAULT 0 +#define CR6_DEFAULT 0x22200000 +#define CR7_DEFAULT 0x180c1 +#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ +#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ +#define MAX_PACKET_SIZE 1514 +#define ULI5261_MAX_MULTICAST 14 +#define RX_COPY_SIZE 100 +#define MAX_CHECK_PACKET 0x8000 + +#define ULI526X_10MHF 0 +#define ULI526X_100MHF 1 +#define ULI526X_10MFD 4 +#define ULI526X_100MFD 5 +#define ULI526X_AUTO 8 + +#define ULI526X_TXTH_72 0x400000 /* TX TH 72 byte */ +#define ULI526X_TXTH_96 0x404000 /* TX TH 96 byte */ +#define ULI526X_TXTH_128 0x0000 /* TX TH 128 byte */ +#define ULI526X_TXTH_256 0x4000 /* TX TH 256 byte */ +#define ULI526X_TXTH_512 0x8000 /* TX TH 512 byte */ +#define ULI526X_TXTH_1K 0xC000 /* TX TH 1K byte */ + +#define ULI526X_TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */ +#define ULI526X_TX_TIMEOUT ((16*HZ)/2) /* tx packet time-out time 8 s" */ +#define ULI526X_TX_KICK (4*HZ/2) /* tx packet Kick-out time 2 s" */ + +#define ULI526X_DBUG(dbug_now, msg, value) \ +do { \ + if (uli526x_debug || (dbug_now)) \ + pr_err("%s %lx\n", (msg), (long) (value)); \ +} while (0) + +#define SHOW_MEDIA_TYPE(mode) \ + pr_err("Change Speed to %sMhz %s duplex\n", \ + mode & 1 ? "100" : "10", \ + mode & 4 ? "full" : "half"); + + +/* CR9 definition: SROM/MII */ +#define CR9_SROM_READ 0x4800 +#define CR9_SRCS 0x1 +#define CR9_SRCLK 0x2 +#define CR9_CRDOUT 0x8 +#define SROM_DATA_0 0x0 +#define SROM_DATA_1 0x4 +#define PHY_DATA_1 0x20000 +#define PHY_DATA_0 0x00000 +#define MDCLKH 0x10000 + +#define PHY_POWER_DOWN 0x800 + +#define SROM_V41_CODE 0x14 + +#define SROM_CLK_WRITE(data, ioaddr) \ + outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr); \ + udelay(5); + +/* Structure/enum declaration ------------------------------- */ +struct tx_desc { + __le32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ + char *tx_buf_ptr; /* Data for us */ + struct tx_desc *next_tx_desc; +} __attribute__(( aligned(32) )); + +struct rx_desc { + __le32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ + struct sk_buff *rx_skb_ptr; /* Data for us */ + struct rx_desc *next_rx_desc; +} __attribute__(( aligned(32) )); + +struct uli526x_board_info { + u32 chip_id; /* Chip vendor/Device ID */ + struct net_device *next_dev; /* next device */ + struct pci_dev *pdev; /* PCI device */ + spinlock_t lock; + + long ioaddr; /* I/O base address */ + u32 cr0_data; + u32 cr5_data; + u32 cr6_data; + u32 cr7_data; + u32 cr15_data; + + /* pointer for memory physical address */ + dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */ + dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */ + dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */ + dma_addr_t first_tx_desc_dma; + dma_addr_t first_rx_desc_dma; + + /* descriptor pointer */ + unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ + unsigned char *buf_pool_start; /* Tx buffer pool align dword */ + unsigned char *desc_pool_ptr; /* descriptor pool memory */ + struct tx_desc *first_tx_desc; + struct tx_desc *tx_insert_ptr; + struct tx_desc *tx_remove_ptr; + struct rx_desc *first_rx_desc; + struct rx_desc *rx_insert_ptr; + struct rx_desc *rx_ready_ptr; /* packet come pointer */ + unsigned long tx_packet_cnt; /* transmitted packet count */ + unsigned long rx_avail_cnt; /* available rx descriptor count */ + unsigned long interval_rx_cnt; /* rx packet count a callback time */ + + u16 dbug_cnt; + u16 NIC_capability; /* NIC media capability */ + u16 PHY_reg4; /* Saved Phyxcer register 4 value */ + + u8 media_mode; /* user specify media mode */ + u8 op_mode; /* real work media mode */ + u8 phy_addr; + u8 link_failed; /* Ever link failed */ + u8 wait_reset; /* Hardware failed, need to reset */ + struct timer_list timer; + + /* Driver defined statistic counter */ + unsigned long tx_fifo_underrun; + unsigned long tx_loss_carrier; + unsigned long tx_no_carrier; + unsigned long tx_late_collision; + unsigned long tx_excessive_collision; + unsigned long tx_jabber_timeout; + unsigned long reset_count; + unsigned long reset_cr8; + unsigned long reset_fatal; + unsigned long reset_TXtimeout; + + /* NIC SROM data */ + unsigned char srom[128]; + u8 init; +}; + +enum uli526x_offsets { + DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, + DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, + DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, + DCR15 = 0x78 +}; + +enum uli526x_CR6_bits { + CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, + CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, + CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 +}; + +/* Global variable declaration ----------------------------- */ +static int __devinitdata printed_version; +static const char version[] __devinitconst = + "ULi M5261/M5263 net driver, version " DRV_VERSION " (" DRV_RELDATE ")"; + +static int uli526x_debug; +static unsigned char uli526x_media_mode = ULI526X_AUTO; +static u32 uli526x_cr6_user_set; + +/* For module input parameter */ +static int debug; +static u32 cr6set; +static int mode = 8; + +/* function declaration ------------------------------------- */ +static int uli526x_open(struct net_device *); +static netdev_tx_t uli526x_start_xmit(struct sk_buff *, + struct net_device *); +static int uli526x_stop(struct net_device *); +static void uli526x_set_filter_mode(struct net_device *); +static const struct ethtool_ops netdev_ethtool_ops; +static u16 read_srom_word(long, int); +static irqreturn_t uli526x_interrupt(int, void *); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void uli526x_poll(struct net_device *dev); +#endif +static void uli526x_descriptor_init(struct uli526x_board_info *, unsigned long); +static void allocate_rx_buffer(struct uli526x_board_info *); +static void update_cr6(u32, unsigned long); +static void send_filter_frame(struct net_device *, int); +static u16 phy_read(unsigned long, u8, u8, u32); +static u16 phy_readby_cr10(unsigned long, u8, u8); +static void phy_write(unsigned long, u8, u8, u16, u32); +static void phy_writeby_cr10(unsigned long, u8, u8, u16); +static void phy_write_1bit(unsigned long, u32, u32); +static u16 phy_read_1bit(unsigned long, u32); +static u8 uli526x_sense_speed(struct uli526x_board_info *); +static void uli526x_process_mode(struct uli526x_board_info *); +static void uli526x_timer(unsigned long); +static void uli526x_rx_packet(struct net_device *, struct uli526x_board_info *); +static void uli526x_free_tx_pkt(struct net_device *, struct uli526x_board_info *); +static void uli526x_reuse_skb(struct uli526x_board_info *, struct sk_buff *); +static void uli526x_dynamic_reset(struct net_device *); +static void uli526x_free_rxbuffer(struct uli526x_board_info *); +static void uli526x_init(struct net_device *); +static void uli526x_set_phyxcer(struct uli526x_board_info *); + +/* ULI526X network board routine ---------------------------- */ + +static const struct net_device_ops netdev_ops = { + .ndo_open = uli526x_open, + .ndo_stop = uli526x_stop, + .ndo_start_xmit = uli526x_start_xmit, + .ndo_set_multicast_list = uli526x_set_filter_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = uli526x_poll, +#endif +}; + +/* + * Search ULI526X board, allocate space and register it + */ + +static int __devinit uli526x_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct uli526x_board_info *db; /* board information structure */ + struct net_device *dev; + int i, err; + + ULI526X_DBUG(0, "uli526x_init_one()", 0); + + if (!printed_version++) + pr_info("%s\n", version); + + /* Init network device */ + dev = alloc_etherdev(sizeof(*db)); + if (dev == NULL) + return -ENOMEM; + SET_NETDEV_DEV(dev, &pdev->dev); + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + pr_warn("32-bit PCI DMA not available\n"); + err = -ENODEV; + goto err_out_free; + } + + /* Enable Master/IO access, Disable memory access */ + err = pci_enable_device(pdev); + if (err) + goto err_out_free; + + if (!pci_resource_start(pdev, 0)) { + pr_err("I/O base is zero\n"); + err = -ENODEV; + goto err_out_disable; + } + + if (pci_resource_len(pdev, 0) < (ULI526X_IO_SIZE) ) { + pr_err("Allocated I/O size too small\n"); + err = -ENODEV; + goto err_out_disable; + } + + if (pci_request_regions(pdev, DRV_NAME)) { + pr_err("Failed to request PCI regions\n"); + err = -ENODEV; + goto err_out_disable; + } + + /* Init system & device */ + db = netdev_priv(dev); + + /* Allocate Tx/Rx descriptor memory */ + db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); + if(db->desc_pool_ptr == NULL) + { + err = -ENOMEM; + goto err_out_nomem; + } + db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); + if(db->buf_pool_ptr == NULL) + { + err = -ENOMEM; + goto err_out_nomem; + } + + db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; + db->first_tx_desc_dma = db->desc_pool_dma_ptr; + db->buf_pool_start = db->buf_pool_ptr; + db->buf_pool_dma_start = db->buf_pool_dma_ptr; + + db->chip_id = ent->driver_data; + db->ioaddr = pci_resource_start(pdev, 0); + + db->pdev = pdev; + db->init = 1; + + dev->base_addr = db->ioaddr; + dev->irq = pdev->irq; + pci_set_drvdata(pdev, dev); + + /* Register some necessary functions */ + dev->netdev_ops = &netdev_ops; + dev->ethtool_ops = &netdev_ethtool_ops; + + spin_lock_init(&db->lock); + + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((__le16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i)); + + /* Set Node address */ + if(((u16 *) db->srom)[0] == 0xffff || ((u16 *) db->srom)[0] == 0) /* SROM absent, so read MAC address from ID Table */ + { + outl(0x10000, db->ioaddr + DCR0); //Diagnosis mode + outl(0x1c0, db->ioaddr + DCR13); //Reset dianostic pointer port + outl(0, db->ioaddr + DCR14); //Clear reset port + outl(0x10, db->ioaddr + DCR14); //Reset ID Table pointer + outl(0, db->ioaddr + DCR14); //Clear reset port + outl(0, db->ioaddr + DCR13); //Clear CR13 + outl(0x1b0, db->ioaddr + DCR13); //Select ID Table access port + //Read MAC address from CR14 + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inl(db->ioaddr + DCR14); + //Read end + outl(0, db->ioaddr + DCR13); //Clear CR13 + outl(0, db->ioaddr + DCR0); //Clear CR0 + udelay(10); + } + else /*Exist SROM*/ + { + for (i = 0; i < 6; i++) + dev->dev_addr[i] = db->srom[20 + i]; + } + err = register_netdev (dev); + if (err) + goto err_out_res; + + netdev_info(dev, "ULi M%04lx at pci%s, %pM, irq %d\n", + ent->driver_data >> 16, pci_name(pdev), + dev->dev_addr, dev->irq); + + pci_set_master(pdev); + + return 0; + +err_out_res: + pci_release_regions(pdev); +err_out_nomem: + if(db->desc_pool_ptr) + pci_free_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, + db->desc_pool_ptr, db->desc_pool_dma_ptr); + + if(db->buf_pool_ptr != NULL) + pci_free_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, + db->buf_pool_ptr, db->buf_pool_dma_ptr); +err_out_disable: + pci_disable_device(pdev); +err_out_free: + pci_set_drvdata(pdev, NULL); + free_netdev(dev); + + return err; +} + + +static void __devexit uli526x_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct uli526x_board_info *db = netdev_priv(dev); + + ULI526X_DBUG(0, "uli526x_remove_one()", 0); + + pci_free_consistent(db->pdev, sizeof(struct tx_desc) * + DESC_ALL_CNT + 0x20, db->desc_pool_ptr, + db->desc_pool_dma_ptr); + pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, + db->buf_pool_ptr, db->buf_pool_dma_ptr); + unregister_netdev(dev); + pci_release_regions(pdev); + free_netdev(dev); /* free board information */ + pci_set_drvdata(pdev, NULL); + pci_disable_device(pdev); + ULI526X_DBUG(0, "uli526x_remove_one() exit", 0); +} + + +/* + * Open the interface. + * The interface is opened whenever "ifconfig" activates it. + */ + +static int uli526x_open(struct net_device *dev) +{ + int ret; + struct uli526x_board_info *db = netdev_priv(dev); + + ULI526X_DBUG(0, "uli526x_open", 0); + + /* system variable init */ + db->cr6_data = CR6_DEFAULT | uli526x_cr6_user_set; + db->tx_packet_cnt = 0; + db->rx_avail_cnt = 0; + db->link_failed = 1; + netif_carrier_off(dev); + db->wait_reset = 0; + + db->NIC_capability = 0xf; /* All capability*/ + db->PHY_reg4 = 0x1e0; + + /* CR6 operation mode decision */ + db->cr6_data |= ULI526X_TXTH_256; + db->cr0_data = CR0_DEFAULT; + + /* Initialize ULI526X board */ + uli526x_init(dev); + + ret = request_irq(dev->irq, uli526x_interrupt, IRQF_SHARED, dev->name, dev); + if (ret) + return ret; + + /* Active System Interface */ + netif_wake_queue(dev); + + /* set and active a timer process */ + init_timer(&db->timer); + db->timer.expires = ULI526X_TIMER_WUT + HZ * 2; + db->timer.data = (unsigned long)dev; + db->timer.function = uli526x_timer; + add_timer(&db->timer); + + return 0; +} + + +/* Initialize ULI526X board + * Reset ULI526X board + * Initialize TX/Rx descriptor chain structure + * Send the set-up frame + * Enable Tx/Rx machine + */ + +static void uli526x_init(struct net_device *dev) +{ + struct uli526x_board_info *db = netdev_priv(dev); + unsigned long ioaddr = db->ioaddr; + u8 phy_tmp; + u8 timeout; + u16 phy_value; + u16 phy_reg_reset; + + + ULI526X_DBUG(0, "uli526x_init()", 0); + + /* Reset M526x MAC controller */ + outl(ULI526X_RESET, ioaddr + DCR0); /* RESET MAC */ + udelay(100); + outl(db->cr0_data, ioaddr + DCR0); + udelay(5); + + /* Phy addr : In some boards,M5261/M5263 phy address != 1 */ + db->phy_addr = 1; + for(phy_tmp=0;phy_tmp<32;phy_tmp++) + { + phy_value=phy_read(db->ioaddr,phy_tmp,3,db->chip_id);//peer add + if(phy_value != 0xffff&&phy_value!=0) + { + db->phy_addr = phy_tmp; + break; + } + } + if(phy_tmp == 32) + pr_warn("Can not find the phy address!!!\n"); + /* Parser SROM and media mode */ + db->media_mode = uli526x_media_mode; + + /* phyxcer capability setting */ + phy_reg_reset = phy_read(db->ioaddr, db->phy_addr, 0, db->chip_id); + phy_reg_reset = (phy_reg_reset | 0x8000); + phy_write(db->ioaddr, db->phy_addr, 0, phy_reg_reset, db->chip_id); + + /* See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management + * functions") or phy data sheet for details on phy reset + */ + udelay(500); + timeout = 10; + while (timeout-- && + phy_read(db->ioaddr, db->phy_addr, 0, db->chip_id) & 0x8000) + udelay(100); + + /* Process Phyxcer Media Mode */ + uli526x_set_phyxcer(db); + + /* Media Mode Process */ + if ( !(db->media_mode & ULI526X_AUTO) ) + db->op_mode = db->media_mode; /* Force Mode */ + + /* Initialize Transmit/Receive decriptor and CR3/4 */ + uli526x_descriptor_init(db, ioaddr); + + /* Init CR6 to program M526X operation */ + update_cr6(db->cr6_data, ioaddr); + + /* Send setup frame */ + send_filter_frame(dev, netdev_mc_count(dev)); /* M5261/M5263 */ + + /* Init CR7, interrupt active bit */ + db->cr7_data = CR7_DEFAULT; + outl(db->cr7_data, ioaddr + DCR7); + + /* Init CR15, Tx jabber and Rx watchdog timer */ + outl(db->cr15_data, ioaddr + DCR15); + + /* Enable ULI526X Tx/Rx function */ + db->cr6_data |= CR6_RXSC | CR6_TXSC; + update_cr6(db->cr6_data, ioaddr); +} + + +/* + * Hardware start transmission. + * Send a packet to media from the upper layer. + */ + +static netdev_tx_t uli526x_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct uli526x_board_info *db = netdev_priv(dev); + struct tx_desc *txptr; + unsigned long flags; + + ULI526X_DBUG(0, "uli526x_start_xmit", 0); + + /* Resource flag check */ + netif_stop_queue(dev); + + /* Too large packet check */ + if (skb->len > MAX_PACKET_SIZE) { + netdev_err(dev, "big packet = %d\n", (u16)skb->len); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + spin_lock_irqsave(&db->lock, flags); + + /* No Tx resource check, it never happen nromally */ + if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) { + spin_unlock_irqrestore(&db->lock, flags); + netdev_err(dev, "No Tx resource %ld\n", db->tx_packet_cnt); + return NETDEV_TX_BUSY; + } + + /* Disable NIC interrupt */ + outl(0, dev->base_addr + DCR7); + + /* transmit this packet */ + txptr = db->tx_insert_ptr; + skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len); + txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); + + /* Point to next transmit free descriptor */ + db->tx_insert_ptr = txptr->next_tx_desc; + + /* Transmit Packet Process */ + if ( (db->tx_packet_cnt < TX_DESC_CNT) ) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + dev->trans_start = jiffies; /* saved time stamp */ + } + + /* Tx resource check */ + if ( db->tx_packet_cnt < TX_FREE_DESC_CNT ) + netif_wake_queue(dev); + + /* Restore CR7 to enable interrupt */ + spin_unlock_irqrestore(&db->lock, flags); + outl(db->cr7_data, dev->base_addr + DCR7); + + /* free this SKB */ + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + + +/* + * Stop the interface. + * The interface is stopped when it is brought. + */ + +static int uli526x_stop(struct net_device *dev) +{ + struct uli526x_board_info *db = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + + ULI526X_DBUG(0, "uli526x_stop", 0); + + /* disable system */ + netif_stop_queue(dev); + + /* deleted timer */ + del_timer_sync(&db->timer); + + /* Reset & stop ULI526X board */ + outl(ULI526X_RESET, ioaddr + DCR0); + udelay(5); + phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); + + /* free interrupt */ + free_irq(dev->irq, dev); + + /* free allocated rx buffer */ + uli526x_free_rxbuffer(db); + + return 0; +} + + +/* + * M5261/M5263 insterrupt handler + * receive the packet to upper layer, free the transmitted packet + */ + +static irqreturn_t uli526x_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct uli526x_board_info *db = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + unsigned long flags; + + spin_lock_irqsave(&db->lock, flags); + outl(0, ioaddr + DCR7); + + /* Got ULI526X status */ + db->cr5_data = inl(ioaddr + DCR5); + outl(db->cr5_data, ioaddr + DCR5); + if ( !(db->cr5_data & 0x180c1) ) { + /* Restore CR7 to enable interrupt mask */ + outl(db->cr7_data, ioaddr + DCR7); + spin_unlock_irqrestore(&db->lock, flags); + return IRQ_HANDLED; + } + + /* Check system status */ + if (db->cr5_data & 0x2000) { + /* system bus error happen */ + ULI526X_DBUG(1, "System bus error happen. CR5=", db->cr5_data); + db->reset_fatal++; + db->wait_reset = 1; /* Need to RESET */ + spin_unlock_irqrestore(&db->lock, flags); + return IRQ_HANDLED; + } + + /* Received the coming packet */ + if ( (db->cr5_data & 0x40) && db->rx_avail_cnt ) + uli526x_rx_packet(dev, db); + + /* reallocate rx descriptor buffer */ + if (db->rx_avail_cntcr5_data & 0x01) + uli526x_free_tx_pkt(dev, db); + + /* Restore CR7 to enable interrupt mask */ + outl(db->cr7_data, ioaddr + DCR7); + + spin_unlock_irqrestore(&db->lock, flags); + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void uli526x_poll(struct net_device *dev) +{ + /* ISR grabs the irqsave lock, so this should be safe */ + uli526x_interrupt(dev->irq, dev); +} +#endif + +/* + * Free TX resource after TX complete + */ + +static void uli526x_free_tx_pkt(struct net_device *dev, + struct uli526x_board_info * db) +{ + struct tx_desc *txptr; + u32 tdes0; + + txptr = db->tx_remove_ptr; + while(db->tx_packet_cnt) { + tdes0 = le32_to_cpu(txptr->tdes0); + if (tdes0 & 0x80000000) + break; + + /* A packet sent completed */ + db->tx_packet_cnt--; + dev->stats.tx_packets++; + + /* Transmit statistic counter */ + if ( tdes0 != 0x7fffffff ) { + dev->stats.collisions += (tdes0 >> 3) & 0xf; + dev->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; + if (tdes0 & TDES0_ERR_MASK) { + dev->stats.tx_errors++; + if (tdes0 & 0x0002) { /* UnderRun */ + db->tx_fifo_underrun++; + if ( !(db->cr6_data & CR6_SFT) ) { + db->cr6_data = db->cr6_data | CR6_SFT; + update_cr6(db->cr6_data, db->ioaddr); + } + } + if (tdes0 & 0x0100) + db->tx_excessive_collision++; + if (tdes0 & 0x0200) + db->tx_late_collision++; + if (tdes0 & 0x0400) + db->tx_no_carrier++; + if (tdes0 & 0x0800) + db->tx_loss_carrier++; + if (tdes0 & 0x4000) + db->tx_jabber_timeout++; + } + } + + txptr = txptr->next_tx_desc; + }/* End of while */ + + /* Update TX remove pointer to next */ + db->tx_remove_ptr = txptr; + + /* Resource available check */ + if ( db->tx_packet_cnt < TX_WAKE_DESC_CNT ) + netif_wake_queue(dev); /* Active upper layer, send again */ +} + + +/* + * Receive the come packet and pass to upper layer + */ + +static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info * db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + int rxlen; + u32 rdes0; + + rxptr = db->rx_ready_ptr; + + while(db->rx_avail_cnt) { + rdes0 = le32_to_cpu(rxptr->rdes0); + if (rdes0 & 0x80000000) /* packet owner check */ + { + break; + } + + db->rx_avail_cnt--; + db->interval_rx_cnt++; + + pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); + if ( (rdes0 & 0x300) != 0x300) { + /* A packet without First/Last flag */ + /* reuse this SKB */ + ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + uli526x_reuse_skb(db, rxptr->rx_skb_ptr); + } else { + /* A packet with First/Last flag */ + rxlen = ( (rdes0 >> 16) & 0x3fff) - 4; + + /* error summary bit check */ + if (rdes0 & 0x8000) { + /* This is a error packet */ + dev->stats.rx_errors++; + if (rdes0 & 1) + dev->stats.rx_fifo_errors++; + if (rdes0 & 2) + dev->stats.rx_crc_errors++; + if (rdes0 & 0x80) + dev->stats.rx_length_errors++; + } + + if ( !(rdes0 & 0x8000) || + ((db->cr6_data & CR6_PM) && (rxlen>6)) ) { + struct sk_buff *new_skb = NULL; + + skb = rxptr->rx_skb_ptr; + + /* Good packet, send to upper layer */ + /* Shorst packet used new SKB */ + if ((rxlen < RX_COPY_SIZE) && + (((new_skb = dev_alloc_skb(rxlen + 2)) != NULL))) { + skb = new_skb; + /* size less than COPY_SIZE, allocate a rxlen SKB */ + skb_reserve(skb, 2); /* 16byte align */ + memcpy(skb_put(skb, rxlen), + skb_tail_pointer(rxptr->rx_skb_ptr), + rxlen); + uli526x_reuse_skb(db, rxptr->rx_skb_ptr); + } else + skb_put(skb, rxlen); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += rxlen; + + } else { + /* Reuse SKB buffer when the packet is error */ + ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + uli526x_reuse_skb(db, rxptr->rx_skb_ptr); + } + } + + rxptr = rxptr->next_rx_desc; + } + + db->rx_ready_ptr = rxptr; +} + + +/* + * Set ULI526X multicast address + */ + +static void uli526x_set_filter_mode(struct net_device * dev) +{ + struct uli526x_board_info *db = netdev_priv(dev); + unsigned long flags; + + ULI526X_DBUG(0, "uli526x_set_filter_mode()", 0); + spin_lock_irqsave(&db->lock, flags); + + if (dev->flags & IFF_PROMISC) { + ULI526X_DBUG(0, "Enable PROM Mode", 0); + db->cr6_data |= CR6_PM | CR6_PBF; + update_cr6(db->cr6_data, db->ioaddr); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + if (dev->flags & IFF_ALLMULTI || + netdev_mc_count(dev) > ULI5261_MAX_MULTICAST) { + ULI526X_DBUG(0, "Pass all multicast address", + netdev_mc_count(dev)); + db->cr6_data &= ~(CR6_PM | CR6_PBF); + db->cr6_data |= CR6_PAM; + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + ULI526X_DBUG(0, "Set multicast address", netdev_mc_count(dev)); + send_filter_frame(dev, netdev_mc_count(dev)); /* M5261/M5263 */ + spin_unlock_irqrestore(&db->lock, flags); +} + +static void +ULi_ethtool_gset(struct uli526x_board_info *db, struct ethtool_cmd *ecmd) +{ + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_MII); + + ecmd->advertising = (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_Autoneg | + ADVERTISED_MII); + + + ecmd->port = PORT_MII; + ecmd->phy_address = db->phy_addr; + + ecmd->transceiver = XCVR_EXTERNAL; + + ethtool_cmd_speed_set(ecmd, SPEED_10); + ecmd->duplex = DUPLEX_HALF; + + if(db->op_mode==ULI526X_100MHF || db->op_mode==ULI526X_100MFD) + { + ethtool_cmd_speed_set(ecmd, SPEED_100); + } + if(db->op_mode==ULI526X_10MFD || db->op_mode==ULI526X_100MFD) + { + ecmd->duplex = DUPLEX_FULL; + } + if(db->link_failed) + { + ethtool_cmd_speed_set(ecmd, -1); + ecmd->duplex = -1; + } + + if (db->media_mode & ULI526X_AUTO) + { + ecmd->autoneg = AUTONEG_ENABLE; + } +} + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct uli526x_board_info *np = netdev_priv(dev); + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + if (np->pdev) + strcpy(info->bus_info, pci_name(np->pdev)); + else + sprintf(info->bus_info, "EISA 0x%lx %d", + dev->base_addr, dev->irq); +} + +static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { + struct uli526x_board_info *np = netdev_priv(dev); + + ULi_ethtool_gset(np, cmd); + + return 0; +} + +static u32 netdev_get_link(struct net_device *dev) { + struct uli526x_board_info *np = netdev_priv(dev); + + if(np->link_failed) + return 0; + else + return 1; +} + +static void uli526x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + wol->supported = WAKE_PHY | WAKE_MAGIC; + wol->wolopts = 0; +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_settings = netdev_get_settings, + .get_link = netdev_get_link, + .get_wol = uli526x_get_wol, +}; + +/* + * A periodic timer routine + * Dynamic media sense, allocate Rx buffer... + */ + +static void uli526x_timer(unsigned long data) +{ + u32 tmp_cr8; + unsigned char tmp_cr12=0; + struct net_device *dev = (struct net_device *) data; + struct uli526x_board_info *db = netdev_priv(dev); + unsigned long flags; + + //ULI526X_DBUG(0, "uli526x_timer()", 0); + spin_lock_irqsave(&db->lock, flags); + + + /* Dynamic reset ULI526X : system error or transmit time-out */ + tmp_cr8 = inl(db->ioaddr + DCR8); + if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) { + db->reset_cr8++; + db->wait_reset = 1; + } + db->interval_rx_cnt = 0; + + /* TX polling kick monitor */ + if ( db->tx_packet_cnt && + time_after(jiffies, dev_trans_start(dev) + ULI526X_TX_KICK) ) { + outl(0x1, dev->base_addr + DCR1); // Tx polling again + + // TX Timeout + if ( time_after(jiffies, dev_trans_start(dev) + ULI526X_TX_TIMEOUT) ) { + db->reset_TXtimeout++; + db->wait_reset = 1; + netdev_err(dev, " Tx timeout - resetting\n"); + } + } + + if (db->wait_reset) { + ULI526X_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt); + db->reset_count++; + uli526x_dynamic_reset(dev); + db->timer.expires = ULI526X_TIMER_WUT; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + /* Link status check, Dynamic media type change */ + if((phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id) & 0x01e0)!=0) + tmp_cr12 = 3; + + if ( !(tmp_cr12 & 0x3) && !db->link_failed ) { + /* Link Failed */ + ULI526X_DBUG(0, "Link Failed", tmp_cr12); + netif_carrier_off(dev); + netdev_info(dev, "NIC Link is Down\n"); + db->link_failed = 1; + + /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */ + /* AUTO don't need */ + if ( !(db->media_mode & 0x8) ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); + + /* AUTO mode, if INT phyxcer link failed, select EXT device */ + if (db->media_mode & ULI526X_AUTO) { + db->cr6_data&=~0x00000200; /* bit9=0, HD mode */ + update_cr6(db->cr6_data, db->ioaddr); + } + } else + if ((tmp_cr12 & 0x3) && db->link_failed) { + ULI526X_DBUG(0, "Link link OK", tmp_cr12); + db->link_failed = 0; + + /* Auto Sense Speed */ + if ( (db->media_mode & ULI526X_AUTO) && + uli526x_sense_speed(db) ) + db->link_failed = 1; + uli526x_process_mode(db); + + if(db->link_failed==0) + { + netdev_info(dev, "NIC Link is Up %d Mbps %s duplex\n", + (db->op_mode == ULI526X_100MHF || + db->op_mode == ULI526X_100MFD) + ? 100 : 10, + (db->op_mode == ULI526X_10MFD || + db->op_mode == ULI526X_100MFD) + ? "Full" : "Half"); + netif_carrier_on(dev); + } + /* SHOW_MEDIA_TYPE(db->op_mode); */ + } + else if(!(tmp_cr12 & 0x3) && db->link_failed) + { + if(db->init==1) + { + netdev_info(dev, "NIC Link is Down\n"); + netif_carrier_off(dev); + } + } + db->init=0; + + /* Timer active again */ + db->timer.expires = ULI526X_TIMER_WUT; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); +} + + +/* + * Stop ULI526X board + * Free Tx/Rx allocated memory + * Init system variable + */ + +static void uli526x_reset_prepare(struct net_device *dev) +{ + struct uli526x_board_info *db = netdev_priv(dev); + + /* Sopt MAC controller */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ + update_cr6(db->cr6_data, dev->base_addr); + outl(0, dev->base_addr + DCR7); /* Disable Interrupt */ + outl(inl(dev->base_addr + DCR5), dev->base_addr + DCR5); + + /* Disable upper layer interface */ + netif_stop_queue(dev); + + /* Free Rx Allocate buffer */ + uli526x_free_rxbuffer(db); + + /* system variable init */ + db->tx_packet_cnt = 0; + db->rx_avail_cnt = 0; + db->link_failed = 1; + db->init=1; + db->wait_reset = 0; +} + + +/* + * Dynamic reset the ULI526X board + * Stop ULI526X board + * Free Tx/Rx allocated memory + * Reset ULI526X board + * Re-initialize ULI526X board + */ + +static void uli526x_dynamic_reset(struct net_device *dev) +{ + ULI526X_DBUG(0, "uli526x_dynamic_reset()", 0); + + uli526x_reset_prepare(dev); + + /* Re-initialize ULI526X board */ + uli526x_init(dev); + + /* Restart upper layer interface */ + netif_wake_queue(dev); +} + + +#ifdef CONFIG_PM + +/* + * Suspend the interface. + */ + +static int uli526x_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + pci_power_t power_state; + int err; + + ULI526X_DBUG(0, "uli526x_suspend", 0); + + if (!netdev_priv(dev)) + return 0; + + pci_save_state(pdev); + + if (!netif_running(dev)) + return 0; + + netif_device_detach(dev); + uli526x_reset_prepare(dev); + + power_state = pci_choose_state(pdev, state); + pci_enable_wake(pdev, power_state, 0); + err = pci_set_power_state(pdev, power_state); + if (err) { + netif_device_attach(dev); + /* Re-initialize ULI526X board */ + uli526x_init(dev); + /* Restart upper layer interface */ + netif_wake_queue(dev); + } + + return err; +} + +/* + * Resume the interface. + */ + +static int uli526x_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + int err; + + ULI526X_DBUG(0, "uli526x_resume", 0); + + if (!netdev_priv(dev)) + return 0; + + pci_restore_state(pdev); + + if (!netif_running(dev)) + return 0; + + err = pci_set_power_state(pdev, PCI_D0); + if (err) { + netdev_warn(dev, "Could not put device into D0\n"); + return err; + } + + netif_device_attach(dev); + /* Re-initialize ULI526X board */ + uli526x_init(dev); + /* Restart upper layer interface */ + netif_wake_queue(dev); + + return 0; +} + +#else /* !CONFIG_PM */ + +#define uli526x_suspend NULL +#define uli526x_resume NULL + +#endif /* !CONFIG_PM */ + + +/* + * free all allocated rx buffer + */ + +static void uli526x_free_rxbuffer(struct uli526x_board_info * db) +{ + ULI526X_DBUG(0, "uli526x_free_rxbuffer()", 0); + + /* free allocated rx buffer */ + while (db->rx_avail_cnt) { + dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr); + db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc; + db->rx_avail_cnt--; + } +} + + +/* + * Reuse the SK buffer + */ + +static void uli526x_reuse_skb(struct uli526x_board_info *db, struct sk_buff * skb) +{ + struct rx_desc *rxptr = db->rx_insert_ptr; + + if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { + rxptr->rx_skb_ptr = skb; + rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev, + skb_tail_pointer(skb), + RX_ALLOC_SIZE, + PCI_DMA_FROMDEVICE)); + wmb(); + rxptr->rdes0 = cpu_to_le32(0x80000000); + db->rx_avail_cnt++; + db->rx_insert_ptr = rxptr->next_rx_desc; + } else + ULI526X_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt); +} + + +/* + * Initialize transmit/Receive descriptor + * Using Chain structure, and allocate Tx/Rx buffer + */ + +static void uli526x_descriptor_init(struct uli526x_board_info *db, unsigned long ioaddr) +{ + struct tx_desc *tmp_tx; + struct rx_desc *tmp_rx; + unsigned char *tmp_buf; + dma_addr_t tmp_tx_dma, tmp_rx_dma; + dma_addr_t tmp_buf_dma; + int i; + + ULI526X_DBUG(0, "uli526x_descriptor_init()", 0); + + /* tx descriptor start pointer */ + db->tx_insert_ptr = db->first_tx_desc; + db->tx_remove_ptr = db->first_tx_desc; + outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ + + /* rx descriptor start pointer */ + db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT; + db->first_rx_desc_dma = db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT; + db->rx_insert_ptr = db->first_rx_desc; + db->rx_ready_ptr = db->first_rx_desc; + outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ + + /* Init Transmit chain */ + tmp_buf = db->buf_pool_start; + tmp_buf_dma = db->buf_pool_dma_start; + tmp_tx_dma = db->first_tx_desc_dma; + for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) { + tmp_tx->tx_buf_ptr = tmp_buf; + tmp_tx->tdes0 = cpu_to_le32(0); + tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */ + tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma); + tmp_tx_dma += sizeof(struct tx_desc); + tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma); + tmp_tx->next_tx_desc = tmp_tx + 1; + tmp_buf = tmp_buf + TX_BUF_ALLOC; + tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC; + } + (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma); + tmp_tx->next_tx_desc = db->first_tx_desc; + + /* Init Receive descriptor chain */ + tmp_rx_dma=db->first_rx_desc_dma; + for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) { + tmp_rx->rdes0 = cpu_to_le32(0); + tmp_rx->rdes1 = cpu_to_le32(0x01000600); + tmp_rx_dma += sizeof(struct rx_desc); + tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma); + tmp_rx->next_rx_desc = tmp_rx + 1; + } + (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma); + tmp_rx->next_rx_desc = db->first_rx_desc; + + /* pre-allocate Rx buffer */ + allocate_rx_buffer(db); +} + + +/* + * Update CR6 value + * Firstly stop ULI526X, then written value and start + */ + +static void update_cr6(u32 cr6_data, unsigned long ioaddr) +{ + + outl(cr6_data, ioaddr + DCR6); + udelay(5); +} + + +/* + * Send a setup frame for M5261/M5263 + * This setup frame initialize ULI526X address filter mode + */ + +#ifdef __BIG_ENDIAN +#define FLT_SHIFT 16 +#else +#define FLT_SHIFT 0 +#endif + +static void send_filter_frame(struct net_device *dev, int mc_cnt) +{ + struct uli526x_board_info *db = netdev_priv(dev); + struct netdev_hw_addr *ha; + struct tx_desc *txptr; + u16 * addrptr; + u32 * suptr; + int i; + + ULI526X_DBUG(0, "send_filter_frame()", 0); + + txptr = db->tx_insert_ptr; + suptr = (u32 *) txptr->tx_buf_ptr; + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + *suptr++ = addrptr[0] << FLT_SHIFT; + *suptr++ = addrptr[1] << FLT_SHIFT; + *suptr++ = addrptr[2] << FLT_SHIFT; + + /* broadcast address */ + *suptr++ = 0xffff << FLT_SHIFT; + *suptr++ = 0xffff << FLT_SHIFT; + *suptr++ = 0xffff << FLT_SHIFT; + + /* fit the multicast address */ + netdev_for_each_mc_addr(ha, dev) { + addrptr = (u16 *) ha->addr; + *suptr++ = addrptr[0] << FLT_SHIFT; + *suptr++ = addrptr[1] << FLT_SHIFT; + *suptr++ = addrptr[2] << FLT_SHIFT; + } + + for (i = netdev_mc_count(dev); i < 14; i++) { + *suptr++ = 0xffff << FLT_SHIFT; + *suptr++ = 0xffff << FLT_SHIFT; + *suptr++ = 0xffff << FLT_SHIFT; + } + + /* prepare the setup frame */ + db->tx_insert_ptr = txptr->next_tx_desc; + txptr->tdes1 = cpu_to_le32(0x890000c0); + + /* Resource Check and Send the setup packet */ + if (db->tx_packet_cnt < TX_DESC_CNT) { + /* Resource Empty */ + db->tx_packet_cnt++; + txptr->tdes0 = cpu_to_le32(0x80000000); + update_cr6(db->cr6_data | 0x2000, dev->base_addr); + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + update_cr6(db->cr6_data, dev->base_addr); + dev->trans_start = jiffies; + } else + netdev_err(dev, "No Tx resource - Send_filter_frame!\n"); +} + + +/* + * Allocate rx buffer, + * As possible as allocate maxiumn Rx buffer + */ + +static void allocate_rx_buffer(struct uli526x_board_info *db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + + rxptr = db->rx_insert_ptr; + + while(db->rx_avail_cnt < RX_DESC_CNT) { + if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) + break; + rxptr->rx_skb_ptr = skb; /* FIXME (?) */ + rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev, + skb_tail_pointer(skb), + RX_ALLOC_SIZE, + PCI_DMA_FROMDEVICE)); + wmb(); + rxptr->rdes0 = cpu_to_le32(0x80000000); + rxptr = rxptr->next_rx_desc; + db->rx_avail_cnt++; + } + + db->rx_insert_ptr = rxptr; +} + + +/* + * Read one word data from the serial ROM + */ + +static u16 read_srom_word(long ioaddr, int offset) +{ + int i; + u16 srom_data = 0; + long cr9_ioaddr = ioaddr + DCR9; + + outl(CR9_SROM_READ, cr9_ioaddr); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + /* Send the Read Command 110b */ + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); + + /* Send the offset */ + for (i = 5; i >= 0; i--) { + srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; + SROM_CLK_WRITE(srom_data, cr9_ioaddr); + } + + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + for (i = 16; i > 0; i--) { + outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); + udelay(5); + srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + udelay(5); + } + + outl(CR9_SROM_READ, cr9_ioaddr); + return srom_data; +} + + +/* + * Auto sense the media mode + */ + +static u8 uli526x_sense_speed(struct uli526x_board_info * db) +{ + u8 ErrFlag = 0; + u16 phy_mode; + + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + + if ( (phy_mode & 0x24) == 0x24 ) { + + phy_mode = ((phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id) & 0x01e0)<<7); + if(phy_mode&0x8000) + phy_mode = 0x8000; + else if(phy_mode&0x4000) + phy_mode = 0x4000; + else if(phy_mode&0x2000) + phy_mode = 0x2000; + else + phy_mode = 0x1000; + + switch (phy_mode) { + case 0x1000: db->op_mode = ULI526X_10MHF; break; + case 0x2000: db->op_mode = ULI526X_10MFD; break; + case 0x4000: db->op_mode = ULI526X_100MHF; break; + case 0x8000: db->op_mode = ULI526X_100MFD; break; + default: db->op_mode = ULI526X_10MHF; ErrFlag = 1; break; + } + } else { + db->op_mode = ULI526X_10MHF; + ULI526X_DBUG(0, "Link Failed :", phy_mode); + ErrFlag = 1; + } + + return ErrFlag; +} + + +/* + * Set 10/100 phyxcer capability + * AUTO mode : phyxcer register4 is NIC capability + * Force mode: phyxcer register4 is the force media + */ + +static void uli526x_set_phyxcer(struct uli526x_board_info *db) +{ + u16 phy_reg; + + /* Phyxcer capability setting */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0; + + if (db->media_mode & ULI526X_AUTO) { + /* AUTO Mode */ + phy_reg |= db->PHY_reg4; + } else { + /* Force Mode */ + switch(db->media_mode) { + case ULI526X_10MHF: phy_reg |= 0x20; break; + case ULI526X_10MFD: phy_reg |= 0x40; break; + case ULI526X_100MHF: phy_reg |= 0x80; break; + case ULI526X_100MFD: phy_reg |= 0x100; break; + } + + } + + /* Write new capability to Phyxcer Reg4 */ + if ( !(phy_reg & 0x01e0)) { + phy_reg|=db->PHY_reg4; + db->media_mode|=ULI526X_AUTO; + } + phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); + + /* Restart Auto-Negotiation */ + phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id); + udelay(50); +} + + +/* + * Process op-mode + AUTO mode : PHY controller in Auto-negotiation Mode + * Force mode: PHY controller in force mode with HUB + * N-way force capability with SWITCH + */ + +static void uli526x_process_mode(struct uli526x_board_info *db) +{ + u16 phy_reg; + + /* Full Duplex Mode Check */ + if (db->op_mode & 0x4) + db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */ + else + db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */ + + update_cr6(db->cr6_data, db->ioaddr); + + /* 10/100M phyxcer force mode need */ + if ( !(db->media_mode & 0x8)) { + /* Forece Mode */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 6, db->chip_id); + if ( !(phy_reg & 0x1) ) { + /* parter without N-Way capability */ + phy_reg = 0x0; + switch(db->op_mode) { + case ULI526X_10MHF: phy_reg = 0x0; break; + case ULI526X_10MFD: phy_reg = 0x100; break; + case ULI526X_100MHF: phy_reg = 0x2000; break; + case ULI526X_100MFD: phy_reg = 0x2100; break; + } + phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); + } + } +} + + +/* + * Write a word to Phy register + */ + +static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id) +{ + u16 i; + unsigned long ioaddr; + + if(chip_id == PCI_ULI5263_ID) + { + phy_writeby_cr10(iobase, phy_addr, offset, phy_data); + return; + } + /* M5261/M5263 Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send Phy address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Send register address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + + /* Write a word data to PHY controller */ + for ( i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0, chip_id); + +} + + +/* + * Read a word data from phy register + */ + +static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) +{ + int i; + u16 phy_data; + unsigned long ioaddr; + + if(chip_id == PCI_ULI5263_ID) + return phy_readby_cr10(iobase, phy_addr, offset); + /* M5261/M5263 Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + + /* Send Phy address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Send register address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Skip transition state */ + phy_read_1bit(ioaddr, chip_id); + + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr, chip_id); + } + + return phy_data; +} + +static u16 phy_readby_cr10(unsigned long iobase, u8 phy_addr, u8 offset) +{ + unsigned long ioaddr,cr10_value; + + ioaddr = iobase + DCR10; + cr10_value = phy_addr; + cr10_value = (cr10_value<<5) + offset; + cr10_value = (cr10_value<<16) + 0x08000000; + outl(cr10_value,ioaddr); + udelay(1); + while(1) + { + cr10_value = inl(ioaddr); + if(cr10_value&0x10000000) + break; + } + return cr10_value & 0x0ffff; +} + +static void phy_writeby_cr10(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data) +{ + unsigned long ioaddr,cr10_value; + + ioaddr = iobase + DCR10; + cr10_value = phy_addr; + cr10_value = (cr10_value<<5) + offset; + cr10_value = (cr10_value<<16) + 0x04000000 + phy_data; + outl(cr10_value,ioaddr); + udelay(1); +} +/* + * Write one bit data to Phy Controller + */ + +static void phy_write_1bit(unsigned long ioaddr, u32 phy_data, u32 chip_id) +{ + outl(phy_data , ioaddr); /* MII Clock Low */ + udelay(1); + outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ + udelay(1); + outl(phy_data , ioaddr); /* MII Clock Low */ + udelay(1); +} + + +/* + * Read one bit phy data from PHY controller + */ + +static u16 phy_read_1bit(unsigned long ioaddr, u32 chip_id) +{ + u16 phy_data; + + outl(0x50000 , ioaddr); + udelay(1); + phy_data = ( inl(ioaddr) >> 19 ) & 0x1; + outl(0x40000 , ioaddr); + udelay(1); + + return phy_data; +} + + +static DEFINE_PCI_DEVICE_TABLE(uli526x_pci_tbl) = { + { 0x10B9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5261_ID }, + { 0x10B9, 0x5263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5263_ID }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, uli526x_pci_tbl); + + +static struct pci_driver uli526x_driver = { + .name = "uli526x", + .id_table = uli526x_pci_tbl, + .probe = uli526x_init_one, + .remove = __devexit_p(uli526x_remove_one), + .suspend = uli526x_suspend, + .resume = uli526x_resume, +}; + +MODULE_AUTHOR("Peer Chen, peer.chen@uli.com.tw"); +MODULE_DESCRIPTION("ULi M5261/M5263 fast ethernet driver"); +MODULE_LICENSE("GPL"); + +module_param(debug, int, 0644); +module_param(mode, int, 0); +module_param(cr6set, int, 0); +MODULE_PARM_DESC(debug, "ULi M5261/M5263 enable debugging (0-1)"); +MODULE_PARM_DESC(mode, "ULi M5261/M5263: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA"); + +/* Description: + * when user used insmod to add module, system invoked init_module() + * to register the services. + */ + +static int __init uli526x_init_module(void) +{ + + pr_info("%s\n", version); + printed_version = 1; + + ULI526X_DBUG(0, "init_module() ", debug); + + if (debug) + uli526x_debug = debug; /* set debug flag */ + if (cr6set) + uli526x_cr6_user_set = cr6set; + + switch (mode) { + case ULI526X_10MHF: + case ULI526X_100MHF: + case ULI526X_10MFD: + case ULI526X_100MFD: + uli526x_media_mode = mode; + break; + default: + uli526x_media_mode = ULI526X_AUTO; + break; + } + + return pci_register_driver(&uli526x_driver); +} + + +/* + * Description: + * when user used rmmod to delete module, system invoked clean_module() + * to un-register all registered services. + */ + +static void __exit uli526x_cleanup_module(void) +{ + ULI526X_DBUG(0, "uli526x_clean_module() ", debug); + pci_unregister_driver(&uli526x_driver); +} + +module_init(uli526x_init_module); +module_exit(uli526x_cleanup_module); diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c new file mode 100644 index 000000000000..862eadf07191 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -0,0 +1,1670 @@ +/* winbond-840.c: A Linux PCI network adapter device driver. */ +/* + Written 1998-2001 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support and updates available at + http://www.scyld.com/network/drivers.html + + Do not remove the copyright information. + Do not change the version information unless an improvement has been made. + Merely removing my name, as Compex has done in the past, does not count + as an improvement. + + Changelog: + * ported to 2.4 + ??? + * spin lock update, memory barriers, new style dma mappings + limit each tx buffer to < 1024 bytes + remove DescIntr from Rx descriptors (that's an Tx flag) + remove next pointer from Tx descriptors + synchronize tx_q_bytes + software reset in tx_timeout + Copyright (C) 2000 Manfred Spraul + * further cleanups + power management. + support for big endian descriptors + Copyright (C) 2001 Manfred Spraul + * ethtool support (jgarzik) + * Replace some MII-related magic numbers with constants (jgarzik) + + TODO: + * enable pci_power_off + * Wake-On-LAN +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "winbond-840" +#define DRV_VERSION "1.01-e" +#define DRV_RELDATE "Sep-11-2006" + + +/* Automatically extracted configuration info: +probe-func: winbond840_probe +config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 + +c-help-name: Winbond W89c840 PCI Ethernet support +c-help-symbol: CONFIG_WINBOND_840 +c-help: This driver is for the Winbond W89c840 chip. It also works with +c-help: the TX9882 chip on the Compex RL100-ATX board. +c-help: More specific information and updates are available from +c-help: http://www.scyld.com/network/drivers.html +*/ + +/* The user-configurable values. + These may be modified when a driver module is loaded.*/ + +static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ +static int max_interrupt_work = 20; +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The '840 uses a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak; + +/* Used to pass the media type, etc. + Both 'options[]' and 'full_duplex[]' should exist for driver + interoperability. + The media type is usually passed in 'options[]'. +*/ +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. + The compiler will convert '%'<2^N> into a bit mask. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */ +#define TX_QUEUE_LEN_RESTART 5 + +#define TX_BUFLIMIT (1024-128) + +/* The presumed FIFO size for working around the Tx-FIFO-overflow bug. + To avoid overflowing we don't queue again until we have room for a + full-size packet. + */ +#define TX_FIFO_SIZE (2048) +#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16) + + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include + +#include "tulip.h" + +#undef PKT_BUF_SZ /* tulip.h also defines this */ +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +/* These identify the driver base version and may not be removed. */ +static const char version[] __initconst = + "v" DRV_VERSION " (2.4 port) " + DRV_RELDATE " Donald Becker \n" + " http://www.scyld.com/network/drivers.html\n"; + +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_param(max_interrupt_work, int, 0); +module_param(debug, int, 0); +module_param(rx_copybreak, int, 0); +module_param(multicast_filter_limit, int, 0); +module_param_array(options, int, NULL, 0); +module_param_array(full_duplex, int, NULL, 0); +MODULE_PARM_DESC(max_interrupt_work, "winbond-840 maximum events handled per interrupt"); +MODULE_PARM_DESC(debug, "winbond-840 debug level (0-6)"); +MODULE_PARM_DESC(rx_copybreak, "winbond-840 copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, "winbond-840 maximum number of filtered multicast addresses"); +MODULE_PARM_DESC(options, "winbond-840: Bits 0-3: media type, bit 17: full duplex"); +MODULE_PARM_DESC(full_duplex, "winbond-840 full duplex setting(s) (1)"); + +/* + Theory of Operation + +I. Board Compatibility + +This driver is for the Winbond w89c840 chip. + +II. Board-specific settings + +None. + +III. Driver operation + +This chip is very similar to the Digital 21*4* "Tulip" family. The first +twelve registers and the descriptor format are nearly identical. Read a +Tulip manual for operational details. + +A significant difference is that the multicast filter and station address are +stored in registers rather than loaded through a pseudo-transmit packet. + +Unlike the Tulip, transmit buffers are limited to 1KB. To transmit a +full-sized packet we must use both data buffers in a descriptor. Thus the +driver uses ring mode where descriptors are implicitly sequential in memory, +rather than using the second descriptor address as a chain pointer to +subsequent descriptors. + +IV. Notes + +If you are going to almost clone a Tulip, why not go all the way and avoid +the need for a new driver? + +IVb. References + +http://www.scyld.com/expert/100mbps.html +http://www.scyld.com/expert/NWay.html +http://www.winbond.com.tw/ + +IVc. Errata + +A horrible bug exists in the transmit FIFO. Apparently the chip doesn't +correctly detect a full FIFO, and queuing more than 2048 bytes may result in +silent data corruption. + +Test with 'ping -s 10000' on a fast computer. + +*/ + + + +/* + PCI probe table. +*/ +enum chip_capability_flags { + CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8, +}; + +static DEFINE_PCI_DEVICE_TABLE(w840_pci_tbl) = { + { 0x1050, 0x0840, PCI_ANY_ID, 0x8153, 0, 0, 0 }, + { 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { } +}; +MODULE_DEVICE_TABLE(pci, w840_pci_tbl); + +enum { + netdev_res_size = 128, /* size of PCI BAR resource */ +}; + +struct pci_id_info { + const char *name; + int drv_flags; /* Driver use, intended as capability flags. */ +}; + +static const struct pci_id_info pci_id_tbl[] __devinitdata = { + { /* Sometime a Level-One switch card. */ + "Winbond W89c840", CanHaveMII | HasBrokenTx | FDXOnNoMII}, + { "Winbond W89c840", CanHaveMII | HasBrokenTx}, + { "Compex RL100-ATX", CanHaveMII | HasBrokenTx}, + { } /* terminate list. */ +}; + +/* This driver was written to use PCI memory space, however some x86 systems + work only with I/O space accesses. See CONFIG_TULIP_MMIO in .config +*/ + +/* Offsets to the Command and Status Registers, "CSRs". + While similar to the Tulip, these registers are longword aligned. + Note: It's not useful to define symbolic names for every register bit in + the device. The name can only partially document the semantics and make + the driver longer and more difficult to read. +*/ +enum w840_offsets { + PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08, + RxRingPtr=0x0C, TxRingPtr=0x10, + IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C, + RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C, + CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */ + MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40, + CurTxDescAddr=0x4C, CurTxBufAddr=0x50, +}; + +/* Bits in the NetworkConfig register. */ +enum rx_mode_bits { + AcceptErr=0x80, + RxAcceptBroadcast=0x20, AcceptMulticast=0x10, + RxAcceptAllPhys=0x08, AcceptMyPhys=0x02, +}; + +enum mii_reg_bits { + MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000, + MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000, +}; + +/* The Tulip Rx and Tx buffer descriptors. */ +struct w840_rx_desc { + s32 status; + s32 length; + u32 buffer1; + u32 buffer2; +}; + +struct w840_tx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; +}; + +#define MII_CNT 1 /* winbond only supports one MII */ +struct netdev_private { + struct w840_rx_desc *rx_ring; + dma_addr_t rx_addr[RX_RING_SIZE]; + struct w840_tx_desc *tx_ring; + dma_addr_t tx_addr[TX_RING_SIZE]; + dma_addr_t ring_dma_addr; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for later free(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct net_device_stats stats; + struct timer_list timer; /* Media monitoring timer. */ + /* Frequently used values: keep some adjacent for cache effect. */ + spinlock_t lock; + int chip_id, drv_flags; + struct pci_dev *pci_dev; + int csr6; + struct w840_rx_desc *rx_head_desc; + unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ + unsigned int rx_buf_sz; /* Based on MTU+slack. */ + unsigned int cur_tx, dirty_tx; + unsigned int tx_q_bytes; + unsigned int tx_full; /* The Tx queue is full. */ + /* MII transceiver section. */ + int mii_cnt; /* MII device addresses. */ + unsigned char phys[MII_CNT]; /* MII device addresses, but only the first is used */ + u32 mii; + struct mii_if_info mii_if; + void __iomem *base_addr; +}; + +static int eeprom_read(void __iomem *ioaddr, int location); +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static int netdev_open(struct net_device *dev); +static int update_link(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void init_rxtx_rings(struct net_device *dev); +static void free_rxtx_rings(struct netdev_private *np); +static void init_registers(struct net_device *dev); +static void tx_timeout(struct net_device *dev); +static int alloc_ringdesc(struct net_device *dev); +static void free_ringdesc(struct netdev_private *np); +static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t intr_handler(int irq, void *dev_instance); +static void netdev_error(struct net_device *dev, int intr_status); +static int netdev_rx(struct net_device *dev); +static u32 __set_rx_mode(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static const struct ethtool_ops netdev_ethtool_ops; +static int netdev_close(struct net_device *dev); + +static const struct net_device_ops netdev_ops = { + .ndo_open = netdev_open, + .ndo_stop = netdev_close, + .ndo_start_xmit = start_tx, + .ndo_get_stats = get_stats, + .ndo_set_multicast_list = set_rx_mode, + .ndo_do_ioctl = netdev_ioctl, + .ndo_tx_timeout = tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __devinit w840_probe1 (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + struct netdev_private *np; + static int find_cnt; + int chip_idx = ent->driver_data; + int irq; + int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; + void __iomem *ioaddr; + + i = pci_enable_device(pdev); + if (i) return i; + + pci_set_master(pdev); + + irq = pdev->irq; + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + pr_warn("Device %s disabled due to DMA limitations\n", + pci_name(pdev)); + return -EIO; + } + dev = alloc_etherdev(sizeof(*np)); + if (!dev) + return -ENOMEM; + SET_NETDEV_DEV(dev, &pdev->dev); + + if (pci_request_regions(pdev, DRV_NAME)) + goto err_out_netdev; + + ioaddr = pci_iomap(pdev, TULIP_BAR, netdev_res_size); + if (!ioaddr) + goto err_out_free_res; + + for (i = 0; i < 3; i++) + ((__le16 *)dev->dev_addr)[i] = cpu_to_le16(eeprom_read(ioaddr, i)); + + /* Reset the chip to erase previous misconfiguration. + No hold time required! */ + iowrite32(0x00000001, ioaddr + PCIBusCfg); + + dev->base_addr = (unsigned long)ioaddr; + dev->irq = irq; + + np = netdev_priv(dev); + np->pci_dev = pdev; + np->chip_id = chip_idx; + np->drv_flags = pci_id_tbl[chip_idx].drv_flags; + spin_lock_init(&np->lock); + np->mii_if.dev = dev; + np->mii_if.mdio_read = mdio_read; + np->mii_if.mdio_write = mdio_write; + np->base_addr = ioaddr; + + pci_set_drvdata(pdev, dev); + + if (dev->mem_start) + option = dev->mem_start; + + /* The lower four bits are the media type. */ + if (option > 0) { + if (option & 0x200) + np->mii_if.full_duplex = 1; + if (option & 15) + dev_info(&dev->dev, + "ignoring user supplied media type %d", + option & 15); + } + if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0) + np->mii_if.full_duplex = 1; + + if (np->mii_if.full_duplex) + np->mii_if.force_media = 1; + + /* The chip-specific entries in the device structure. */ + dev->netdev_ops = &netdev_ops; + dev->ethtool_ops = &netdev_ethtool_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + i = register_netdev(dev); + if (i) + goto err_out_cleardev; + + dev_info(&dev->dev, "%s at %p, %pM, IRQ %d\n", + pci_id_tbl[chip_idx].name, ioaddr, dev->dev_addr, irq); + + if (np->drv_flags & CanHaveMII) { + int phy, phy_idx = 0; + for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) { + int mii_status = mdio_read(dev, phy, MII_BMSR); + if (mii_status != 0xffff && mii_status != 0x0000) { + np->phys[phy_idx++] = phy; + np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE); + np->mii = (mdio_read(dev, phy, MII_PHYSID1) << 16)+ + mdio_read(dev, phy, MII_PHYSID2); + dev_info(&dev->dev, + "MII PHY %08xh found at address %d, status 0x%04x advertising %04x\n", + np->mii, phy, mii_status, + np->mii_if.advertising); + } + } + np->mii_cnt = phy_idx; + np->mii_if.phy_id = np->phys[0]; + if (phy_idx == 0) { + dev_warn(&dev->dev, + "MII PHY not found -- this device may not operate correctly\n"); + } + } + + find_cnt++; + return 0; + +err_out_cleardev: + pci_set_drvdata(pdev, NULL); + pci_iounmap(pdev, ioaddr); +err_out_free_res: + pci_release_regions(pdev); +err_out_netdev: + free_netdev (dev); + return -ENODEV; +} + + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are + often serial bit streams generated by the host processor. + The example below is for the common 93c46 EEPROM, 64 16 bit words. */ + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need + a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that + made udelay() unreliable. + The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is + deprecated. +*/ +#define eeprom_delay(ee_addr) ioread32(ee_addr) + +enum EEPROM_Ctrl_Bits { + EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805, + EE_ChipSelect=0x801, EE_DataIn=0x08, +}; + +/* The EEPROM commands include the alway-set leading bit. */ +enum EEPROM_Cmds { + EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), +}; + +static int eeprom_read(void __iomem *addr, int location) +{ + int i; + int retval = 0; + void __iomem *ee_addr = addr + EECtrl; + int read_cmd = location | EE_ReadCmd; + iowrite32(EE_ChipSelect, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + iowrite32(dataval, ee_addr); + eeprom_delay(ee_addr); + iowrite32(dataval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + iowrite32(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + + for (i = 16; i > 0; i--) { + iowrite32(EE_ChipSelect | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + retval = (retval << 1) | ((ioread32(ee_addr) & EE_DataIn) ? 1 : 0); + iowrite32(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + } + + /* Terminate the EEPROM access. */ + iowrite32(0, ee_addr); + return retval; +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. + + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back 33Mhz PCI cycles. */ +#define mdio_delay(mdio_addr) ioread32(mdio_addr) + +/* Set iff a MII transceiver on any interface requires mdio preamble. + This only set with older transceivers, so the extra + code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 1; + +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void mdio_sync(void __iomem *mdio_addr) +{ + int bits = 32; + + /* Establish sync by sending at least 32 logic ones. */ + while (--bits >= 0) { + iowrite32(MDIO_WRITE1, mdio_addr); + mdio_delay(mdio_addr); + iowrite32(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } +} + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *mdio_addr = np->base_addr + MIICtrl; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i, retval = 0; + + if (mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + iowrite32(dataval, mdio_addr); + mdio_delay(mdio_addr); + iowrite32(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 20; i > 0; i--) { + iowrite32(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + retval = (retval << 1) | ((ioread32(mdio_addr) & MDIO_DataIn) ? 1 : 0); + iowrite32(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *mdio_addr = np->base_addr + MIICtrl; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + int i; + + if (location == 4 && phy_id == np->phys[0]) + np->mii_if.advertising = value; + + if (mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + iowrite32(dataval, mdio_addr); + mdio_delay(mdio_addr); + iowrite32(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + iowrite32(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + iowrite32(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } +} + + +static int netdev_open(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + int i; + + iowrite32(0x00000001, ioaddr + PCIBusCfg); /* Reset */ + + netif_device_detach(dev); + i = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev); + if (i) + goto out_err; + + if (debug > 1) + netdev_dbg(dev, "w89c840_open() irq %d\n", dev->irq); + + if((i=alloc_ringdesc(dev))) + goto out_err; + + spin_lock_irq(&np->lock); + netif_device_attach(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + + netif_start_queue(dev); + if (debug > 2) + netdev_dbg(dev, "Done netdev_open()\n"); + + /* Set the timer to check for link beat. */ + init_timer(&np->timer); + np->timer.expires = jiffies + 1*HZ; + np->timer.data = (unsigned long)dev; + np->timer.function = netdev_timer; /* timer handler */ + add_timer(&np->timer); + return 0; +out_err: + netif_device_attach(dev); + return i; +} + +#define MII_DAVICOM_DM9101 0x0181b800 + +static int update_link(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + int duplex, fasteth, result, mii_reg; + + /* BSMR */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); + + if (mii_reg == 0xffff) + return np->csr6; + /* reread: the link status bit is sticky */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); + if (!(mii_reg & 0x4)) { + if (netif_carrier_ok(dev)) { + if (debug) + dev_info(&dev->dev, + "MII #%d reports no link. Disabling watchdog\n", + np->phys[0]); + netif_carrier_off(dev); + } + return np->csr6; + } + if (!netif_carrier_ok(dev)) { + if (debug) + dev_info(&dev->dev, + "MII #%d link is back. Enabling watchdog\n", + np->phys[0]); + netif_carrier_on(dev); + } + + if ((np->mii & ~0xf) == MII_DAVICOM_DM9101) { + /* If the link partner doesn't support autonegotiation + * the MII detects it's abilities with the "parallel detection". + * Some MIIs update the LPA register to the result of the parallel + * detection, some don't. + * The Davicom PHY [at least 0181b800] doesn't. + * Instead bit 9 and 13 of the BMCR are updated to the result + * of the negotiation.. + */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMCR); + duplex = mii_reg & BMCR_FULLDPLX; + fasteth = mii_reg & BMCR_SPEED100; + } else { + int negotiated; + mii_reg = mdio_read(dev, np->phys[0], MII_LPA); + negotiated = mii_reg & np->mii_if.advertising; + + duplex = (negotiated & LPA_100FULL) || ((negotiated & 0x02C0) == LPA_10FULL); + fasteth = negotiated & 0x380; + } + duplex |= np->mii_if.force_media; + /* remove fastether and fullduplex */ + result = np->csr6 & ~0x20000200; + if (duplex) + result |= 0x200; + if (fasteth) + result |= 0x20000000; + if (result != np->csr6 && debug) + dev_info(&dev->dev, + "Setting %dMBit-%s-duplex based on MII#%d\n", + fasteth ? 100 : 10, duplex ? "full" : "half", + np->phys[0]); + return result; +} + +#define RXTX_TIMEOUT 2000 +static inline void update_csr6(struct net_device *dev, int new) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + int limit = RXTX_TIMEOUT; + + if (!netif_device_present(dev)) + new = 0; + if (new==np->csr6) + return; + /* stop both Tx and Rx processes */ + iowrite32(np->csr6 & ~0x2002, ioaddr + NetworkConfig); + /* wait until they have really stopped */ + for (;;) { + int csr5 = ioread32(ioaddr + IntrStatus); + int t; + + t = (csr5 >> 17) & 0x07; + if (t==0||t==1) { + /* rx stopped */ + t = (csr5 >> 20) & 0x07; + if (t==0||t==1) + break; + } + + limit--; + if(!limit) { + dev_info(&dev->dev, + "couldn't stop rxtx, IntrStatus %xh\n", csr5); + break; + } + udelay(1); + } + np->csr6 = new; + /* and restart them with the new configuration */ + iowrite32(np->csr6, ioaddr + NetworkConfig); + if (new & 0x200) + np->mii_if.full_duplex = 1; +} + +static void netdev_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + + if (debug > 2) + netdev_dbg(dev, "Media selection timer tick, status %08x config %08x\n", + ioread32(ioaddr + IntrStatus), + ioread32(ioaddr + NetworkConfig)); + spin_lock_irq(&np->lock); + update_csr6(dev, update_link(dev)); + spin_unlock_irq(&np->lock); + np->timer.expires = jiffies + 10*HZ; + add_timer(&np->timer); +} + +static void init_rxtx_rings(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + int i; + + np->rx_head_desc = &np->rx_ring[0]; + np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE]; + + /* Initial all Rx descriptors. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].length = np->rx_buf_sz; + np->rx_ring[i].status = 0; + np->rx_skbuff[i] = NULL; + } + /* Mark the last entry as wrapping the ring. */ + np->rx_ring[i-1].length |= DescEndRing; + + /* Fill in the Rx buffers. Handle allocation failure gracefully. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[i] = skb; + if (skb == NULL) + break; + np->rx_addr[i] = pci_map_single(np->pci_dev,skb->data, + np->rx_buf_sz,PCI_DMA_FROMDEVICE); + + np->rx_ring[i].buffer1 = np->rx_addr[i]; + np->rx_ring[i].status = DescOwned; + } + + np->cur_rx = 0; + np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* Initialize the Tx descriptors */ + for (i = 0; i < TX_RING_SIZE; i++) { + np->tx_skbuff[i] = NULL; + np->tx_ring[i].status = 0; + } + np->tx_full = 0; + np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0; + + iowrite32(np->ring_dma_addr, np->base_addr + RxRingPtr); + iowrite32(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE, + np->base_addr + TxRingPtr); + +} + +static void free_rxtx_rings(struct netdev_private* np) +{ + int i; + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].status = 0; + if (np->rx_skbuff[i]) { + pci_unmap_single(np->pci_dev, + np->rx_addr[i], + np->rx_skbuff[i]->len, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(np->rx_skbuff[i]); + } + np->rx_skbuff[i] = NULL; + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (np->tx_skbuff[i]) { + pci_unmap_single(np->pci_dev, + np->tx_addr[i], + np->tx_skbuff[i]->len, + PCI_DMA_TODEVICE); + dev_kfree_skb(np->tx_skbuff[i]); + } + np->tx_skbuff[i] = NULL; + } +} + +static void init_registers(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + int i; + + for (i = 0; i < 6; i++) + iowrite8(dev->dev_addr[i], ioaddr + StationAddr + i); + + /* Initialize other registers. */ +#ifdef __BIG_ENDIAN + i = (1<<20); /* Big-endian descriptors */ +#else + i = 0; +#endif + i |= (0x04<<2); /* skip length 4 u32 */ + i |= 0x02; /* give Rx priority */ + + /* Configure the PCI bus bursts and FIFO thresholds. + 486: Set 8 longword cache alignment, 8 longword burst. + 586: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 0000 align to cache 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords */ + +#if defined (__i386__) && !defined(MODULE) + /* When not a module we can work around broken '486 PCI boards. */ + if (boot_cpu_data.x86 <= 4) { + i |= 0x4800; + dev_info(&dev->dev, + "This is a 386/486 PCI system, setting cache alignment to 8 longwords\n"); + } else { + i |= 0xE000; + } +#elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) + i |= 0xE000; +#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC) + i |= 0x4800; +#else +#warning Processor architecture undefined + i |= 0x4800; +#endif + iowrite32(i, ioaddr + PCIBusCfg); + + np->csr6 = 0; + /* 128 byte Tx threshold; + Transmit on; Receive on; */ + update_csr6(dev, 0x00022002 | update_link(dev) | __set_rx_mode(dev)); + + /* Clear and Enable interrupts by setting the interrupt mask. */ + iowrite32(0x1A0F5, ioaddr + IntrStatus); + iowrite32(0x1A0F5, ioaddr + IntrEnable); + + iowrite32(0, ioaddr + RxStartDemand); +} + +static void tx_timeout(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + + dev_warn(&dev->dev, "Transmit timed out, status %08x, resetting...\n", + ioread32(ioaddr + IntrStatus)); + + { + int i; + printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(KERN_CONT " %08x", (unsigned int)np->rx_ring[i].status); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG " Tx ring %p: ", np->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(KERN_CONT " %08x", np->tx_ring[i].status); + printk(KERN_CONT "\n"); + } + printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d\n", + np->cur_tx, np->dirty_tx, np->tx_full, np->tx_q_bytes); + printk(KERN_DEBUG "Tx Descriptor addr %xh\n", ioread32(ioaddr+0x4C)); + + disable_irq(dev->irq); + spin_lock_irq(&np->lock); + /* + * Under high load dirty_tx and the internal tx descriptor pointer + * come out of sync, thus perform a software reset and reinitialize + * everything. + */ + + iowrite32(1, np->base_addr+PCIBusCfg); + udelay(1); + + free_rxtx_rings(np); + init_rxtx_rings(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + enable_irq(dev->irq); + + netif_wake_queue(dev); + dev->trans_start = jiffies; /* prevent tx timeout */ + np->stats.tx_errors++; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static int alloc_ringdesc(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + + np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + + np->rx_ring = pci_alloc_consistent(np->pci_dev, + sizeof(struct w840_rx_desc)*RX_RING_SIZE + + sizeof(struct w840_tx_desc)*TX_RING_SIZE, + &np->ring_dma_addr); + if(!np->rx_ring) + return -ENOMEM; + init_rxtx_rings(dev); + return 0; +} + +static void free_ringdesc(struct netdev_private *np) +{ + pci_free_consistent(np->pci_dev, + sizeof(struct w840_rx_desc)*RX_RING_SIZE + + sizeof(struct w840_tx_desc)*TX_RING_SIZE, + np->rx_ring, np->ring_dma_addr); + +} + +static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + unsigned entry; + + /* Caution: the write order is important here, set the field + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = np->cur_tx % TX_RING_SIZE; + + np->tx_addr[entry] = pci_map_single(np->pci_dev, + skb->data,skb->len, PCI_DMA_TODEVICE); + np->tx_skbuff[entry] = skb; + + np->tx_ring[entry].buffer1 = np->tx_addr[entry]; + if (skb->len < TX_BUFLIMIT) { + np->tx_ring[entry].length = DescWholePkt | skb->len; + } else { + int len = skb->len - TX_BUFLIMIT; + + np->tx_ring[entry].buffer2 = np->tx_addr[entry]+TX_BUFLIMIT; + np->tx_ring[entry].length = DescWholePkt | (len << 11) | TX_BUFLIMIT; + } + if(entry == TX_RING_SIZE-1) + np->tx_ring[entry].length |= DescEndRing; + + /* Now acquire the irq spinlock. + * The difficult race is the ordering between + * increasing np->cur_tx and setting DescOwned: + * - if np->cur_tx is increased first the interrupt + * handler could consider the packet as transmitted + * since DescOwned is cleared. + * - If DescOwned is set first the NIC could report the + * packet as sent, but the interrupt handler would ignore it + * since the np->cur_tx was not yet increased. + */ + spin_lock_irq(&np->lock); + np->cur_tx++; + + wmb(); /* flush length, buffer1, buffer2 */ + np->tx_ring[entry].status = DescOwned; + wmb(); /* flush status and kick the hardware */ + iowrite32(0, np->base_addr + TxStartDemand); + np->tx_q_bytes += skb->len; + /* Work around horrible bug in the chip by marking the queue as full + when we do not have FIFO room for a maximum sized packet. */ + if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN || + ((np->drv_flags & HasBrokenTx) && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)) { + netif_stop_queue(dev); + wmb(); + np->tx_full = 1; + } + spin_unlock_irq(&np->lock); + + if (debug > 4) { + netdev_dbg(dev, "Transmit frame #%d queued in slot %d\n", + np->cur_tx, entry); + } + return NETDEV_TX_OK; +} + +static void netdev_tx_done(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { + int entry = np->dirty_tx % TX_RING_SIZE; + int tx_status = np->tx_ring[entry].status; + + if (tx_status < 0) + break; + if (tx_status & 0x8000) { /* There was an error, log it. */ +#ifndef final_version + if (debug > 1) + netdev_dbg(dev, "Transmit error, Tx status %08x\n", + tx_status); +#endif + np->stats.tx_errors++; + if (tx_status & 0x0104) np->stats.tx_aborted_errors++; + if (tx_status & 0x0C80) np->stats.tx_carrier_errors++; + if (tx_status & 0x0200) np->stats.tx_window_errors++; + if (tx_status & 0x0002) np->stats.tx_fifo_errors++; + if ((tx_status & 0x0080) && np->mii_if.full_duplex == 0) + np->stats.tx_heartbeat_errors++; + } else { +#ifndef final_version + if (debug > 3) + netdev_dbg(dev, "Transmit slot %d ok, Tx status %08x\n", + entry, tx_status); +#endif + np->stats.tx_bytes += np->tx_skbuff[entry]->len; + np->stats.collisions += (tx_status >> 3) & 15; + np->stats.tx_packets++; + } + /* Free the original skb. */ + pci_unmap_single(np->pci_dev,np->tx_addr[entry], + np->tx_skbuff[entry]->len, + PCI_DMA_TODEVICE); + np->tx_q_bytes -= np->tx_skbuff[entry]->len; + dev_kfree_skb_irq(np->tx_skbuff[entry]); + np->tx_skbuff[entry] = NULL; + } + if (np->tx_full && + np->cur_tx - np->dirty_tx < TX_QUEUE_LEN_RESTART && + np->tx_q_bytes < TX_BUG_FIFO_LIMIT) { + /* The ring is no longer full, clear tbusy. */ + np->tx_full = 0; + wmb(); + netif_wake_queue(dev); + } +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static irqreturn_t intr_handler(int irq, void *dev_instance) +{ + struct net_device *dev = (struct net_device *)dev_instance; + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + int work_limit = max_interrupt_work; + int handled = 0; + + if (!netif_device_present(dev)) + return IRQ_NONE; + do { + u32 intr_status = ioread32(ioaddr + IntrStatus); + + /* Acknowledge all of the current interrupt sources ASAP. */ + iowrite32(intr_status & 0x001ffff, ioaddr + IntrStatus); + + if (debug > 4) + netdev_dbg(dev, "Interrupt, status %04x\n", intr_status); + + if ((intr_status & (NormalIntr|AbnormalIntr)) == 0) + break; + + handled = 1; + + if (intr_status & (RxIntr | RxNoBuf)) + netdev_rx(dev); + if (intr_status & RxNoBuf) + iowrite32(0, ioaddr + RxStartDemand); + + if (intr_status & (TxNoBuf | TxIntr) && + np->cur_tx != np->dirty_tx) { + spin_lock(&np->lock); + netdev_tx_done(dev); + spin_unlock(&np->lock); + } + + /* Abnormal error summary/uncommon events handlers. */ + if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SystemError | + TimerInt | TxDied)) + netdev_error(dev, intr_status); + + if (--work_limit < 0) { + dev_warn(&dev->dev, + "Too much work at interrupt, status=0x%04x\n", + intr_status); + /* Set the timer to re-enable the other interrupts after + 10*82usec ticks. */ + spin_lock(&np->lock); + if (netif_device_present(dev)) { + iowrite32(AbnormalIntr | TimerInt, ioaddr + IntrEnable); + iowrite32(10, ioaddr + GPTimer); + } + spin_unlock(&np->lock); + break; + } + } while (1); + + if (debug > 3) + netdev_dbg(dev, "exiting interrupt, status=%#4.4x\n", + ioread32(ioaddr + IntrStatus)); + return IRQ_RETVAL(handled); +} + +/* This routine is logically part of the interrupt handler, but separated + for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + int entry = np->cur_rx % RX_RING_SIZE; + int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx; + + if (debug > 4) { + netdev_dbg(dev, " In netdev_rx(), entry %d status %04x\n", + entry, np->rx_ring[entry].status); + } + + /* If EOP is set on the next entry, it's a new packet. Send it up. */ + while (--work_limit >= 0) { + struct w840_rx_desc *desc = np->rx_head_desc; + s32 status = desc->status; + + if (debug > 4) + netdev_dbg(dev, " netdev_rx() status was %08x\n", + status); + if (status < 0) + break; + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + dev_warn(&dev->dev, + "Oversized Ethernet frame spanned multiple buffers, entry %#x status %04x!\n", + np->cur_rx, status); + np->stats.rx_length_errors++; + } + } else if (status & 0x8000) { + /* There was a fatal error. */ + if (debug > 2) + netdev_dbg(dev, "Receive error, Rx status %08x\n", + status); + np->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) np->stats.rx_length_errors++; + if (status & 0x004C) np->stats.rx_frame_errors++; + if (status & 0x0002) np->stats.rx_crc_errors++; + } + } else { + struct sk_buff *skb; + /* Omit the four octet CRC from the length. */ + int pkt_len = ((status >> 16) & 0x7ff) - 4; + +#ifndef final_version + if (debug > 4) + netdev_dbg(dev, " netdev_rx() normal Rx pkt length %d status %x\n", + pkt_len, status); +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single_for_cpu(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); + skb_copy_to_linear_data(skb, np->rx_skbuff[entry]->data, pkt_len); + skb_put(skb, pkt_len); + pci_dma_sync_single_for_device(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); + } else { + pci_unmap_single(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); + skb_put(skb = np->rx_skbuff[entry], pkt_len); + np->rx_skbuff[entry] = NULL; + } +#ifndef final_version /* Remove after testing. */ + /* You will want this info for the initial debug. */ + if (debug > 5) + netdev_dbg(dev, " Rx data %pM %pM %02x%02x %pI4\n", + &skb->data[0], &skb->data[6], + skb->data[12], skb->data[13], + &skb->data[14]); +#endif + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + np->stats.rx_packets++; + np->stats.rx_bytes += pkt_len; + } + entry = (++np->cur_rx) % RX_RING_SIZE; + np->rx_head_desc = &np->rx_ring[entry]; + } + + /* Refill the Rx ring buffers. */ + for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { + struct sk_buff *skb; + entry = np->dirty_rx % RX_RING_SIZE; + if (np->rx_skbuff[entry] == NULL) { + skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[entry] = skb; + if (skb == NULL) + break; /* Better luck next round. */ + np->rx_addr[entry] = pci_map_single(np->pci_dev, + skb->data, + np->rx_buf_sz, PCI_DMA_FROMDEVICE); + np->rx_ring[entry].buffer1 = np->rx_addr[entry]; + } + wmb(); + np->rx_ring[entry].status = DescOwned; + } + + return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + + if (debug > 2) + netdev_dbg(dev, "Abnormal event, %08x\n", intr_status); + if (intr_status == 0xffffffff) + return; + spin_lock(&np->lock); + if (intr_status & TxFIFOUnderflow) { + int new; + /* Bump up the Tx threshold */ +#if 0 + /* This causes lots of dropped packets, + * and under high load even tx_timeouts + */ + new = np->csr6 + 0x4000; +#else + new = (np->csr6 >> 14)&0x7f; + if (new < 64) + new *= 2; + else + new = 127; /* load full packet before starting */ + new = (np->csr6 & ~(0x7F << 14)) | (new<<14); +#endif + netdev_dbg(dev, "Tx underflow, new csr6 %08x\n", new); + update_csr6(dev, new); + } + if (intr_status & RxDied) { /* Missed a Rx frame. */ + np->stats.rx_errors++; + } + if (intr_status & TimerInt) { + /* Re-enable other interrupts. */ + if (netif_device_present(dev)) + iowrite32(0x1A0F5, ioaddr + IntrEnable); + } + np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff; + iowrite32(0, ioaddr + RxStartDemand); + spin_unlock(&np->lock); +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + + /* The chip only need report frame silently dropped. */ + spin_lock_irq(&np->lock); + if (netif_running(dev) && netif_device_present(dev)) + np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff; + spin_unlock_irq(&np->lock); + + return &np->stats; +} + + +static u32 __set_rx_mode(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + u32 rx_mode; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = RxAcceptBroadcast | AcceptMulticast | RxAcceptAllPhys + | AcceptMyPhys; + } else if ((netdev_mc_count(dev) > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { + /* Too many to match, or accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys; + } else { + struct netdev_hw_addr *ha; + + memset(mc_filter, 0, sizeof(mc_filter)); + netdev_for_each_mc_addr(ha, dev) { + int filbit; + + filbit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F; + filbit &= 0x3f; + mc_filter[filbit >> 5] |= 1 << (filbit & 31); + } + rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys; + } + iowrite32(mc_filter[0], ioaddr + MulticastFilter0); + iowrite32(mc_filter[1], ioaddr + MulticastFilter1); + return rx_mode; +} + +static void set_rx_mode(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + u32 rx_mode = __set_rx_mode(dev); + spin_lock_irq(&np->lock); + update_csr6(dev, (np->csr6 & ~0x00F8) | rx_mode); + spin_unlock_irq(&np->lock); +} + +static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct netdev_private *np = netdev_priv(dev); + + strcpy (info->driver, DRV_NAME); + strcpy (info->version, DRV_VERSION); + strcpy (info->bus_info, pci_name(np->pci_dev)); +} + +static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct netdev_private *np = netdev_priv(dev); + int rc; + + spin_lock_irq(&np->lock); + rc = mii_ethtool_gset(&np->mii_if, cmd); + spin_unlock_irq(&np->lock); + + return rc; +} + +static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct netdev_private *np = netdev_priv(dev); + int rc; + + spin_lock_irq(&np->lock); + rc = mii_ethtool_sset(&np->mii_if, cmd); + spin_unlock_irq(&np->lock); + + return rc; +} + +static int netdev_nway_reset(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return mii_nway_restart(&np->mii_if); +} + +static u32 netdev_get_link(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return mii_link_ok(&np->mii_if); +} + +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return debug; +} + +static void netdev_set_msglevel(struct net_device *dev, u32 value) +{ + debug = value; +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_settings = netdev_get_settings, + .set_settings = netdev_set_settings, + .nway_reset = netdev_nway_reset, + .get_link = netdev_get_link, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; + +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct mii_ioctl_data *data = if_mii(rq); + struct netdev_private *np = netdev_priv(dev); + + switch(cmd) { + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + data->phy_id = ((struct netdev_private *)netdev_priv(dev))->phys[0] & 0x1f; + /* Fall Through */ + + case SIOCGMIIREG: /* Read MII PHY register. */ + spin_lock_irq(&np->lock); + data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f); + spin_unlock_irq(&np->lock); + return 0; + + case SIOCSMIIREG: /* Write MII PHY register. */ + spin_lock_irq(&np->lock); + mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); + spin_unlock_irq(&np->lock); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int netdev_close(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + + netif_stop_queue(dev); + + if (debug > 1) { + netdev_dbg(dev, "Shutting down ethercard, status was %08x Config %08x\n", + ioread32(ioaddr + IntrStatus), + ioread32(ioaddr + NetworkConfig)); + netdev_dbg(dev, "Queue pointers were Tx %d / %d, Rx %d / %d\n", + np->cur_tx, np->dirty_tx, + np->cur_rx, np->dirty_rx); + } + + /* Stop the chip's Tx and Rx processes. */ + spin_lock_irq(&np->lock); + netif_device_detach(dev); + update_csr6(dev, 0); + iowrite32(0x0000, ioaddr + IntrEnable); + spin_unlock_irq(&np->lock); + + free_irq(dev->irq, dev); + wmb(); + netif_device_attach(dev); + + if (ioread32(ioaddr + NetworkConfig) != 0xffffffff) + np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff; + +#ifdef __i386__ + if (debug > 2) { + int i; + + printk(KERN_DEBUG" Tx ring at %p:\n", np->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(KERN_DEBUG " #%d desc. %04x %04x %08x\n", + i, np->tx_ring[i].length, + np->tx_ring[i].status, np->tx_ring[i].buffer1); + printk(KERN_DEBUG " Rx ring %p:\n", np->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) { + printk(KERN_DEBUG " #%d desc. %04x %04x %08x\n", + i, np->rx_ring[i].length, + np->rx_ring[i].status, np->rx_ring[i].buffer1); + } + } +#endif /* __i386__ debugging only */ + + del_timer_sync(&np->timer); + + free_rxtx_rings(np); + free_ringdesc(np); + + return 0; +} + +static void __devexit w840_remove1 (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (dev) { + struct netdev_private *np = netdev_priv(dev); + unregister_netdev(dev); + pci_release_regions(pdev); + pci_iounmap(pdev, np->base_addr); + free_netdev(dev); + } + + pci_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM + +/* + * suspend/resume synchronization: + * - open, close, do_ioctl: + * rtnl_lock, & netif_device_detach after the rtnl_unlock. + * - get_stats: + * spin_lock_irq(np->lock), doesn't touch hw if not present + * - start_xmit: + * synchronize_irq + netif_tx_disable; + * - tx_timeout: + * netif_device_detach + netif_tx_disable; + * - set_multicast_list + * netif_device_detach + netif_tx_disable; + * - interrupt handler + * doesn't touch hw if not present, synchronize_irq waits for + * running instances of the interrupt handler. + * + * Disabling hw requires clearing csr6 & IntrEnable. + * update_csr6 & all function that write IntrEnable check netif_device_present + * before settings any bits. + * + * Detach must occur under spin_unlock_irq(), interrupts from a detached + * device would cause an irq storm. + */ +static int w840_suspend (struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->base_addr; + + rtnl_lock(); + if (netif_running (dev)) { + del_timer_sync(&np->timer); + + spin_lock_irq(&np->lock); + netif_device_detach(dev); + update_csr6(dev, 0); + iowrite32(0, ioaddr + IntrEnable); + spin_unlock_irq(&np->lock); + + synchronize_irq(dev->irq); + netif_tx_disable(dev); + + np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff; + + /* no more hardware accesses behind this line. */ + + BUG_ON(np->csr6 || ioread32(ioaddr + IntrEnable)); + + /* pci_power_off(pdev, -1); */ + + free_rxtx_rings(np); + } else { + netif_device_detach(dev); + } + rtnl_unlock(); + return 0; +} + +static int w840_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct netdev_private *np = netdev_priv(dev); + int retval = 0; + + rtnl_lock(); + if (netif_device_present(dev)) + goto out; /* device not suspended */ + if (netif_running(dev)) { + if ((retval = pci_enable_device(pdev))) { + dev_err(&dev->dev, + "pci_enable_device failed in resume\n"); + goto out; + } + spin_lock_irq(&np->lock); + iowrite32(1, np->base_addr+PCIBusCfg); + ioread32(np->base_addr+PCIBusCfg); + udelay(1); + netif_device_attach(dev); + init_rxtx_rings(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + + netif_wake_queue(dev); + + mod_timer(&np->timer, jiffies + 1*HZ); + } else { + netif_device_attach(dev); + } +out: + rtnl_unlock(); + return retval; +} +#endif + +static struct pci_driver w840_driver = { + .name = DRV_NAME, + .id_table = w840_pci_tbl, + .probe = w840_probe1, + .remove = __devexit_p(w840_remove1), +#ifdef CONFIG_PM + .suspend = w840_suspend, + .resume = w840_resume, +#endif +}; + +static int __init w840_init(void) +{ + printk(version); + return pci_register_driver(&w840_driver); +} + +static void __exit w840_exit(void) +{ + pci_unregister_driver(&w840_driver); +} + +module_init(w840_init); +module_exit(w840_exit); diff --git a/drivers/net/ethernet/dec/tulip/xircom_cb.c b/drivers/net/ethernet/dec/tulip/xircom_cb.c new file mode 100644 index 000000000000..988b8eb24d37 --- /dev/null +++ b/drivers/net/ethernet/dec/tulip/xircom_cb.c @@ -0,0 +1,1154 @@ +/* + * xircom_cb: A driver for the (tulip-like) Xircom Cardbus ethernet cards + * + * This software is (C) by the respective authors, and licensed under the GPL + * License. + * + * Written by Arjan van de Ven for Red Hat, Inc. + * Based on work by Jeff Garzik, Doug Ledford and Donald Becker + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * + * $Id: xircom_cb.c,v 1.33 2001/03/19 14:02:07 arjanv Exp $ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef CONFIG_NET_POLL_CONTROLLER +#include +#endif + +MODULE_DESCRIPTION("Xircom Cardbus ethernet driver"); +MODULE_AUTHOR("Arjan van de Ven "); +MODULE_LICENSE("GPL"); + + + +/* IO registers on the card, offsets */ +#define CSR0 0x00 +#define CSR1 0x08 +#define CSR2 0x10 +#define CSR3 0x18 +#define CSR4 0x20 +#define CSR5 0x28 +#define CSR6 0x30 +#define CSR7 0x38 +#define CSR8 0x40 +#define CSR9 0x48 +#define CSR10 0x50 +#define CSR11 0x58 +#define CSR12 0x60 +#define CSR13 0x68 +#define CSR14 0x70 +#define CSR15 0x78 +#define CSR16 0x80 + +/* PCI registers */ +#define PCI_POWERMGMT 0x40 + +/* Offsets of the buffers within the descriptor pages, in bytes */ + +#define NUMDESCRIPTORS 4 + +static int bufferoffsets[NUMDESCRIPTORS] = {128,2048,4096,6144}; + + +struct xircom_private { + /* Send and receive buffers, kernel-addressable and dma addressable forms */ + + __le32 *rx_buffer; + __le32 *tx_buffer; + + dma_addr_t rx_dma_handle; + dma_addr_t tx_dma_handle; + + struct sk_buff *tx_skb[4]; + + unsigned long io_port; + int open; + + /* transmit_used is the rotating counter that indicates which transmit + descriptor has to be used next */ + int transmit_used; + + /* Spinlock to serialize register operations. + It must be helt while manipulating the following registers: + CSR0, CSR6, CSR7, CSR9, CSR10, CSR15 + */ + spinlock_t lock; + + struct pci_dev *pdev; + struct net_device *dev; +}; + + +/* Function prototypes */ +static int xircom_probe(struct pci_dev *pdev, const struct pci_device_id *id); +static void xircom_remove(struct pci_dev *pdev); +static irqreturn_t xircom_interrupt(int irq, void *dev_instance); +static netdev_tx_t xircom_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static int xircom_open(struct net_device *dev); +static int xircom_close(struct net_device *dev); +static void xircom_up(struct xircom_private *card); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void xircom_poll_controller(struct net_device *dev); +#endif + +static void investigate_read_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset); +static void investigate_write_descriptor(struct net_device *dev, struct xircom_private *card, int descnr, unsigned int bufferoffset); +static void read_mac_address(struct xircom_private *card); +static void transceiver_voodoo(struct xircom_private *card); +static void initialize_card(struct xircom_private *card); +static void trigger_transmit(struct xircom_private *card); +static void trigger_receive(struct xircom_private *card); +static void setup_descriptors(struct xircom_private *card); +static void remove_descriptors(struct xircom_private *card); +static int link_status_changed(struct xircom_private *card); +static void activate_receiver(struct xircom_private *card); +static void deactivate_receiver(struct xircom_private *card); +static void activate_transmitter(struct xircom_private *card); +static void deactivate_transmitter(struct xircom_private *card); +static void enable_transmit_interrupt(struct xircom_private *card); +static void enable_receive_interrupt(struct xircom_private *card); +static void enable_link_interrupt(struct xircom_private *card); +static void disable_all_interrupts(struct xircom_private *card); +static int link_status(struct xircom_private *card); + + + +static DEFINE_PCI_DEVICE_TABLE(xircom_pci_table) = { + {0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID,}, + {0,}, +}; +MODULE_DEVICE_TABLE(pci, xircom_pci_table); + +static struct pci_driver xircom_ops = { + .name = "xircom_cb", + .id_table = xircom_pci_table, + .probe = xircom_probe, + .remove = xircom_remove, + .suspend =NULL, + .resume =NULL +}; + + +#if defined DEBUG && DEBUG > 1 +static void print_binary(unsigned int number) +{ + int i,i2; + char buffer[64]; + memset(buffer,0,64); + i2=0; + for (i=31;i>=0;i--) { + if (number & (1<rx_buffer = pci_alloc_consistent(pdev,8192,&private->rx_dma_handle); + if (private->rx_buffer == NULL) { + pr_err("%s: no memory for rx buffer\n", __func__); + goto rx_buf_fail; + } + private->tx_buffer = pci_alloc_consistent(pdev,8192,&private->tx_dma_handle); + if (private->tx_buffer == NULL) { + pr_err("%s: no memory for tx buffer\n", __func__); + goto tx_buf_fail; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + + + private->dev = dev; + private->pdev = pdev; + private->io_port = pci_resource_start(pdev, 0); + spin_lock_init(&private->lock); + dev->irq = pdev->irq; + dev->base_addr = private->io_port; + + initialize_card(private); + read_mac_address(private); + setup_descriptors(private); + + dev->netdev_ops = &netdev_ops; + pci_set_drvdata(pdev, dev); + + if (register_netdev(dev)) { + pr_err("%s: netdevice registration failed\n", __func__); + goto reg_fail; + } + + netdev_info(dev, "Xircom cardbus revision %i at irq %i\n", + pdev->revision, pdev->irq); + /* start the transmitter to get a heartbeat */ + /* TODO: send 2 dummy packets here */ + transceiver_voodoo(private); + + spin_lock_irqsave(&private->lock,flags); + activate_transmitter(private); + activate_receiver(private); + spin_unlock_irqrestore(&private->lock,flags); + + trigger_receive(private); + + return 0; + +reg_fail: + kfree(private->tx_buffer); +tx_buf_fail: + kfree(private->rx_buffer); +rx_buf_fail: + free_netdev(dev); +device_fail: + return -ENODEV; +} + + +/* + xircom_remove is called on module-unload or on device-eject. + it unregisters the irq, io-region and network device. + Interrupts and such are already stopped in the "ifconfig ethX down" + code. + */ +static void __devexit xircom_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct xircom_private *card = netdev_priv(dev); + + pci_free_consistent(pdev,8192,card->rx_buffer,card->rx_dma_handle); + pci_free_consistent(pdev,8192,card->tx_buffer,card->tx_dma_handle); + + release_region(dev->base_addr, 128); + unregister_netdev(dev); + free_netdev(dev); + pci_set_drvdata(pdev, NULL); +} + +static irqreturn_t xircom_interrupt(int irq, void *dev_instance) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct xircom_private *card = netdev_priv(dev); + unsigned int status; + int i; + + spin_lock(&card->lock); + status = inl(card->io_port+CSR5); + +#if defined DEBUG && DEBUG > 1 + print_binary(status); + pr_debug("tx status 0x%08x 0x%08x\n", + card->tx_buffer[0], card->tx_buffer[4]); + pr_debug("rx status 0x%08x 0x%08x\n", + card->rx_buffer[0], card->rx_buffer[4]); +#endif + /* Handle shared irq and hotplug */ + if (status == 0 || status == 0xffffffff) { + spin_unlock(&card->lock); + return IRQ_NONE; + } + + if (link_status_changed(card)) { + int newlink; + netdev_dbg(dev, "Link status has changed\n"); + newlink = link_status(card); + netdev_info(dev, "Link is %d mbit\n", newlink); + if (newlink) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + + } + + /* Clear all remaining interrupts */ + status |= 0xffffffff; /* FIXME: make this clear only the + real existing bits */ + outl(status,card->io_port+CSR5); + + + for (i=0;ilock); + return IRQ_HANDLED; +} + +static netdev_tx_t xircom_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct xircom_private *card; + unsigned long flags; + int nextdescriptor; + int desc; + + card = netdev_priv(dev); + spin_lock_irqsave(&card->lock,flags); + + /* First see if we can free some descriptors */ + for (desc=0;desctransmit_used +1) % (NUMDESCRIPTORS); + desc = card->transmit_used; + + /* only send the packet if the descriptor is free */ + if (card->tx_buffer[4*desc]==0) { + /* Copy the packet data; zero the memory first as the card + sometimes sends more than you ask it to. */ + + memset(&card->tx_buffer[bufferoffsets[desc]/4],0,1536); + skb_copy_from_linear_data(skb, + &(card->tx_buffer[bufferoffsets[desc] / 4]), + skb->len); + /* FIXME: The specification tells us that the length we send HAS to be a multiple of + 4 bytes. */ + + card->tx_buffer[4*desc+1] = cpu_to_le32(skb->len); + if (desc == NUMDESCRIPTORS - 1) /* bit 25: last descriptor of the ring */ + card->tx_buffer[4*desc+1] |= cpu_to_le32(1<<25); + + card->tx_buffer[4*desc+1] |= cpu_to_le32(0xF0000000); + /* 0xF0... means want interrupts*/ + card->tx_skb[desc] = skb; + + wmb(); + /* This gives the descriptor to the card */ + card->tx_buffer[4*desc] = cpu_to_le32(0x80000000); + trigger_transmit(card); + if (card->tx_buffer[nextdescriptor*4] & cpu_to_le32(0x8000000)) { + /* next descriptor is occupied... */ + netif_stop_queue(dev); + } + card->transmit_used = nextdescriptor; + spin_unlock_irqrestore(&card->lock,flags); + return NETDEV_TX_OK; + } + + /* Uh oh... no free descriptor... drop the packet */ + netif_stop_queue(dev); + spin_unlock_irqrestore(&card->lock,flags); + trigger_transmit(card); + + return NETDEV_TX_BUSY; +} + + + + +static int xircom_open(struct net_device *dev) +{ + struct xircom_private *xp = netdev_priv(dev); + int retval; + + netdev_info(dev, "xircom cardbus adaptor found, using irq %i\n", + dev->irq); + retval = request_irq(dev->irq, xircom_interrupt, IRQF_SHARED, dev->name, dev); + if (retval) + return retval; + + xircom_up(xp); + xp->open = 1; + + return 0; +} + +static int xircom_close(struct net_device *dev) +{ + struct xircom_private *card; + unsigned long flags; + + card = netdev_priv(dev); + netif_stop_queue(dev); /* we don't want new packets */ + + + spin_lock_irqsave(&card->lock,flags); + + disable_all_interrupts(card); +#if 0 + /* We can enable this again once we send dummy packets on ifconfig ethX up */ + deactivate_receiver(card); + deactivate_transmitter(card); +#endif + remove_descriptors(card); + + spin_unlock_irqrestore(&card->lock,flags); + + card->open = 0; + free_irq(dev->irq,dev); + + return 0; + +} + + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void xircom_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + xircom_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + + +static void initialize_card(struct xircom_private *card) +{ + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + /* First: reset the card */ + val = inl(card->io_port + CSR0); + val |= 0x01; /* Software reset */ + outl(val, card->io_port + CSR0); + + udelay(100); /* give the card some time to reset */ + + val = inl(card->io_port + CSR0); + val &= ~0x01; /* disable Software reset */ + outl(val, card->io_port + CSR0); + + + val = 0; /* Value 0x00 is a safe and conservative value + for the PCI configuration settings */ + outl(val, card->io_port + CSR0); + + + disable_all_interrupts(card); + deactivate_receiver(card); + deactivate_transmitter(card); + + spin_unlock_irqrestore(&card->lock, flags); +} + +/* +trigger_transmit causes the card to check for frames to be transmitted. +This is accomplished by writing to the CSR1 port. The documentation +claims that the act of writing is sufficient and that the value is +ignored; I chose zero. +*/ +static void trigger_transmit(struct xircom_private *card) +{ + unsigned int val; + + val = 0; + outl(val, card->io_port + CSR1); +} + +/* +trigger_receive causes the card to check for empty frames in the +descriptor list in which packets can be received. +This is accomplished by writing to the CSR2 port. The documentation +claims that the act of writing is sufficient and that the value is +ignored; I chose zero. +*/ +static void trigger_receive(struct xircom_private *card) +{ + unsigned int val; + + val = 0; + outl(val, card->io_port + CSR2); +} + +/* +setup_descriptors initializes the send and receive buffers to be valid +descriptors and programs the addresses into the card. +*/ +static void setup_descriptors(struct xircom_private *card) +{ + u32 address; + int i; + + BUG_ON(card->rx_buffer == NULL); + BUG_ON(card->tx_buffer == NULL); + + /* Receive descriptors */ + memset(card->rx_buffer, 0, 128); /* clear the descriptors */ + for (i=0;i 0x80000000 */ + card->rx_buffer[i*4 + 0] = cpu_to_le32(0x80000000); + /* Rx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ + card->rx_buffer[i*4 + 1] = cpu_to_le32(1536); + if (i == NUMDESCRIPTORS - 1) /* bit 25 is "last descriptor" */ + card->rx_buffer[i*4 + 1] |= cpu_to_le32(1 << 25); + + /* Rx Descr2: address of the buffer + we store the buffer at the 2nd half of the page */ + + address = card->rx_dma_handle; + card->rx_buffer[i*4 + 2] = cpu_to_le32(address + bufferoffsets[i]); + /* Rx Desc3: address of 2nd buffer -> 0 */ + card->rx_buffer[i*4 + 3] = 0; + } + + wmb(); + /* Write the receive descriptor ring address to the card */ + address = card->rx_dma_handle; + outl(address, card->io_port + CSR3); /* Receive descr list address */ + + + /* transmit descriptors */ + memset(card->tx_buffer, 0, 128); /* clear the descriptors */ + + for (i=0;i 0x00000000 */ + card->tx_buffer[i*4 + 0] = 0x00000000; + /* Tx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ + card->tx_buffer[i*4 + 1] = cpu_to_le32(1536); + if (i == NUMDESCRIPTORS - 1) /* bit 25 is "last descriptor" */ + card->tx_buffer[i*4 + 1] |= cpu_to_le32(1 << 25); + + /* Tx Descr2: address of the buffer + we store the buffer at the 2nd half of the page */ + address = card->tx_dma_handle; + card->tx_buffer[i*4 + 2] = cpu_to_le32(address + bufferoffsets[i]); + /* Tx Desc3: address of 2nd buffer -> 0 */ + card->tx_buffer[i*4 + 3] = 0; + } + + wmb(); + /* wite the transmit descriptor ring to the card */ + address = card->tx_dma_handle; + outl(address, card->io_port + CSR4); /* xmit descr list address */ +} + +/* +remove_descriptors informs the card the descriptors are no longer +valid by setting the address in the card to 0x00. +*/ +static void remove_descriptors(struct xircom_private *card) +{ + unsigned int val; + + val = 0; + outl(val, card->io_port + CSR3); /* Receive descriptor address */ + outl(val, card->io_port + CSR4); /* Send descriptor address */ +} + +/* +link_status_changed returns 1 if the card has indicated that +the link status has changed. The new link status has to be read from CSR12. + +This function also clears the status-bit. +*/ +static int link_status_changed(struct xircom_private *card) +{ + unsigned int val; + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (1 << 27)) == 0) /* no change */ + return 0; + + /* clear the event by writing a 1 to the bit in the + status register. */ + val = (1 << 27); + outl(val, card->io_port + CSR5); + + return 1; +} + + +/* +transmit_active returns 1 if the transmitter on the card is +in a non-stopped state. +*/ +static int transmit_active(struct xircom_private *card) +{ + unsigned int val; + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (7 << 20)) == 0) /* transmitter disabled */ + return 0; + + return 1; +} + +/* +receive_active returns 1 if the receiver on the card is +in a non-stopped state. +*/ +static int receive_active(struct xircom_private *card) +{ + unsigned int val; + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (7 << 17)) == 0) /* receiver disabled */ + return 0; + + return 1; +} + +/* +activate_receiver enables the receiver on the card. +Before being allowed to active the receiver, the receiver +must be completely de-activated. To achieve this, +this code actually disables the receiver first; then it waits for the +receiver to become inactive, then it activates the receiver and then +it waits for the receiver to be active. + +must be called with the lock held and interrupts disabled. +*/ +static void activate_receiver(struct xircom_private *card) +{ + unsigned int val; + int counter; + + val = inl(card->io_port + CSR6); /* Operation mode */ + + /* If the "active" bit is set and the receiver is already + active, no need to do the expensive thing */ + if ((val&2) && (receive_active(card))) + return; + + + val = val & ~2; /* disable the receiver */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + netdev_err(card->dev, "Receiver failed to deactivate\n"); + } + + /* enable the receiver */ + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val | 2; /* enable the receiver */ + outl(val, card->io_port + CSR6); + + /* now wait for the card to activate again */ + counter = 10; + while (counter > 0) { + if (receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + netdev_err(card->dev, + "Receiver failed to re-activate\n"); + } +} + +/* +deactivate_receiver disables the receiver on the card. +To achieve this this code disables the receiver first; +then it waits for the receiver to become inactive. + +must be called with the lock held and interrupts disabled. +*/ +static void deactivate_receiver(struct xircom_private *card) +{ + unsigned int val; + int counter; + + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val & ~2; /* disable the receiver */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + netdev_err(card->dev, "Receiver failed to deactivate\n"); + } +} + + +/* +activate_transmitter enables the transmitter on the card. +Before being allowed to active the transmitter, the transmitter +must be completely de-activated. To achieve this, +this code actually disables the transmitter first; then it waits for the +transmitter to become inactive, then it activates the transmitter and then +it waits for the transmitter to be active again. + +must be called with the lock held and interrupts disabled. +*/ +static void activate_transmitter(struct xircom_private *card) +{ + unsigned int val; + int counter; + + val = inl(card->io_port + CSR6); /* Operation mode */ + + /* If the "active" bit is set and the receiver is already + active, no need to do the expensive thing */ + if ((val&(1<<13)) && (transmit_active(card))) + return; + + val = val & ~(1 << 13); /* disable the transmitter */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + netdev_err(card->dev, + "Transmitter failed to deactivate\n"); + } + + /* enable the transmitter */ + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val | (1 << 13); /* enable the transmitter */ + outl(val, card->io_port + CSR6); + + /* now wait for the card to activate again */ + counter = 10; + while (counter > 0) { + if (transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + netdev_err(card->dev, + "Transmitter failed to re-activate\n"); + } +} + +/* +deactivate_transmitter disables the transmitter on the card. +To achieve this this code disables the transmitter first; +then it waits for the transmitter to become inactive. + +must be called with the lock held and interrupts disabled. +*/ +static void deactivate_transmitter(struct xircom_private *card) +{ + unsigned int val; + int counter; + + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val & ~2; /* disable the transmitter */ + outl(val, card->io_port + CSR6); + + counter = 20; + while (counter > 0) { + if (!transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + netdev_err(card->dev, + "Transmitter failed to deactivate\n"); + } +} + + +/* +enable_transmit_interrupt enables the transmit interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_transmit_interrupt(struct xircom_private *card) +{ + unsigned int val; + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val |= 1; /* enable the transmit interrupt */ + outl(val, card->io_port + CSR7); +} + + +/* +enable_receive_interrupt enables the receive interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_receive_interrupt(struct xircom_private *card) +{ + unsigned int val; + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val = val | (1 << 6); /* enable the receive interrupt */ + outl(val, card->io_port + CSR7); +} + +/* +enable_link_interrupt enables the link status change interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_link_interrupt(struct xircom_private *card) +{ + unsigned int val; + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val = val | (1 << 27); /* enable the link status chage interrupt */ + outl(val, card->io_port + CSR7); +} + + + +/* +disable_all_interrupts disables all interrupts + +must be called with the lock held and interrupts disabled. +*/ +static void disable_all_interrupts(struct xircom_private *card) +{ + unsigned int val; + + val = 0; /* disable all interrupts */ + outl(val, card->io_port + CSR7); +} + +/* +enable_common_interrupts enables several weird interrupts + +must be called with the lock held and interrupts disabled. +*/ +static void enable_common_interrupts(struct xircom_private *card) +{ + unsigned int val; + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val |= (1<<16); /* Normal Interrupt Summary */ + val |= (1<<15); /* Abnormal Interrupt Summary */ + val |= (1<<13); /* Fatal bus error */ + val |= (1<<8); /* Receive Process Stopped */ + val |= (1<<7); /* Receive Buffer Unavailable */ + val |= (1<<5); /* Transmit Underflow */ + val |= (1<<2); /* Transmit Buffer Unavailable */ + val |= (1<<1); /* Transmit Process Stopped */ + outl(val, card->io_port + CSR7); +} + +/* +enable_promisc starts promisc mode + +must be called with the lock held and interrupts disabled. +*/ +static int enable_promisc(struct xircom_private *card) +{ + unsigned int val; + + val = inl(card->io_port + CSR6); + val = val | (1 << 6); + outl(val, card->io_port + CSR6); + + return 1; +} + + + + +/* +link_status() checks the links status and will return 0 for no link, 10 for 10mbit link and 100 for.. guess what. + +Must be called in locked state with interrupts disabled +*/ +static int link_status(struct xircom_private *card) +{ + unsigned int val; + + val = inb(card->io_port + CSR12); + + if (!(val&(1<<2))) /* bit 2 is 0 for 10mbit link, 1 for not an 10mbit link */ + return 10; + if (!(val&(1<<1))) /* bit 1 is 0 for 100mbit link, 1 for not an 100mbit link */ + return 100; + + /* If we get here -> no link at all */ + + return 0; +} + + + + + +/* + read_mac_address() reads the MAC address from the NIC and stores it in the "dev" structure. + + This function will take the spinlock itself and can, as a result, not be called with the lock helt. + */ +static void read_mac_address(struct xircom_private *card) +{ + unsigned char j, tuple, link, data_id, data_count; + unsigned long flags; + int i; + + spin_lock_irqsave(&card->lock, flags); + + outl(1 << 12, card->io_port + CSR9); /* enable boot rom access */ + for (i = 0x100; i < 0x1f7; i += link + 2) { + outl(i, card->io_port + CSR10); + tuple = inl(card->io_port + CSR9) & 0xff; + outl(i + 1, card->io_port + CSR10); + link = inl(card->io_port + CSR9) & 0xff; + outl(i + 2, card->io_port + CSR10); + data_id = inl(card->io_port + CSR9) & 0xff; + outl(i + 3, card->io_port + CSR10); + data_count = inl(card->io_port + CSR9) & 0xff; + if ((tuple == 0x22) && (data_id == 0x04) && (data_count == 0x06)) { + /* + * This is it. We have the data we want. + */ + for (j = 0; j < 6; j++) { + outl(i + j + 4, card->io_port + CSR10); + card->dev->dev_addr[j] = inl(card->io_port + CSR9) & 0xff; + } + break; + } else if (link == 0) { + break; + } + } + spin_unlock_irqrestore(&card->lock, flags); + pr_debug(" %pM\n", card->dev->dev_addr); +} + + +/* + transceiver_voodoo() enables the external UTP plug thingy. + it's called voodoo as I stole this code and cannot cross-reference + it with the specification. + */ +static void transceiver_voodoo(struct xircom_private *card) +{ + unsigned long flags; + + /* disable all powermanagement */ + pci_write_config_dword(card->pdev, PCI_POWERMGMT, 0x0000); + + setup_descriptors(card); + + spin_lock_irqsave(&card->lock, flags); + + outl(0x0008, card->io_port + CSR15); + udelay(25); + outl(0xa8050000, card->io_port + CSR15); + udelay(25); + outl(0xa00f0000, card->io_port + CSR15); + udelay(25); + + spin_unlock_irqrestore(&card->lock, flags); + + netif_start_queue(card->dev); +} + + +static void xircom_up(struct xircom_private *card) +{ + unsigned long flags; + int i; + + /* disable all powermanagement */ + pci_write_config_dword(card->pdev, PCI_POWERMGMT, 0x0000); + + setup_descriptors(card); + + spin_lock_irqsave(&card->lock, flags); + + + enable_link_interrupt(card); + enable_transmit_interrupt(card); + enable_receive_interrupt(card); + enable_common_interrupts(card); + enable_promisc(card); + + /* The card can have received packets already, read them away now */ + for (i=0;idev,card,i,bufferoffsets[i]); + + + spin_unlock_irqrestore(&card->lock, flags); + trigger_receive(card); + trigger_transmit(card); + netif_start_queue(card->dev); +} + +/* Bufferoffset is in BYTES */ +static void +investigate_read_descriptor(struct net_device *dev, struct xircom_private *card, + int descnr, unsigned int bufferoffset) +{ + int status; + + status = le32_to_cpu(card->rx_buffer[4*descnr]); + + if (status > 0) { /* packet received */ + + /* TODO: discard error packets */ + + short pkt_len = ((status >> 16) & 0x7ff) - 4; + /* minus 4, we don't want the CRC */ + struct sk_buff *skb; + + if (pkt_len > 1518) { + netdev_err(dev, "Packet length %i is bogus\n", pkt_len); + pkt_len = 1518; + } + + skb = dev_alloc_skb(pkt_len + 2); + if (skb == NULL) { + dev->stats.rx_dropped++; + goto out; + } + skb_reserve(skb, 2); + skb_copy_to_linear_data(skb, + &card->rx_buffer[bufferoffset / 4], + pkt_len); + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + +out: + /* give the buffer back to the card */ + card->rx_buffer[4*descnr] = cpu_to_le32(0x80000000); + trigger_receive(card); + } +} + + +/* Bufferoffset is in BYTES */ +static void +investigate_write_descriptor(struct net_device *dev, + struct xircom_private *card, + int descnr, unsigned int bufferoffset) +{ + int status; + + status = le32_to_cpu(card->tx_buffer[4*descnr]); +#if 0 + if (status & 0x8000) { /* Major error */ + pr_err("Major transmit error status %x\n", status); + card->tx_buffer[4*descnr] = 0; + netif_wake_queue (dev); + } +#endif + if (status > 0) { /* bit 31 is 0 when done */ + if (card->tx_skb[descnr]!=NULL) { + dev->stats.tx_bytes += card->tx_skb[descnr]->len; + dev_kfree_skb_irq(card->tx_skb[descnr]); + } + card->tx_skb[descnr] = NULL; + /* Bit 8 in the status field is 1 if there was a collision */ + if (status & (1 << 8)) + dev->stats.collisions++; + card->tx_buffer[4*descnr] = 0; /* descriptor is free again */ + netif_wake_queue (dev); + dev->stats.tx_packets++; + } +} + +static int __init xircom_init(void) +{ + return pci_register_driver(&xircom_ops); +} + +static void __exit xircom_exit(void) +{ + pci_unregister_driver(&xircom_ops); +} + +module_init(xircom_init) +module_exit(xircom_exit) + -- cgit v1.2.3 From afc4b13df143122f99a0eb10bfefb216c2806de0 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 16 Aug 2011 06:29:01 +0000 Subject: net: remove use of ndo_set_multicast_list in drivers replace it by ndo_set_rx_mode Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/dec/ewrk3.c | 2 +- drivers/net/ethernet/dec/tulip/de2104x.c | 2 +- drivers/net/ethernet/dec/tulip/de4x5.c | 2 +- drivers/net/ethernet/dec/tulip/dmfe.c | 2 +- drivers/net/ethernet/dec/tulip/tulip_core.c | 2 +- drivers/net/ethernet/dec/tulip/uli526x.c | 2 +- drivers/net/ethernet/dec/tulip/winbond-840.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net/ethernet/dec') diff --git a/drivers/net/ethernet/dec/ewrk3.c b/drivers/net/ethernet/dec/ewrk3.c index 05a5f71451a7..f9df5e4d0341 100644 --- a/drivers/net/ethernet/dec/ewrk3.c +++ b/drivers/net/ethernet/dec/ewrk3.c @@ -393,7 +393,7 @@ 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_multicast_list = set_multicast_list, + .ndo_set_rx_mode = set_multicast_list, .ndo_do_ioctl = ewrk3_ioctl, .ndo_tx_timeout = ewrk3_timeout, .ndo_change_mtu = eth_change_mtu, diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index ce90efc6ba3c..1427739d9a51 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -1956,7 +1956,7 @@ bad_srom: static const struct net_device_ops de_netdev_ops = { .ndo_open = de_open, .ndo_stop = de_close, - .ndo_set_multicast_list = de_set_rx_mode, + .ndo_set_rx_mode = de_set_rx_mode, .ndo_start_xmit = de_start_xmit, .ndo_get_stats = de_get_stats, .ndo_tx_timeout = de_tx_timeout, diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index 959b41021a65..871bcaa7068d 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -1084,7 +1084,7 @@ static const struct net_device_ops de4x5_netdev_ops = { .ndo_stop = de4x5_close, .ndo_start_xmit = de4x5_queue_pkt, .ndo_get_stats = de4x5_get_stats, - .ndo_set_multicast_list = set_multicast_list, + .ndo_set_rx_mode = set_multicast_list, .ndo_do_ioctl = de4x5_ioctl, .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address= eth_mac_addr, diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c index 9a21ca3873fc..17b11ee1745a 100644 --- a/drivers/net/ethernet/dec/tulip/dmfe.c +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -356,7 +356,7 @@ static const struct net_device_ops netdev_ops = { .ndo_open = dmfe_open, .ndo_stop = dmfe_stop, .ndo_start_xmit = dmfe_start_xmit, - .ndo_set_multicast_list = dmfe_set_filter_mode, + .ndo_set_rx_mode = dmfe_set_filter_mode, .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 1246998a677c..011f67c7ca47 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -1291,7 +1291,7 @@ static const struct net_device_ops tulip_netdev_ops = { .ndo_stop = tulip_close, .ndo_get_stats = tulip_get_stats, .ndo_do_ioctl = private_ioctl, - .ndo_set_multicast_list = set_rx_mode, + .ndo_set_rx_mode = set_rx_mode, .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c index 9e63f406f72d..7a44a7a6adc8 100644 --- a/drivers/net/ethernet/dec/tulip/uli526x.c +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -259,7 +259,7 @@ static const struct net_device_ops netdev_ops = { .ndo_open = uli526x_open, .ndo_stop = uli526x_stop, .ndo_start_xmit = uli526x_start_xmit, - .ndo_set_multicast_list = uli526x_set_filter_mode, + .ndo_set_rx_mode = uli526x_set_filter_mode, .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c index 862eadf07191..4d01219ba22f 100644 --- a/drivers/net/ethernet/dec/tulip/winbond-840.c +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -350,7 +350,7 @@ static const struct net_device_ops netdev_ops = { .ndo_stop = netdev_close, .ndo_start_xmit = start_tx, .ndo_get_stats = get_stats, - .ndo_set_multicast_list = set_rx_mode, + .ndo_set_rx_mode = set_rx_mode, .ndo_do_ioctl = netdev_ioctl, .ndo_tx_timeout = tx_timeout, .ndo_change_mtu = eth_change_mtu, -- cgit v1.2.3 From 88f07484ccdf08e58dc462ed1ac7eb2e84d88a17 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Tue, 23 Aug 2011 01:29:52 -0700 Subject: drivers/net/ethernet/*: Enabled vendor Kconfig options Based on finds for Stephen Rothwell, where current defconfig's enable a ethernet driver and it is not compiled due to the newly added NET_VENDOR_* component of Kconfig. This patch enables all the "new" Kconfig options so that current defconfig's will continue to compile the expected drivers. In addition, by enabling all the new Kconfig options does not add any un-expected options. CC: Stephen Rothwll Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/dec/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/ethernet/dec') diff --git a/drivers/net/ethernet/dec/Kconfig b/drivers/net/ethernet/dec/Kconfig index 40e8df9fde8d..37940279ded8 100644 --- a/drivers/net/ethernet/dec/Kconfig +++ b/drivers/net/ethernet/dec/Kconfig @@ -4,6 +4,7 @@ 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 -- cgit v1.2.3 From aaba215ca0b4232824c92b830853f465f16a6672 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Wed, 14 Sep 2011 21:23:14 +0000 Subject: MII: fix Kconfig dependencies for MII MII Kconfig option is apart of the core networking drivers and by default NET_CORE is enabled so drivers selecting MII will have MII enabled as well. It was found using the randconfig option during testing, MII would be selected but NET_CORE could be disabled. This caused a dependency error. Resolved the dependency by selecting NET_CORE when MII is selected. Reported-by: Emil Tantilov Signed-off-by: Jeff Kirsher Tested-by: Aaron Brown Signed-off-by: David S. Miller --- drivers/net/ethernet/dec/tulip/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/ethernet/dec') diff --git a/drivers/net/ethernet/dec/tulip/Kconfig b/drivers/net/ethernet/dec/tulip/Kconfig index f6af772b12c9..1203be0436e2 100644 --- a/drivers/net/ethernet/dec/tulip/Kconfig +++ b/drivers/net/ethernet/dec/tulip/Kconfig @@ -125,6 +125,7 @@ config WINBOND_840 tristate "Winbond W89c840 Ethernet support" depends on PCI select CRC32 + select NET_CORE select MII ---help--- This driver is for the Winbond W89c840 chip. It also works with -- cgit v1.2.3 From 5d472b7377ce5f02929ae0bed53407aa795571dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20romieu?= Date: Fri, 30 Sep 2011 00:38:49 +0000 Subject: drivers/net/ethernet: remove unused #define. Signed-off-by: Francois Romieu Signed-off-by: David S. Miller --- drivers/net/ethernet/dec/tulip/de4x5.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net/ethernet/dec') diff --git a/drivers/net/ethernet/dec/tulip/de4x5.h b/drivers/net/ethernet/dec/tulip/de4x5.h index 9f2877438fb0..ec756eba397b 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.h +++ b/drivers/net/ethernet/dec/tulip/de4x5.h @@ -991,8 +991,6 @@ */ #include -#define DE4X5IOCTL SIOCDEVPRIVATE - struct de4x5_ioctl { unsigned short cmd; /* Command to run */ unsigned short len; /* Length of the data buffer */ -- cgit v1.2.3