wd.c revision d3d7b53d1ae46534cd73e1073a5c29e3b61a0552
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* wd.c: A WD80x3 ethernet driver for linux. */ 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Written 1993-94 by Donald Becker. 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Copyright 1993 United States Government as represented by the 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Director, National Security Agency. 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds This software may be used and distributed according to the terms 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds of the GNU General Public License, incorporated herein by reference. 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds The author may be reached as becker@scyld.com, or C/O 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Scyld Computing Corporation 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 410 Severn Ave., Suite 210 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Annapolis MD 21403 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds This is a driver for WD8003 and WD8013 "compatible" ethercards. 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013. 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Changelog: 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Paul Gortmaker : multiple card support for module users, support 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for non-standard memory sizes. 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/ 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char version[] = 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h> 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h> 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h> 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h> 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h> 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/system.h> 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "8390.h" 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_NAME "wd" 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* A zero-terminated list of I/O addresses to be probed. */ 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int wd_portlist[] __initdata = 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{0x300, 0x280, 0x380, 0x240, 0}; 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wd_probe1(struct net_device *dev, int ioaddr); 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wd_open(struct net_device *dev); 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wd_reset_8390(struct net_device *dev); 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ring_page); 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wd_block_input(struct net_device *dev, int count, 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct sk_buff *skb, int ring_offset); 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wd_block_output(struct net_device *dev, int count, 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds const unsigned char *buf, int start_page); 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wd_close(struct net_device *dev); 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 646aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_START_PG 0x00 /* First page of TX buffer */ 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */ 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_CMDREG 0 /* Offset to ASIC command register. */ 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */ 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_MEMENB 0x40 /* Enable the shared memory. */ 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */ 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */ 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NIC16 0x40 /* Enable 16 bit access from the 8390. */ 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */ 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_IO_EXTENT 32 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 786aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Probe for the WD8003 and WD8013. These cards have the station 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds address PROM at I/O ports <base>+8 to <base>+13, with a checksum 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds following. A Soundblaster can have the same checksum as an WDethercard, 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds so we have an extra exclusionary check for it. 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds The wd_probe1() routine initializes the card and fills the 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds station address field. */ 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init do_wd_probe(struct net_device *dev) 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct resource *r; 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int base_addr = dev->base_addr; 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int irq = dev->irq; 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int mem_start = dev->mem_start; 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int mem_end = dev->mem_end; 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (base_addr > 0x1ff) { /* Check a user specified location. */ 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds r = request_region(base_addr, WD_IO_EXTENT, "wd-probe"); 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ( r == NULL) 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EBUSY; 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i = wd_probe1(dev, base_addr); 1016aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik if (i != 0) 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds release_region(base_addr, WD_IO_EXTENT); 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds r->name = dev->name; 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return i; 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else if (base_addr != 0) /* Don't probe at all. */ 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENXIO; 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; wd_portlist[i]; i++) { 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ioaddr = wd_portlist[i]; 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe"); 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (r == NULL) 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (wd_probe1(dev, ioaddr) == 0) { 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds r->name = dev->name; 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds release_region(ioaddr, WD_IO_EXTENT); 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->irq = irq; 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_start = mem_start; 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_end = mem_end; 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef MODULE 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct net_device * __init wd_probe(int unit) 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct net_device *dev = alloc_ei_netdev(); 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int err; 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!dev) 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ERR_PTR(-ENOMEM); 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sprintf(dev->name, "eth%d", unit); 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds netdev_boot_setup_check(dev); 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = do_wd_probe(dev); 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (err) 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return dev; 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout: 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_netdev(dev); 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ERR_PTR(err); 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init wd_probe1(struct net_device *dev, int ioaddr) 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 153b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au> int err; 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int checksum = 0; 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ancient = 0; /* An old card without config registers. */ 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */ 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds const char *model_name; 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds static unsigned version_printed; 1590795af5729b18218767fab27c44b1384f72dc9adJoe Perches DECLARE_MAC_BUF(mac); 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < 8; i++) 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds checksum += inb(ioaddr + 8 + i); 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */ 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds || inb(ioaddr + 9) == 0xff 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds || (checksum & 0xff) != 0xFF) 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check for semi-valid mem_start/end values if supplied. */ 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) { 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n"); 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_start = 0; 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_end = 0; 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_debug && version_printed++ == 0) 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(version); 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < 6; i++) 1790795af5729b18218767fab27c44b1384f72dc9adJoe Perches dev->dev_addr[i] = inb(ioaddr + 8 + i); 1800795af5729b18218767fab27c44b1384f72dc9adJoe Perches 1810795af5729b18218767fab27c44b1384f72dc9adJoe Perches printk("%s: WD80x3 at %#3x, %s", 1820795af5729b18218767fab27c44b1384f72dc9adJoe Perches dev->name, ioaddr, print_mac(mac, dev->dev_addr)); 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* The following PureData probe code was contributed by 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds configuration differently from others so we have to check for them. 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card. 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char reg5 = inb(ioaddr+5); 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (inb(ioaddr+2)) { 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0x03: word16 = 0; model_name = "PDI8023-8"; break; 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0x05: word16 = 0; model_name = "PDUC8023"; break; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0x0a: word16 = 1; model_name = "PDI8023-16"; break; 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Either 0x01 (dumb) or they've released a new version. */ 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: word16 = 0; model_name = "PDI8023"; break; 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12; 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { /* End of PureData probe */ 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* This method of checking for a 16-bit board is borrowed from the 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds we.c driver. A simpler method is just to look in ASIC reg. 0x03. 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds I'm comparing the two method in alpha test to make certain they 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return the same result. */ 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check for the old 8 bit board - it has register 0/8 aliasing. 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Do NOT check i>=6 here -- it hangs the old 8003 boards! */ 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < 6; i++) 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (inb(ioaddr+i) != inb(ioaddr+8+i)) 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (i >= 6) { 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ancient = 1; 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds model_name = "WD8003-old"; 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds word16 = 0; 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */ 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int asic_reg5 = inb(ioaddr+WD_CMDREG5); 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Magic to set ASIC to word-wide mode. */ 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(tmp, ioaddr+1); 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds model_name = "WD8013"; 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds word16 = 1; /* We have a 16bit board here! */ 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds model_name = "WD8003"; 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds word16 = 0; 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(tmp, ioaddr+1); /* Restore original reg1 value. */ 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef final_version 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01)) 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8); 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined(WD_SHMEM) && WD_SHMEM > 0x80000 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Allow a compile-time override. */ 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_start = WD_SHMEM; 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (dev->mem_start == 0) { 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Sanity and old 8003 check */ 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int reg0 = inb(ioaddr); 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (reg0 == 0xff || reg0 == 0) { 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Future plan: this could check a few likely locations first. */ 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_start = 0xd0000; 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" assigning address %#lx", dev->mem_start); 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Some boards don't have the register 5 -- it returns 0xff. */ 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (high_addr_bits == 0x1f || word16 == 0) 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds high_addr_bits = 0x01; 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19); 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* The 8390 isn't at the base address -- the ASIC regs are there! */ 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->base_addr = ioaddr+WD_NIC_OFFSET; 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (dev->irq < 2) { 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int irqmap[] = {9,3,5,7,10,11,15,4}; 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int reg1 = inb(ioaddr+1); 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int reg4 = inb(ioaddr+4); 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */ 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds short nic_addr = ioaddr+WD_NIC_OFFSET; 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long irq_mask; 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We have an old-style ethercard that doesn't report its IRQ 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds line. Do autoirq to find the IRQ line. Note that this IS NOT 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds a reliable way to trigger an interrupt. */ 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb_p(E8390_NODMA + E8390_STOP, nic_addr); 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */ 2766aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds irq_mask = probe_irq_on(); 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */ 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb_p(0x00, nic_addr + EN0_RCNTLO); 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb_p(0x00, nic_addr + EN0_RCNTHI); 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */ 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mdelay(20); 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->irq = probe_irq_off(irq_mask); 2846aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */ 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_debug > 2) 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" autoirq is %d", dev->irq); 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (dev->irq < 2) 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->irq = word16 ? 10 : 5; 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */ 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->irq = 9; 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Snarf the interrupt now. There's no point in waiting since we cannot 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds share and the board will usually be enabled. */ 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev); 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (i) { 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk (" unable to get IRQ %d.\n", dev->irq); 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return i; 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* OK, were are certain this is going to work. Setup the device. */ 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.name = model_name; 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.word16 = word16; 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.tx_start_page = WD_START_PG; 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.rx_start_page = WD_START_PG + TX_PAGES; 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't map in the shared memory until the board is actually opened. */ 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (dev->mem_end != 0) { 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.priv = dev->mem_end - dev->mem_start; 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.priv = (ei_status.stop_page - WD_START_PG)*256; 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.mem = ioremap(dev->mem_start, ei_status.priv); 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ei_status.mem) { 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_irq(dev->irq, dev); 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds model_name, dev->irq, dev->mem_start, dev->mem_end-1); 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.reset_8390 = &wd_reset_8390; 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.block_input = &wd_block_input; 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.block_output = &wd_block_output; 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.get_8390_hdr = &wd_get_8390_hdr; 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->open = &wd_open; 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->stop = &wd_close; 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_NET_POLL_CONTROLLER 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->poll_controller = ei_poll; 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 340d3d7b53d1ae46534cd73e1073a5c29e3b61a0552Alan Cox NS8390_init(dev, 0); 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 1 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Enable interrupt generation on softconfig cards -- M.U */ 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* .. but possibly potentially unsafe - Donald */ 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (inb(ioaddr+14) & 0x20) 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(inb(ioaddr+4)|0x80, ioaddr+4); 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 349b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au> err = register_netdev(dev); 350b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au> if (err) 351b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au> free_irq(dev->irq, dev); 352b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au> return err; 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_open(struct net_device *dev) 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Map in the shared memory. Always set register 0 last to remain 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds compatible with very old boards. */ 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB; 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16; 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_status.word16) 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(ei_status.reg5, ioaddr+WD_CMDREG5); 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_open(dev); 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_reset_8390(struct net_device *dev) 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(WD_RESET, wd_cmd_port); 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies); 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_status.txing = 0; 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Set up the ASIC registers, just in case something changed them. */ 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_status.word16) 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5); 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_debug > 1) printk("reset done\n"); 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Grab the 8390 specific header. Similar to the block_input routine, but 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds we don't need to be concerned with ring wrap as the header will be at 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds the start of a page, so we optimize accordingly. */ 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8); 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We'll always get a 4 byte header read followed by a packet read, so 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds we enable 16 bit mode before the header, and disable after the body. */ 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_status.word16) 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __BIG_ENDIAN 4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Officially this is what we are doing, but the readl() is faster */ 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* unfortunately it isn't endian aware of the struct */ 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds hdr->count = le16_to_cpu(hdr->count); 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ((unsigned int*)hdr)[0] = readl(hdr_start); 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Block input and output are easy on shared memory ethercards, and trivial 4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds on the Western digital card where there is no choice of how to do it. 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds The only complications are that the ring buffer wraps, and need to map 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch between 8- and 16-bit modes. */ 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void 4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long offset = ring_offset - (WD_START_PG<<8); 4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem *xfer_start = ei_status.mem + offset; 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (offset + count > ei_status.priv) { 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We must wrap the input move. */ 4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int semi_count = ei_status.priv - offset; 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy_fromio(skb->data, xfer_start, semi_count); 4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds count -= semi_count; 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Packet is in one chunk -- we can copy + cksum. */ 4374ec031166f6a466a443f462e567f7551096b1741Al Viro memcpy_fromio(skb->data, xfer_start, count); 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Turn off 16 bit access so that reboot works. ISA brain-damage */ 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_status.word16) 4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_block_output(struct net_device *dev, int count, const unsigned char *buf, 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int start_page) 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8); 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_status.word16) { 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Turn on and off 16 bit access so that reboot works. */ 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy_toio(shmem, buf, count); 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy_toio(shmem, buf, count); 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_close(struct net_device *dev) 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_debug > 1) 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: Shutting down ethercard.\n", dev->name); 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ei_close(dev); 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Change from 16-bit to 8-bit shared memory so reboot works. */ 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ei_status.word16) 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* And disable the shared memory. */ 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4826aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef MODULE 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_WD_CARDS 4 /* Max number of wd cards per module */ 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device *dev_wd[MAX_WD_CARDS]; 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io[MAX_WD_CARDS]; 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq[MAX_WD_CARDS]; 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mem[MAX_WD_CARDS]; 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mem_end[MAX_WD_CARDS]; /* for non std. mem size */ 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(io, int, NULL, 0); 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(irq, int, NULL, 0); 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(mem, int, NULL, 0); 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(mem_end, int, NULL, 0); 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(io, "I/O base address(es)"); 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)"); 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)"); 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(mem_end, "memory end address(es)"); 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver"); 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This is set up so that only a single autoprobe takes place per call. 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsISA device autoprobes on a running machine are not recommended. */ 5045e5fa01d55e9949660a89b23f4a43884eaf49097Andrew Morton 5055e5fa01d55e9949660a89b23f4a43884eaf49097Andrew Mortonint __init init_module(void) 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct net_device *dev; 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int this_dev, found = 0; 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (io[this_dev] == 0) { 5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this_dev != 0) break; /* only autoprobe 1st one */ 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n"); 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev = alloc_ei_netdev(); 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!dev) 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->irq = irq[this_dev]; 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->base_addr = io[this_dev]; 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_start = mem[this_dev]; 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mem_end = mem_end[this_dev]; 5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (do_wd_probe(dev) == 0) { 523b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au> dev_wd[found++] = dev; 524b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au> continue; 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_netdev(dev); 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (found) 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENXIO; 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 53564916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenkostatic void cleanup_card(struct net_device *dev) 53664916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko{ 53764916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko free_irq(dev->irq, dev); 53864916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT); 53964916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko iounmap(ei_status.mem); 54064916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko} 54164916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko 542afc8eb46c0ea2cab8bc28713b2e0614f015a7516Al Virovoid __exit 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldscleanup_module(void) 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int this_dev; 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct net_device *dev = dev_wd[this_dev]; 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (dev) { 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unregister_netdev(dev); 5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cleanup_card(dev); 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_netdev(dev); 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* MODULE */ 557