wd.c revision bdb0f8672ff6f601a32df5af40f11526b741985c
1470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* wd.c: A WD80x3 ethernet driver for linux. */ 25886269962f94fa9185c32db3ec936c612503235Uwe Kleine-König/* 3470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Written 1993-94 by Donald Becker. 4470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 5470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Copyright 1993 United States Government as represented by the 6470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Director, National Security Agency. 7470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 8470decc613ab2048b619a01028072d932d9086eeDave Kleikamp This software may be used and distributed according to the terms 9470decc613ab2048b619a01028072d932d9086eeDave Kleikamp of the GNU General Public License, incorporated herein by reference. 10470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 11470decc613ab2048b619a01028072d932d9086eeDave Kleikamp The author may be reached as becker@scyld.com, or C/O 12470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Scyld Computing Corporation 13470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 410 Severn Ave., Suite 210 14470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Annapolis MD 21403 15470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 16470decc613ab2048b619a01028072d932d9086eeDave Kleikamp This is a driver for WD8003 and WD8013 "compatible" ethercards. 17470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 18470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013. 19470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 20470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Changelog: 21470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 22f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao Paul Gortmaker : multiple card support for module users, support 23470decc613ab2048b619a01028072d932d9086eeDave Kleikamp for non-standard memory sizes. 24470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 25470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 26470decc613ab2048b619a01028072d932d9086eeDave Kleikamp*/ 27470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 28470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic const char version[] = 29470decc613ab2048b619a01028072d932d9086eeDave Kleikamp "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; 30470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 31470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/module.h> 32470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/kernel.h> 33470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/errno.h> 34470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/string.h> 35470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/init.h> 36470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/interrupt.h> 37470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/delay.h> 38470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/netdevice.h> 39470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/etherdevice.h> 40470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 41470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <asm/io.h> 42470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <asm/system.h> 43470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 44470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include "8390.h" 45470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 46470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define DRV_NAME "wd" 47470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 48470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* A zero-terminated list of I/O addresses to be probed. */ 49470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic unsigned int wd_portlist[] __initdata = 50470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{0x300, 0x280, 0x380, 0x240, 0}; 51470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 52470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int wd_probe1(struct net_device *dev, int ioaddr); 53470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 54470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int wd_open(struct net_device *dev); 55470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void wd_reset_8390(struct net_device *dev); 56470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, 57470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int ring_page); 58470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void wd_block_input(struct net_device *dev, int count, 59470decc613ab2048b619a01028072d932d9086eeDave Kleikamp struct sk_buff *skb, int ring_offset); 60470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void wd_block_output(struct net_device *dev, int count, 61470decc613ab2048b619a01028072d932d9086eeDave Kleikamp const unsigned char *buf, int start_page); 62470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int wd_close(struct net_device *dev); 63470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 64470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 65470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_START_PG 0x00 /* First page of TX buffer */ 66470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ 67470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */ 68470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 69470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_CMDREG 0 /* Offset to ASIC command register. */ 70470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */ 71470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_MEMENB 0x40 /* Enable the shared memory. */ 72470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */ 73470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */ 74470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define NIC16 0x40 /* Enable 16 bit access from the 8390. */ 75470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */ 76470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_IO_EXTENT 32 77470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 78470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 79470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* Probe for the WD8003 and WD8013. These cards have the station 80470decc613ab2048b619a01028072d932d9086eeDave Kleikamp address PROM at I/O ports <base>+8 to <base>+13, with a checksum 81470decc613ab2048b619a01028072d932d9086eeDave Kleikamp following. A Soundblaster can have the same checksum as an WDethercard, 82470decc613ab2048b619a01028072d932d9086eeDave Kleikamp so we have an extra exclusionary check for it. 83470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 84470decc613ab2048b619a01028072d932d9086eeDave Kleikamp The wd_probe1() routine initializes the card and fills the 85470decc613ab2048b619a01028072d932d9086eeDave Kleikamp station address field. */ 86470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 87470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int __init do_wd_probe(struct net_device *dev) 88470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 89470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int i; 90470decc613ab2048b619a01028072d932d9086eeDave Kleikamp struct resource *r; 91470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int base_addr = dev->base_addr; 92470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int irq = dev->irq; 93470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int mem_start = dev->mem_start; 94470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int mem_end = dev->mem_end; 95470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 96470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (base_addr > 0x1ff) { /* Check a user specified location. */ 97470decc613ab2048b619a01028072d932d9086eeDave Kleikamp r = request_region(base_addr, WD_IO_EXTENT, "wd-probe"); 98f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao if ( r == NULL) 99470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return -EBUSY; 100f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao i = wd_probe1(dev, base_addr); 101470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (i != 0) 102470decc613ab2048b619a01028072d932d9086eeDave Kleikamp release_region(base_addr, WD_IO_EXTENT); 103470decc613ab2048b619a01028072d932d9086eeDave Kleikamp else 104470decc613ab2048b619a01028072d932d9086eeDave Kleikamp r->name = dev->name; 105470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return i; 106470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 107470decc613ab2048b619a01028072d932d9086eeDave Kleikamp else if (base_addr != 0) /* Don't probe at all. */ 108470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return -ENXIO; 109470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 110f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao for (i = 0; wd_portlist[i]; i++) { 111470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int ioaddr = wd_portlist[i]; 112470decc613ab2048b619a01028072d932d9086eeDave Kleikamp r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe"); 113470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (r == NULL) 114470decc613ab2048b619a01028072d932d9086eeDave Kleikamp continue; 115f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao if (wd_probe1(dev, ioaddr) == 0) { 116470decc613ab2048b619a01028072d932d9086eeDave Kleikamp r->name = dev->name; 117470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return 0; 118470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 119470decc613ab2048b619a01028072d932d9086eeDave Kleikamp release_region(ioaddr, WD_IO_EXTENT); 120470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->irq = irq; 121f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao dev->mem_start = mem_start; 122f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao dev->mem_end = mem_end; 123470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 124470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 125470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return -ENODEV; 126470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 127470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 128470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifndef MODULE 129470decc613ab2048b619a01028072d932d9086eeDave Kleikampstruct net_device * __init wd_probe(int unit) 130470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 131470decc613ab2048b619a01028072d932d9086eeDave Kleikamp struct net_device *dev = alloc_ei_netdev(); 132470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int err; 133f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao 134470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (!dev) 135f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao return ERR_PTR(-ENOMEM); 136470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 137470decc613ab2048b619a01028072d932d9086eeDave Kleikamp sprintf(dev->name, "eth%d", unit); 138470decc613ab2048b619a01028072d932d9086eeDave Kleikamp netdev_boot_setup_check(dev); 139470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 140470decc613ab2048b619a01028072d932d9086eeDave Kleikamp err = do_wd_probe(dev); 141470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (err) 142470decc613ab2048b619a01028072d932d9086eeDave Kleikamp goto out; 143470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return dev; 144470decc613ab2048b619a01028072d932d9086eeDave Kleikampout: 145470decc613ab2048b619a01028072d932d9086eeDave Kleikamp free_netdev(dev); 146470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return ERR_PTR(err); 147470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 148470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif 149470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 150470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic const struct net_device_ops wd_netdev_ops = { 151470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_open = wd_open, 152470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_stop = wd_close, 153470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_start_xmit = ei_start_xmit, 154470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_tx_timeout = ei_tx_timeout, 155470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_get_stats = ei_get_stats, 156470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_set_multicast_list = ei_set_multicast_list, 157470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_validate_addr = eth_validate_addr, 158470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_set_mac_address = eth_mac_addr, 159470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_change_mtu = eth_change_mtu, 160470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifdef CONFIG_NET_POLL_CONTROLLER 161470decc613ab2048b619a01028072d932d9086eeDave Kleikamp .ndo_poll_controller = ei_poll, 162470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif 163470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}; 164470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 165470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int __init wd_probe1(struct net_device *dev, int ioaddr) 166470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 167470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int i; 168470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int err; 169470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int checksum = 0; 170470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int ancient = 0; /* An old card without config registers. */ 171470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */ 172470decc613ab2048b619a01028072d932d9086eeDave Kleikamp const char *model_name; 173470decc613ab2048b619a01028072d932d9086eeDave Kleikamp static unsigned version_printed; 174470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 175470decc613ab2048b619a01028072d932d9086eeDave Kleikamp for (i = 0; i < 8; i++) 176470decc613ab2048b619a01028072d932d9086eeDave Kleikamp checksum += inb(ioaddr + 8 + i); 177470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */ 178470decc613ab2048b619a01028072d932d9086eeDave Kleikamp || inb(ioaddr + 9) == 0xff 179470decc613ab2048b619a01028072d932d9086eeDave Kleikamp || (checksum & 0xff) != 0xFF) 180470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return -ENODEV; 181470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 182470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Check for semi-valid mem_start/end values if supplied. */ 183470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) { 184470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n"); 185470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->mem_start = 0; 186470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->mem_end = 0; 187470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 188470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 189470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_debug && version_printed++ == 0) 190470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk(version); 191470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 192470decc613ab2048b619a01028072d932d9086eeDave Kleikamp for (i = 0; i < 6; i++) 193470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->dev_addr[i] = inb(ioaddr + 8 + i); 194470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 195470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk("%s: WD80x3 at %#3x, %pM", 196470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->name, ioaddr, dev->dev_addr); 197470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 198470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* The following PureData probe code was contributed by 199470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software 200470decc613ab2048b619a01028072d932d9086eeDave Kleikamp configuration differently from others so we have to check for them. 201f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card. 202470decc613ab2048b619a01028072d932d9086eeDave Kleikamp */ 203f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { 204470decc613ab2048b619a01028072d932d9086eeDave Kleikamp unsigned char reg5 = inb(ioaddr+5); 205470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 206470decc613ab2048b619a01028072d932d9086eeDave Kleikamp switch (inb(ioaddr+2)) { 207470decc613ab2048b619a01028072d932d9086eeDave Kleikamp case 0x03: word16 = 0; model_name = "PDI8023-8"; break; 208470decc613ab2048b619a01028072d932d9086eeDave Kleikamp case 0x05: word16 = 0; model_name = "PDUC8023"; break; 209470decc613ab2048b619a01028072d932d9086eeDave Kleikamp case 0x0a: word16 = 1; model_name = "PDI8023-16"; break; 210470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Either 0x01 (dumb) or they've released a new version. */ 211470decc613ab2048b619a01028072d932d9086eeDave Kleikamp default: word16 = 0; model_name = "PDI8023"; break; 212470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 213470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12; 214470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; 215470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } else { /* End of PureData probe */ 216470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* This method of checking for a 16-bit board is borrowed from the 217470decc613ab2048b619a01028072d932d9086eeDave Kleikamp we.c driver. A simpler method is just to look in ASIC reg. 0x03. 218470decc613ab2048b619a01028072d932d9086eeDave Kleikamp I'm comparing the two method in alpha test to make certain they 219470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return the same result. */ 220470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Check for the old 8 bit board - it has register 0/8 aliasing. 221470decc613ab2048b619a01028072d932d9086eeDave Kleikamp Do NOT check i>=6 here -- it hangs the old 8003 boards! */ 222470decc613ab2048b619a01028072d932d9086eeDave Kleikamp for (i = 0; i < 6; i++) 223470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (inb(ioaddr+i) != inb(ioaddr+8+i)) 224470decc613ab2048b619a01028072d932d9086eeDave Kleikamp break; 225470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (i >= 6) { 226470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ancient = 1; 227470decc613ab2048b619a01028072d932d9086eeDave Kleikamp model_name = "WD8003-old"; 228470decc613ab2048b619a01028072d932d9086eeDave Kleikamp word16 = 0; 229470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } else { 230470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ 231470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ 232470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ 233470decc613ab2048b619a01028072d932d9086eeDave Kleikamp && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */ 234470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int asic_reg5 = inb(ioaddr+WD_CMDREG5); 235470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Magic to set ASIC to word-wide mode. */ 236470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); 237470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(tmp, ioaddr+1); 238470decc613ab2048b619a01028072d932d9086eeDave Kleikamp model_name = "WD8013"; 239470decc613ab2048b619a01028072d932d9086eeDave Kleikamp word16 = 1; /* We have a 16bit board here! */ 240470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } else { 241470decc613ab2048b619a01028072d932d9086eeDave Kleikamp model_name = "WD8003"; 242470decc613ab2048b619a01028072d932d9086eeDave Kleikamp word16 = 0; 243470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 244470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(tmp, ioaddr+1); /* Restore original reg1 value. */ 245470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 246470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifndef final_version 247470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01)) 248470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", 249470decc613ab2048b619a01028072d932d9086eeDave Kleikamp word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8); 250470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif 251470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 252470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 253470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#if defined(WD_SHMEM) && WD_SHMEM > 0x80000 254470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Allow a compile-time override. */ 255f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao dev->mem_start = WD_SHMEM; 256f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao#else 257470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (dev->mem_start == 0) { 258470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Sanity and old 8003 check */ 259470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int reg0 = inb(ioaddr); 260470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (reg0 == 0xff || reg0 == 0) { 261f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao /* Future plan: this could check a few likely locations first. */ 262470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->mem_start = 0xd0000; 263470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk(" assigning address %#lx", dev->mem_start); 264f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao } else { 265470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; 266470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Some boards don't have the register 5 -- it returns 0xff. */ 267470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (high_addr_bits == 0x1f || word16 == 0) 268470decc613ab2048b619a01028072d932d9086eeDave Kleikamp high_addr_bits = 0x01; 269470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19); 270470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 271470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 272470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif 273470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 274470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* The 8390 isn't at the base address -- the ASIC regs are there! */ 275470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->base_addr = ioaddr+WD_NIC_OFFSET; 276470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 277470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (dev->irq < 2) { 278470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int irqmap[] = {9,3,5,7,10,11,15,4}; 279470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int reg1 = inb(ioaddr+1); 280470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int reg4 = inb(ioaddr+4); 281470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */ 282470decc613ab2048b619a01028072d932d9086eeDave Kleikamp short nic_addr = ioaddr+WD_NIC_OFFSET; 283470decc613ab2048b619a01028072d932d9086eeDave Kleikamp unsigned long irq_mask; 284470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 285470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* We have an old-style ethercard that doesn't report its IRQ 286470decc613ab2048b619a01028072d932d9086eeDave Kleikamp line. Do autoirq to find the IRQ line. Note that this IS NOT 287470decc613ab2048b619a01028072d932d9086eeDave Kleikamp a reliable way to trigger an interrupt. */ 288470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb_p(E8390_NODMA + E8390_STOP, nic_addr); 289470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */ 290470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 291470decc613ab2048b619a01028072d932d9086eeDave Kleikamp irq_mask = probe_irq_on(); 292470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */ 293470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb_p(0x00, nic_addr + EN0_RCNTLO); 294470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb_p(0x00, nic_addr + EN0_RCNTHI); 295470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */ 296470decc613ab2048b619a01028072d932d9086eeDave Kleikamp mdelay(20); 297470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->irq = probe_irq_off(irq_mask); 298470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 299f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */ 300470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 301470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_debug > 2) 302470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk(" autoirq is %d", dev->irq); 303470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (dev->irq < 2) 304470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->irq = word16 ? 10 : 5; 305470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } else 306470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; 307470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */ 308470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->irq = 9; 309470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 310470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Snarf the interrupt now. There's no point in waiting since we cannot 311470decc613ab2048b619a01028072d932d9086eeDave Kleikamp share and the board will usually be enabled. */ 312f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev); 313470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (i) { 314470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk (" unable to get IRQ %d.\n", dev->irq); 315470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return i; 316470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 317470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 318470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* OK, were are certain this is going to work. Setup the device. */ 319470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.name = model_name; 320470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.word16 = word16; 321470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.tx_start_page = WD_START_PG; 322470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.rx_start_page = WD_START_PG + TX_PAGES; 323470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 324470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Don't map in the shared memory until the board is actually opened. */ 325470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 326470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ 327470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (dev->mem_end != 0) { 328470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; 329470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.priv = dev->mem_end - dev->mem_start; 330470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } else { 331470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; 332470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; 333470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.priv = (ei_status.stop_page - WD_START_PG)*256; 334470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 335470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 336470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.mem = ioremap(dev->mem_start, ei_status.priv); 337470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (!ei_status.mem) { 338470decc613ab2048b619a01028072d932d9086eeDave Kleikamp free_irq(dev->irq, dev); 339470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return -ENOMEM; 340470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 341470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 342470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", 343470decc613ab2048b619a01028072d932d9086eeDave Kleikamp model_name, dev->irq, dev->mem_start, dev->mem_end-1); 344470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 345470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.reset_8390 = &wd_reset_8390; 346470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.block_input = &wd_block_input; 347470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.block_output = &wd_block_output; 348470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.get_8390_hdr = &wd_get_8390_hdr; 349470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 350470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->netdev_ops = &wd_netdev_ops; 351470decc613ab2048b619a01028072d932d9086eeDave Kleikamp NS8390_init(dev, 0); 352470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 353470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#if 1 354470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Enable interrupt generation on softconfig cards -- M.U */ 355470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* .. but possibly potentially unsafe - Donald */ 356470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (inb(ioaddr+14) & 0x20) 357470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(inb(ioaddr+4)|0x80, ioaddr+4); 358470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif 359470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 360470decc613ab2048b619a01028072d932d9086eeDave Kleikamp err = register_netdev(dev); 361470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (err) { 362470decc613ab2048b619a01028072d932d9086eeDave Kleikamp free_irq(dev->irq, dev); 363470decc613ab2048b619a01028072d932d9086eeDave Kleikamp iounmap(ei_status.mem); 364470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 365470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return err; 366470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 367470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 368470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int 369470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_open(struct net_device *dev) 370470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 371470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 372470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 373470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Map in the shared memory. Always set register 0 last to remain 374470decc613ab2048b619a01028072d932d9086eeDave Kleikamp compatible with very old boards. */ 375470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB; 376470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16; 377f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao 378470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_status.word16) 379470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(ei_status.reg5, ioaddr+WD_CMDREG5); 380470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ 381470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 382470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return ei_open(dev); 383470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 384470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 385470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void 386470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_reset_8390(struct net_device *dev) 387470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 388470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 389470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 390470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(WD_RESET, wd_cmd_port); 391470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies); 392470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_status.txing = 0; 393470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 394470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Set up the ASIC registers, just in case something changed them. */ 395470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); 396470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_status.word16) 397470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5); 398470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 399470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_debug > 1) printk("reset done\n"); 400f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao} 401470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 402470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* Grab the 8390 specific header. Similar to the block_input routine, but 403470decc613ab2048b619a01028072d932d9086eeDave Kleikamp we don't need to be concerned with ring wrap as the header will be at 404470decc613ab2048b619a01028072d932d9086eeDave Kleikamp the start of a page, so we optimize accordingly. */ 405470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 406470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void 407470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) 408470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 409470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 410470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 411470decc613ab2048b619a01028072d932d9086eeDave Kleikamp void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8); 412470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 413470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* We'll always get a 4 byte header read followed by a packet read, so 414470decc613ab2048b619a01028072d932d9086eeDave Kleikamp we enable 16 bit mode before the header, and disable after the body. */ 415470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_status.word16) 416470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); 417470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 418470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifdef __BIG_ENDIAN 419470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Officially this is what we are doing, but the readl() is faster */ 420470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* unfortunately it isn't endian aware of the struct */ 421470decc613ab2048b619a01028072d932d9086eeDave Kleikamp memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); 422470decc613ab2048b619a01028072d932d9086eeDave Kleikamp hdr->count = le16_to_cpu(hdr->count); 423470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#else 424470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ((unsigned int*)hdr)[0] = readl(hdr_start); 425470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif 426470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 427470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 428470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* Block input and output are easy on shared memory ethercards, and trivial 429470decc613ab2048b619a01028072d932d9086eeDave Kleikamp on the Western digital card where there is no choice of how to do it. 430470decc613ab2048b619a01028072d932d9086eeDave Kleikamp The only complications are that the ring buffer wraps, and need to map 431470decc613ab2048b619a01028072d932d9086eeDave Kleikamp switch between 8- and 16-bit modes. */ 432470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 433470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void 434470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) 435470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 436470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 437470decc613ab2048b619a01028072d932d9086eeDave Kleikamp unsigned long offset = ring_offset - (WD_START_PG<<8); 438470decc613ab2048b619a01028072d932d9086eeDave Kleikamp void __iomem *xfer_start = ei_status.mem + offset; 439470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 440470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (offset + count > ei_status.priv) { 441470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* We must wrap the input move. */ 442470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int semi_count = ei_status.priv - offset; 443470decc613ab2048b619a01028072d932d9086eeDave Kleikamp memcpy_fromio(skb->data, xfer_start, semi_count); 444470decc613ab2048b619a01028072d932d9086eeDave Kleikamp count -= semi_count; 445470decc613ab2048b619a01028072d932d9086eeDave Kleikamp memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); 446470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } else { 447470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Packet is in one chunk -- we can copy + cksum. */ 448470decc613ab2048b619a01028072d932d9086eeDave Kleikamp memcpy_fromio(skb->data, xfer_start, count); 449470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 450470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 451470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Turn off 16 bit access so that reboot works. ISA brain-damage */ 452470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_status.word16) 453470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); 454470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 455f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao 456f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Caostatic void 457470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_block_output(struct net_device *dev, int count, const unsigned char *buf, 458470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int start_page) 459470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 460470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 461470decc613ab2048b619a01028072d932d9086eeDave Kleikamp void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8); 462470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 463470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 464470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_status.word16) { 465470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Turn on and off 16 bit access so that reboot works. */ 466470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); 467470decc613ab2048b619a01028072d932d9086eeDave Kleikamp memcpy_toio(shmem, buf, count); 468470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); 469470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } else 470470decc613ab2048b619a01028072d932d9086eeDave Kleikamp memcpy_toio(shmem, buf, count); 471470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 472470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 473470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 474470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int 475470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_close(struct net_device *dev) 476470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 477470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 478470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 479470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_debug > 1) 480470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk("%s: Shutting down ethercard.\n", dev->name); 481470decc613ab2048b619a01028072d932d9086eeDave Kleikamp ei_close(dev); 482470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 483470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* Change from 16-bit to 8-bit shared memory so reboot works. */ 484470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (ei_status.word16) 485470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); 486470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 487470decc613ab2048b619a01028072d932d9086eeDave Kleikamp /* And disable the shared memory. */ 488470decc613ab2048b619a01028072d932d9086eeDave Kleikamp outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); 489470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 490470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return 0; 491470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 492470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 493470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 494470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifdef MODULE 495470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define MAX_WD_CARDS 4 /* Max number of wd cards per module */ 496470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic struct net_device *dev_wd[MAX_WD_CARDS]; 497470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int io[MAX_WD_CARDS]; 498470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int irq[MAX_WD_CARDS]; 499470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int mem[MAX_WD_CARDS]; 500470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int mem_end[MAX_WD_CARDS]; /* for non std. mem size */ 501470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 502470decc613ab2048b619a01028072d932d9086eeDave Kleikampmodule_param_array(io, int, NULL, 0); 503470decc613ab2048b619a01028072d932d9086eeDave Kleikampmodule_param_array(irq, int, NULL, 0); 504470decc613ab2048b619a01028072d932d9086eeDave Kleikampmodule_param_array(mem, int, NULL, 0); 505470decc613ab2048b619a01028072d932d9086eeDave Kleikampmodule_param_array(mem_end, int, NULL, 0); 506470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_PARM_DESC(io, "I/O base address(es)"); 507470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)"); 508470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)"); 509470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_PARM_DESC(mem_end, "memory end address(es)"); 510470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver"); 511470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_LICENSE("GPL"); 512470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 513470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* This is set up so that only a single autoprobe takes place per call. 514470decc613ab2048b619a01028072d932d9086eeDave KleikampISA device autoprobes on a running machine are not recommended. */ 515470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 516470decc613ab2048b619a01028072d932d9086eeDave Kleikampint __init init_module(void) 517470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 518470decc613ab2048b619a01028072d932d9086eeDave Kleikamp struct net_device *dev; 519470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int this_dev, found = 0; 520470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 521f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { 522470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (io[this_dev] == 0) { 523470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (this_dev != 0) break; /* only autoprobe 1st one */ 524470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n"); 525470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 526470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev = alloc_ei_netdev(); 527470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (!dev) 528470decc613ab2048b619a01028072d932d9086eeDave Kleikamp break; 529470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->irq = irq[this_dev]; 530470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->base_addr = io[this_dev]; 531470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->mem_start = mem[this_dev]; 532470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev->mem_end = mem_end[this_dev]; 533470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (do_wd_probe(dev) == 0) { 534470decc613ab2048b619a01028072d932d9086eeDave Kleikamp dev_wd[found++] = dev; 535470decc613ab2048b619a01028072d932d9086eeDave Kleikamp continue; 536470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 537470decc613ab2048b619a01028072d932d9086eeDave Kleikamp free_netdev(dev); 538470decc613ab2048b619a01028072d932d9086eeDave Kleikamp printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); 539470decc613ab2048b619a01028072d932d9086eeDave Kleikamp break; 540470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 541470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (found) 542470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return 0; 543470decc613ab2048b619a01028072d932d9086eeDave Kleikamp return -ENXIO; 544470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 545470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 546470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void cleanup_card(struct net_device *dev) 547470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 548470decc613ab2048b619a01028072d932d9086eeDave Kleikamp free_irq(dev->irq, dev); 549470decc613ab2048b619a01028072d932d9086eeDave Kleikamp release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT); 550470decc613ab2048b619a01028072d932d9086eeDave Kleikamp iounmap(ei_status.mem); 551470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 552470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 553470decc613ab2048b619a01028072d932d9086eeDave Kleikampvoid __exit 554470decc613ab2048b619a01028072d932d9086eeDave Kleikampcleanup_module(void) 555470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{ 556470decc613ab2048b619a01028072d932d9086eeDave Kleikamp int this_dev; 557470decc613ab2048b619a01028072d932d9086eeDave Kleikamp 558470decc613ab2048b619a01028072d932d9086eeDave Kleikamp for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { 559470decc613ab2048b619a01028072d932d9086eeDave Kleikamp struct net_device *dev = dev_wd[this_dev]; 560470decc613ab2048b619a01028072d932d9086eeDave Kleikamp if (dev) { 561470decc613ab2048b619a01028072d932d9086eeDave Kleikamp unregister_netdev(dev); 562470decc613ab2048b619a01028072d932d9086eeDave Kleikamp cleanup_card(dev); 563470decc613ab2048b619a01028072d932d9086eeDave Kleikamp free_netdev(dev); 564470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 565470decc613ab2048b619a01028072d932d9086eeDave Kleikamp } 566470decc613ab2048b619a01028072d932d9086eeDave Kleikamp} 567470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif /* MODULE */ 568470decc613ab2048b619a01028072d932d9086eeDave Kleikamp