10a3360e1SGeert Uytterhoeven /* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */ 20a3360e1SGeert Uytterhoeven /* 30a3360e1SGeert Uytterhoeven Written 1996 by Russell Nelson, with reference to skeleton.c 40a3360e1SGeert Uytterhoeven written 1993-1994 by Donald Becker. 50a3360e1SGeert Uytterhoeven 60a3360e1SGeert Uytterhoeven This software may be used and distributed according to the terms 70a3360e1SGeert Uytterhoeven of the GNU General Public License, incorporated herein by reference. 80a3360e1SGeert Uytterhoeven 90a3360e1SGeert Uytterhoeven The author may be reached at nelson@crynwr.com, Crynwr 100a3360e1SGeert Uytterhoeven Software, 11 Grant St., Potsdam, NY 13676 110a3360e1SGeert Uytterhoeven 120a3360e1SGeert Uytterhoeven Changelog: 130a3360e1SGeert Uytterhoeven 140a3360e1SGeert Uytterhoeven Mike Cruse : mcruse@cti-ltd.com 150a3360e1SGeert Uytterhoeven : Changes for Linux 2.0 compatibility. 160a3360e1SGeert Uytterhoeven : Added dev_id parameter in net_interrupt(), 170a3360e1SGeert Uytterhoeven : request_irq() and free_irq(). Just NULL for now. 180a3360e1SGeert Uytterhoeven 190a3360e1SGeert Uytterhoeven Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros 200a3360e1SGeert Uytterhoeven : in net_open() and net_close() so kerneld would know 210a3360e1SGeert Uytterhoeven : that the module is in use and wouldn't eject the 220a3360e1SGeert Uytterhoeven : driver prematurely. 230a3360e1SGeert Uytterhoeven 240a3360e1SGeert Uytterhoeven Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c 250a3360e1SGeert Uytterhoeven : as an example. Disabled autoprobing in init_module(), 260a3360e1SGeert Uytterhoeven : not a good thing to do to other devices while Linux 270a3360e1SGeert Uytterhoeven : is running from all accounts. 280a3360e1SGeert Uytterhoeven 290a3360e1SGeert Uytterhoeven Alan Cox : Removed 1.2 support, added 2.1 extra counters. 300a3360e1SGeert Uytterhoeven 310a3360e1SGeert Uytterhoeven David Huggins-Daines <dhd@debian.org> 320a3360e1SGeert Uytterhoeven 330a3360e1SGeert Uytterhoeven Split this off into mac89x0.c, and gutted it of all parts which are 340a3360e1SGeert Uytterhoeven not relevant to the existing CS8900 cards on the Macintosh 350a3360e1SGeert Uytterhoeven (i.e. basically the Daynaport CS and LC cards). To be precise: 360a3360e1SGeert Uytterhoeven 370a3360e1SGeert Uytterhoeven * Removed all the media-detection stuff, because these cards are 380a3360e1SGeert Uytterhoeven TP-only. 390a3360e1SGeert Uytterhoeven 400a3360e1SGeert Uytterhoeven * Lobotomized the ISA interrupt bogosity, because these cards use 410a3360e1SGeert Uytterhoeven a hardwired NuBus interrupt and a magic ISAIRQ value in the card. 420a3360e1SGeert Uytterhoeven 430a3360e1SGeert Uytterhoeven * Basically eliminated everything not relevant to getting the 440a3360e1SGeert Uytterhoeven cards minimally functioning on the Macintosh. 450a3360e1SGeert Uytterhoeven 460a3360e1SGeert Uytterhoeven I might add that these cards are badly designed even from the Mac 470a3360e1SGeert Uytterhoeven standpoint, in that Dayna, in their infinite wisdom, used NuBus slot 480a3360e1SGeert Uytterhoeven I/O space and NuBus interrupts for these cards, but neglected to 490a3360e1SGeert Uytterhoeven provide anything even remotely resembling a NuBus ROM. Therefore we 500a3360e1SGeert Uytterhoeven have to probe for them in a brain-damaged ISA-like fashion. 510a3360e1SGeert Uytterhoeven 520a3360e1SGeert Uytterhoeven Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001 530a3360e1SGeert Uytterhoeven check kmalloc and release the allocated memory on failure in 540a3360e1SGeert Uytterhoeven mac89x0_probe and in init_module 550a3360e1SGeert Uytterhoeven use local_irq_{save,restore}(flags) in net_get_stat, not just 560a3360e1SGeert Uytterhoeven local_irq_{dis,en}able() 570a3360e1SGeert Uytterhoeven */ 580a3360e1SGeert Uytterhoeven 590a3360e1SGeert Uytterhoeven static char *version = 600a3360e1SGeert Uytterhoeven "cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n"; 610a3360e1SGeert Uytterhoeven 620a3360e1SGeert Uytterhoeven /* ======================= configure the driver here ======================= */ 630a3360e1SGeert Uytterhoeven 640a3360e1SGeert Uytterhoeven /* use 0 for production, 1 for verification, >2 for debug */ 650a3360e1SGeert Uytterhoeven #ifndef NET_DEBUG 660a3360e1SGeert Uytterhoeven #define NET_DEBUG 0 670a3360e1SGeert Uytterhoeven #endif 680a3360e1SGeert Uytterhoeven 690a3360e1SGeert Uytterhoeven /* ======================= end of configuration ======================= */ 700a3360e1SGeert Uytterhoeven 710a3360e1SGeert Uytterhoeven 720a3360e1SGeert Uytterhoeven /* Always include 'config.h' first in case the user wants to turn on 730a3360e1SGeert Uytterhoeven or override something. */ 740a3360e1SGeert Uytterhoeven #include <linux/module.h> 750a3360e1SGeert Uytterhoeven 760a3360e1SGeert Uytterhoeven /* 770a3360e1SGeert Uytterhoeven Sources: 780a3360e1SGeert Uytterhoeven 790a3360e1SGeert Uytterhoeven Crynwr packet driver epktisa. 800a3360e1SGeert Uytterhoeven 810a3360e1SGeert Uytterhoeven Crystal Semiconductor data sheets. 820a3360e1SGeert Uytterhoeven 830a3360e1SGeert Uytterhoeven */ 840a3360e1SGeert Uytterhoeven 850a3360e1SGeert Uytterhoeven #include <linux/kernel.h> 860a3360e1SGeert Uytterhoeven #include <linux/types.h> 870a3360e1SGeert Uytterhoeven #include <linux/fcntl.h> 880a3360e1SGeert Uytterhoeven #include <linux/interrupt.h> 890a3360e1SGeert Uytterhoeven #include <linux/ioport.h> 900a3360e1SGeert Uytterhoeven #include <linux/in.h> 910a3360e1SGeert Uytterhoeven #include <linux/string.h> 920a3360e1SGeert Uytterhoeven #include <linux/nubus.h> 930a3360e1SGeert Uytterhoeven #include <linux/errno.h> 940a3360e1SGeert Uytterhoeven #include <linux/init.h> 950a3360e1SGeert Uytterhoeven #include <linux/netdevice.h> 960a3360e1SGeert Uytterhoeven #include <linux/etherdevice.h> 970a3360e1SGeert Uytterhoeven #include <linux/skbuff.h> 980a3360e1SGeert Uytterhoeven #include <linux/delay.h> 990a3360e1SGeert Uytterhoeven #include <linux/bitops.h> 1000a3360e1SGeert Uytterhoeven #include <linux/gfp.h> 1010a3360e1SGeert Uytterhoeven 1020a3360e1SGeert Uytterhoeven #include <asm/system.h> 1030a3360e1SGeert Uytterhoeven #include <asm/io.h> 1040a3360e1SGeert Uytterhoeven #include <asm/hwtest.h> 1050a3360e1SGeert Uytterhoeven #include <asm/macints.h> 1060a3360e1SGeert Uytterhoeven 1070a3360e1SGeert Uytterhoeven #include "cs89x0.h" 1080a3360e1SGeert Uytterhoeven 1090a3360e1SGeert Uytterhoeven static unsigned int net_debug = NET_DEBUG; 1100a3360e1SGeert Uytterhoeven 1110a3360e1SGeert Uytterhoeven /* Information that need to be kept for each board. */ 1120a3360e1SGeert Uytterhoeven struct net_local { 1130a3360e1SGeert Uytterhoeven int chip_type; /* one of: CS8900, CS8920, CS8920M */ 1140a3360e1SGeert Uytterhoeven char chip_revision; /* revision letter of the chip ('A'...) */ 1150a3360e1SGeert Uytterhoeven int send_cmd; /* the propercommand used to send a packet. */ 1160a3360e1SGeert Uytterhoeven int rx_mode; 1170a3360e1SGeert Uytterhoeven int curr_rx_cfg; 1180a3360e1SGeert Uytterhoeven int send_underrun; /* keep track of how many underruns in a row we get */ 1190a3360e1SGeert Uytterhoeven struct sk_buff *skb; 1200a3360e1SGeert Uytterhoeven }; 1210a3360e1SGeert Uytterhoeven 1220a3360e1SGeert Uytterhoeven /* Index to functions, as function prototypes. */ 1230a3360e1SGeert Uytterhoeven 1240a3360e1SGeert Uytterhoeven #if 0 1250a3360e1SGeert Uytterhoeven extern void reset_chip(struct net_device *dev); 1260a3360e1SGeert Uytterhoeven #endif 1270a3360e1SGeert Uytterhoeven static int net_open(struct net_device *dev); 1280a3360e1SGeert Uytterhoeven static int net_send_packet(struct sk_buff *skb, struct net_device *dev); 1290a3360e1SGeert Uytterhoeven static irqreturn_t net_interrupt(int irq, void *dev_id); 1300a3360e1SGeert Uytterhoeven static void set_multicast_list(struct net_device *dev); 1310a3360e1SGeert Uytterhoeven static void net_rx(struct net_device *dev); 1320a3360e1SGeert Uytterhoeven static int net_close(struct net_device *dev); 1330a3360e1SGeert Uytterhoeven static struct net_device_stats *net_get_stats(struct net_device *dev); 1340a3360e1SGeert Uytterhoeven static int set_mac_address(struct net_device *dev, void *addr); 1350a3360e1SGeert Uytterhoeven 1360a3360e1SGeert Uytterhoeven 1370a3360e1SGeert Uytterhoeven /* Example routines you must write ;->. */ 1380a3360e1SGeert Uytterhoeven #define tx_done(dev) 1 1390a3360e1SGeert Uytterhoeven 1400a3360e1SGeert Uytterhoeven /* For reading/writing registers ISA-style */ 1410a3360e1SGeert Uytterhoeven static inline int 1420a3360e1SGeert Uytterhoeven readreg_io(struct net_device *dev, int portno) 1430a3360e1SGeert Uytterhoeven { 1440a3360e1SGeert Uytterhoeven nubus_writew(swab16(portno), dev->base_addr + ADD_PORT); 1450a3360e1SGeert Uytterhoeven return swab16(nubus_readw(dev->base_addr + DATA_PORT)); 1460a3360e1SGeert Uytterhoeven } 1470a3360e1SGeert Uytterhoeven 1480a3360e1SGeert Uytterhoeven static inline void 1490a3360e1SGeert Uytterhoeven writereg_io(struct net_device *dev, int portno, int value) 1500a3360e1SGeert Uytterhoeven { 1510a3360e1SGeert Uytterhoeven nubus_writew(swab16(portno), dev->base_addr + ADD_PORT); 1520a3360e1SGeert Uytterhoeven nubus_writew(swab16(value), dev->base_addr + DATA_PORT); 1530a3360e1SGeert Uytterhoeven } 1540a3360e1SGeert Uytterhoeven 1550a3360e1SGeert Uytterhoeven /* These are for reading/writing registers in shared memory */ 1560a3360e1SGeert Uytterhoeven static inline int 1570a3360e1SGeert Uytterhoeven readreg(struct net_device *dev, int portno) 1580a3360e1SGeert Uytterhoeven { 1590a3360e1SGeert Uytterhoeven return swab16(nubus_readw(dev->mem_start + portno)); 1600a3360e1SGeert Uytterhoeven } 1610a3360e1SGeert Uytterhoeven 1620a3360e1SGeert Uytterhoeven static inline void 1630a3360e1SGeert Uytterhoeven writereg(struct net_device *dev, int portno, int value) 1640a3360e1SGeert Uytterhoeven { 1650a3360e1SGeert Uytterhoeven nubus_writew(swab16(value), dev->mem_start + portno); 1660a3360e1SGeert Uytterhoeven } 1670a3360e1SGeert Uytterhoeven 1680a3360e1SGeert Uytterhoeven static const struct net_device_ops mac89x0_netdev_ops = { 1690a3360e1SGeert Uytterhoeven .ndo_open = net_open, 1700a3360e1SGeert Uytterhoeven .ndo_stop = net_close, 1710a3360e1SGeert Uytterhoeven .ndo_start_xmit = net_send_packet, 1720a3360e1SGeert Uytterhoeven .ndo_get_stats = net_get_stats, 1730a3360e1SGeert Uytterhoeven .ndo_set_rx_mode = set_multicast_list, 1740a3360e1SGeert Uytterhoeven .ndo_set_mac_address = set_mac_address, 1750a3360e1SGeert Uytterhoeven .ndo_validate_addr = eth_validate_addr, 1760a3360e1SGeert Uytterhoeven .ndo_change_mtu = eth_change_mtu, 1770a3360e1SGeert Uytterhoeven }; 1780a3360e1SGeert Uytterhoeven 1790a3360e1SGeert Uytterhoeven /* Probe for the CS8900 card in slot E. We won't bother looking 1800a3360e1SGeert Uytterhoeven anywhere else until we have a really good reason to do so. */ 1810a3360e1SGeert Uytterhoeven struct net_device * __init mac89x0_probe(int unit) 1820a3360e1SGeert Uytterhoeven { 1830a3360e1SGeert Uytterhoeven struct net_device *dev; 1840a3360e1SGeert Uytterhoeven static int once_is_enough; 1850a3360e1SGeert Uytterhoeven struct net_local *lp; 1860a3360e1SGeert Uytterhoeven static unsigned version_printed; 1870a3360e1SGeert Uytterhoeven int i, slot; 1880a3360e1SGeert Uytterhoeven unsigned rev_type = 0; 1890a3360e1SGeert Uytterhoeven unsigned long ioaddr; 1900a3360e1SGeert Uytterhoeven unsigned short sig; 1910a3360e1SGeert Uytterhoeven int err = -ENODEV; 1920a3360e1SGeert Uytterhoeven 1930a3360e1SGeert Uytterhoeven if (!MACH_IS_MAC) 1940a3360e1SGeert Uytterhoeven return ERR_PTR(-ENODEV); 1950a3360e1SGeert Uytterhoeven 1960a3360e1SGeert Uytterhoeven dev = alloc_etherdev(sizeof(struct net_local)); 1970a3360e1SGeert Uytterhoeven if (!dev) 1980a3360e1SGeert Uytterhoeven return ERR_PTR(-ENOMEM); 1990a3360e1SGeert Uytterhoeven 2000a3360e1SGeert Uytterhoeven if (unit >= 0) { 2010a3360e1SGeert Uytterhoeven sprintf(dev->name, "eth%d", unit); 2020a3360e1SGeert Uytterhoeven netdev_boot_setup_check(dev); 2030a3360e1SGeert Uytterhoeven } 2040a3360e1SGeert Uytterhoeven 2050a3360e1SGeert Uytterhoeven if (once_is_enough) 2060a3360e1SGeert Uytterhoeven goto out; 2070a3360e1SGeert Uytterhoeven once_is_enough = 1; 2080a3360e1SGeert Uytterhoeven 2090a3360e1SGeert Uytterhoeven /* We might have to parameterize this later */ 2100a3360e1SGeert Uytterhoeven slot = 0xE; 2110a3360e1SGeert Uytterhoeven /* Get out now if there's a real NuBus card in slot E */ 2120a3360e1SGeert Uytterhoeven if (nubus_find_slot(slot, NULL) != NULL) 2130a3360e1SGeert Uytterhoeven goto out; 2140a3360e1SGeert Uytterhoeven 2150a3360e1SGeert Uytterhoeven /* The pseudo-ISA bits always live at offset 0x300 (gee, 2160a3360e1SGeert Uytterhoeven wonder why...) */ 2170a3360e1SGeert Uytterhoeven ioaddr = (unsigned long) 2180a3360e1SGeert Uytterhoeven nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE); 2190a3360e1SGeert Uytterhoeven { 2200a3360e1SGeert Uytterhoeven unsigned long flags; 2210a3360e1SGeert Uytterhoeven int card_present; 2220a3360e1SGeert Uytterhoeven 2230a3360e1SGeert Uytterhoeven local_irq_save(flags); 2240a3360e1SGeert Uytterhoeven card_present = (hwreg_present((void*) ioaddr+4) && 2250a3360e1SGeert Uytterhoeven hwreg_present((void*) ioaddr + DATA_PORT)); 2260a3360e1SGeert Uytterhoeven local_irq_restore(flags); 2270a3360e1SGeert Uytterhoeven 2280a3360e1SGeert Uytterhoeven if (!card_present) 2290a3360e1SGeert Uytterhoeven goto out; 2300a3360e1SGeert Uytterhoeven } 2310a3360e1SGeert Uytterhoeven 2320a3360e1SGeert Uytterhoeven nubus_writew(0, ioaddr + ADD_PORT); 2330a3360e1SGeert Uytterhoeven sig = nubus_readw(ioaddr + DATA_PORT); 2340a3360e1SGeert Uytterhoeven if (sig != swab16(CHIP_EISA_ID_SIG)) 2350a3360e1SGeert Uytterhoeven goto out; 2360a3360e1SGeert Uytterhoeven 2370a3360e1SGeert Uytterhoeven /* Initialize the net_device structure. */ 2380a3360e1SGeert Uytterhoeven lp = netdev_priv(dev); 2390a3360e1SGeert Uytterhoeven 2400a3360e1SGeert Uytterhoeven /* Fill in the 'dev' fields. */ 2410a3360e1SGeert Uytterhoeven dev->base_addr = ioaddr; 2420a3360e1SGeert Uytterhoeven dev->mem_start = (unsigned long) 2430a3360e1SGeert Uytterhoeven nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE); 2440a3360e1SGeert Uytterhoeven dev->mem_end = dev->mem_start + 0x1000; 2450a3360e1SGeert Uytterhoeven 2460a3360e1SGeert Uytterhoeven /* Turn on shared memory */ 2470a3360e1SGeert Uytterhoeven writereg_io(dev, PP_BusCTL, MEMORY_ON); 2480a3360e1SGeert Uytterhoeven 2490a3360e1SGeert Uytterhoeven /* get the chip type */ 2500a3360e1SGeert Uytterhoeven rev_type = readreg(dev, PRODUCT_ID_ADD); 2510a3360e1SGeert Uytterhoeven lp->chip_type = rev_type &~ REVISON_BITS; 2520a3360e1SGeert Uytterhoeven lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; 2530a3360e1SGeert Uytterhoeven 2540a3360e1SGeert Uytterhoeven /* Check the chip type and revision in order to set the correct send command 2550a3360e1SGeert Uytterhoeven CS8920 revision C and CS8900 revision F can use the faster send. */ 2560a3360e1SGeert Uytterhoeven lp->send_cmd = TX_AFTER_381; 2570a3360e1SGeert Uytterhoeven if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') 2580a3360e1SGeert Uytterhoeven lp->send_cmd = TX_NOW; 2590a3360e1SGeert Uytterhoeven if (lp->chip_type != CS8900 && lp->chip_revision >= 'C') 2600a3360e1SGeert Uytterhoeven lp->send_cmd = TX_NOW; 2610a3360e1SGeert Uytterhoeven 2620a3360e1SGeert Uytterhoeven if (net_debug && version_printed++ == 0) 2630a3360e1SGeert Uytterhoeven printk(version); 2640a3360e1SGeert Uytterhoeven 2650a3360e1SGeert Uytterhoeven printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx", 2660a3360e1SGeert Uytterhoeven dev->name, 2670a3360e1SGeert Uytterhoeven lp->chip_type==CS8900?'0':'2', 2680a3360e1SGeert Uytterhoeven lp->chip_type==CS8920M?"M":"", 2690a3360e1SGeert Uytterhoeven lp->chip_revision, 2700a3360e1SGeert Uytterhoeven dev->base_addr); 2710a3360e1SGeert Uytterhoeven 2720a3360e1SGeert Uytterhoeven /* Try to read the MAC address */ 2730a3360e1SGeert Uytterhoeven if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) { 2740a3360e1SGeert Uytterhoeven printk("\nmac89x0: No EEPROM, giving up now.\n"); 2750a3360e1SGeert Uytterhoeven goto out1; 2760a3360e1SGeert Uytterhoeven } else { 2770a3360e1SGeert Uytterhoeven for (i = 0; i < ETH_ALEN; i += 2) { 2780a3360e1SGeert Uytterhoeven /* Big-endian (why??!) */ 2790a3360e1SGeert Uytterhoeven unsigned short s = readreg(dev, PP_IA + i); 2800a3360e1SGeert Uytterhoeven dev->dev_addr[i] = s >> 8; 2810a3360e1SGeert Uytterhoeven dev->dev_addr[i+1] = s & 0xff; 2820a3360e1SGeert Uytterhoeven } 2830a3360e1SGeert Uytterhoeven } 2840a3360e1SGeert Uytterhoeven 2850a3360e1SGeert Uytterhoeven dev->irq = SLOT2IRQ(slot); 2860a3360e1SGeert Uytterhoeven 2870a3360e1SGeert Uytterhoeven /* print the IRQ and ethernet address. */ 2880a3360e1SGeert Uytterhoeven 2890a3360e1SGeert Uytterhoeven printk(" IRQ %d ADDR %pM\n", dev->irq, dev->dev_addr); 2900a3360e1SGeert Uytterhoeven 2910a3360e1SGeert Uytterhoeven dev->netdev_ops = &mac89x0_netdev_ops; 2920a3360e1SGeert Uytterhoeven 2930a3360e1SGeert Uytterhoeven err = register_netdev(dev); 2940a3360e1SGeert Uytterhoeven if (err) 2950a3360e1SGeert Uytterhoeven goto out1; 2960a3360e1SGeert Uytterhoeven return NULL; 2970a3360e1SGeert Uytterhoeven out1: 2980a3360e1SGeert Uytterhoeven nubus_writew(0, dev->base_addr + ADD_PORT); 2990a3360e1SGeert Uytterhoeven out: 3000a3360e1SGeert Uytterhoeven free_netdev(dev); 3010a3360e1SGeert Uytterhoeven return ERR_PTR(err); 3020a3360e1SGeert Uytterhoeven } 3030a3360e1SGeert Uytterhoeven 3040a3360e1SGeert Uytterhoeven #if 0 3050a3360e1SGeert Uytterhoeven /* This is useful for something, but I don't know what yet. */ 3060a3360e1SGeert Uytterhoeven void __init reset_chip(struct net_device *dev) 3070a3360e1SGeert Uytterhoeven { 3080a3360e1SGeert Uytterhoeven int reset_start_time; 3090a3360e1SGeert Uytterhoeven 3100a3360e1SGeert Uytterhoeven writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); 3110a3360e1SGeert Uytterhoeven 3120a3360e1SGeert Uytterhoeven /* wait 30 ms */ 3130a3360e1SGeert Uytterhoeven msleep_interruptible(30); 3140a3360e1SGeert Uytterhoeven 3150a3360e1SGeert Uytterhoeven /* Wait until the chip is reset */ 3160a3360e1SGeert Uytterhoeven reset_start_time = jiffies; 3170a3360e1SGeert Uytterhoeven while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) 3180a3360e1SGeert Uytterhoeven ; 3190a3360e1SGeert Uytterhoeven } 3200a3360e1SGeert Uytterhoeven #endif 3210a3360e1SGeert Uytterhoeven 3220a3360e1SGeert Uytterhoeven /* Open/initialize the board. This is called (in the current kernel) 3230a3360e1SGeert Uytterhoeven sometime after booting when the 'ifconfig' program is run. 3240a3360e1SGeert Uytterhoeven 3250a3360e1SGeert Uytterhoeven This routine should set everything up anew at each open, even 3260a3360e1SGeert Uytterhoeven registers that "should" only need to be set once at boot, so that 3270a3360e1SGeert Uytterhoeven there is non-reboot way to recover if something goes wrong. 3280a3360e1SGeert Uytterhoeven */ 3290a3360e1SGeert Uytterhoeven static int 3300a3360e1SGeert Uytterhoeven net_open(struct net_device *dev) 3310a3360e1SGeert Uytterhoeven { 3320a3360e1SGeert Uytterhoeven struct net_local *lp = netdev_priv(dev); 3330a3360e1SGeert Uytterhoeven int i; 3340a3360e1SGeert Uytterhoeven 3350a3360e1SGeert Uytterhoeven /* Disable the interrupt for now */ 3360a3360e1SGeert Uytterhoeven writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); 3370a3360e1SGeert Uytterhoeven 3380a3360e1SGeert Uytterhoeven /* Grab the interrupt */ 3390a3360e1SGeert Uytterhoeven if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev)) 3400a3360e1SGeert Uytterhoeven return -EAGAIN; 3410a3360e1SGeert Uytterhoeven 3420a3360e1SGeert Uytterhoeven /* Set up the IRQ - Apparently magic */ 3430a3360e1SGeert Uytterhoeven if (lp->chip_type == CS8900) 3440a3360e1SGeert Uytterhoeven writereg(dev, PP_CS8900_ISAINT, 0); 3450a3360e1SGeert Uytterhoeven else 3460a3360e1SGeert Uytterhoeven writereg(dev, PP_CS8920_ISAINT, 0); 3470a3360e1SGeert Uytterhoeven 3480a3360e1SGeert Uytterhoeven /* set the Ethernet address */ 3490a3360e1SGeert Uytterhoeven for (i=0; i < ETH_ALEN/2; i++) 3500a3360e1SGeert Uytterhoeven writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); 3510a3360e1SGeert Uytterhoeven 3520a3360e1SGeert Uytterhoeven /* Turn on both receive and transmit operations */ 3530a3360e1SGeert Uytterhoeven writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON); 3540a3360e1SGeert Uytterhoeven 3550a3360e1SGeert Uytterhoeven /* Receive only error free packets addressed to this card */ 3560a3360e1SGeert Uytterhoeven lp->rx_mode = 0; 3570a3360e1SGeert Uytterhoeven writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); 3580a3360e1SGeert Uytterhoeven 3590a3360e1SGeert Uytterhoeven lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; 3600a3360e1SGeert Uytterhoeven 3610a3360e1SGeert Uytterhoeven writereg(dev, PP_RxCFG, lp->curr_rx_cfg); 3620a3360e1SGeert Uytterhoeven 3630a3360e1SGeert Uytterhoeven writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | 3640a3360e1SGeert Uytterhoeven TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); 3650a3360e1SGeert Uytterhoeven 3660a3360e1SGeert Uytterhoeven writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | 3670a3360e1SGeert Uytterhoeven TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); 3680a3360e1SGeert Uytterhoeven 3690a3360e1SGeert Uytterhoeven /* now that we've got our act together, enable everything */ 3700a3360e1SGeert Uytterhoeven writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ); 3710a3360e1SGeert Uytterhoeven netif_start_queue(dev); 3720a3360e1SGeert Uytterhoeven return 0; 3730a3360e1SGeert Uytterhoeven } 3740a3360e1SGeert Uytterhoeven 3750a3360e1SGeert Uytterhoeven static int 3760a3360e1SGeert Uytterhoeven net_send_packet(struct sk_buff *skb, struct net_device *dev) 3770a3360e1SGeert Uytterhoeven { 3780a3360e1SGeert Uytterhoeven struct net_local *lp = netdev_priv(dev); 3790a3360e1SGeert Uytterhoeven unsigned long flags; 3800a3360e1SGeert Uytterhoeven 3810a3360e1SGeert Uytterhoeven if (net_debug > 3) 3820a3360e1SGeert Uytterhoeven printk("%s: sent %d byte packet of type %x\n", 3830a3360e1SGeert Uytterhoeven dev->name, skb->len, 3840a3360e1SGeert Uytterhoeven (skb->data[ETH_ALEN+ETH_ALEN] << 8) 3850a3360e1SGeert Uytterhoeven | skb->data[ETH_ALEN+ETH_ALEN+1]); 3860a3360e1SGeert Uytterhoeven 3870a3360e1SGeert Uytterhoeven /* keep the upload from being interrupted, since we 3880a3360e1SGeert Uytterhoeven ask the chip to start transmitting before the 3890a3360e1SGeert Uytterhoeven whole packet has been completely uploaded. */ 3900a3360e1SGeert Uytterhoeven local_irq_save(flags); 3910a3360e1SGeert Uytterhoeven netif_stop_queue(dev); 3920a3360e1SGeert Uytterhoeven 3930a3360e1SGeert Uytterhoeven /* initiate a transmit sequence */ 3940a3360e1SGeert Uytterhoeven writereg(dev, PP_TxCMD, lp->send_cmd); 3950a3360e1SGeert Uytterhoeven writereg(dev, PP_TxLength, skb->len); 3960a3360e1SGeert Uytterhoeven 3970a3360e1SGeert Uytterhoeven /* Test to see if the chip has allocated memory for the packet */ 3980a3360e1SGeert Uytterhoeven if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { 3990a3360e1SGeert Uytterhoeven /* Gasp! It hasn't. But that shouldn't happen since 4000a3360e1SGeert Uytterhoeven we're waiting for TxOk, so return 1 and requeue this packet. */ 4010a3360e1SGeert Uytterhoeven local_irq_restore(flags); 4020a3360e1SGeert Uytterhoeven return NETDEV_TX_BUSY; 4030a3360e1SGeert Uytterhoeven } 4040a3360e1SGeert Uytterhoeven 4050a3360e1SGeert Uytterhoeven /* Write the contents of the packet */ 4060a3360e1SGeert Uytterhoeven skb_copy_from_linear_data(skb, (void *)(dev->mem_start + PP_TxFrame), 4070a3360e1SGeert Uytterhoeven skb->len+1); 4080a3360e1SGeert Uytterhoeven 4090a3360e1SGeert Uytterhoeven local_irq_restore(flags); 4100a3360e1SGeert Uytterhoeven dev_kfree_skb (skb); 4110a3360e1SGeert Uytterhoeven 4120a3360e1SGeert Uytterhoeven return NETDEV_TX_OK; 4130a3360e1SGeert Uytterhoeven } 4140a3360e1SGeert Uytterhoeven 4150a3360e1SGeert Uytterhoeven /* The typical workload of the driver: 4160a3360e1SGeert Uytterhoeven Handle the network interface interrupts. */ 4170a3360e1SGeert Uytterhoeven static irqreturn_t net_interrupt(int irq, void *dev_id) 4180a3360e1SGeert Uytterhoeven { 4190a3360e1SGeert Uytterhoeven struct net_device *dev = dev_id; 4200a3360e1SGeert Uytterhoeven struct net_local *lp; 4210a3360e1SGeert Uytterhoeven int ioaddr, status; 4220a3360e1SGeert Uytterhoeven 4230a3360e1SGeert Uytterhoeven if (dev == NULL) { 4240a3360e1SGeert Uytterhoeven printk ("net_interrupt(): irq %d for unknown device.\n", irq); 4250a3360e1SGeert Uytterhoeven return IRQ_NONE; 4260a3360e1SGeert Uytterhoeven } 4270a3360e1SGeert Uytterhoeven 4280a3360e1SGeert Uytterhoeven ioaddr = dev->base_addr; 4290a3360e1SGeert Uytterhoeven lp = netdev_priv(dev); 4300a3360e1SGeert Uytterhoeven 4310a3360e1SGeert Uytterhoeven /* we MUST read all the events out of the ISQ, otherwise we'll never 4320a3360e1SGeert Uytterhoeven get interrupted again. As a consequence, we can't have any limit 4330a3360e1SGeert Uytterhoeven on the number of times we loop in the interrupt handler. The 4340a3360e1SGeert Uytterhoeven hardware guarantees that eventually we'll run out of events. Of 4350a3360e1SGeert Uytterhoeven course, if you're on a slow machine, and packets are arriving 4360a3360e1SGeert Uytterhoeven faster than you can read them off, you're screwed. Hasta la 4370a3360e1SGeert Uytterhoeven vista, baby! */ 4380a3360e1SGeert Uytterhoeven while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) { 4390a3360e1SGeert Uytterhoeven if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); 4400a3360e1SGeert Uytterhoeven switch(status & ISQ_EVENT_MASK) { 4410a3360e1SGeert Uytterhoeven case ISQ_RECEIVER_EVENT: 4420a3360e1SGeert Uytterhoeven /* Got a packet(s). */ 4430a3360e1SGeert Uytterhoeven net_rx(dev); 4440a3360e1SGeert Uytterhoeven break; 4450a3360e1SGeert Uytterhoeven case ISQ_TRANSMITTER_EVENT: 4460a3360e1SGeert Uytterhoeven dev->stats.tx_packets++; 4470a3360e1SGeert Uytterhoeven netif_wake_queue(dev); 4480a3360e1SGeert Uytterhoeven if ((status & TX_OK) == 0) 4490a3360e1SGeert Uytterhoeven dev->stats.tx_errors++; 4500a3360e1SGeert Uytterhoeven if (status & TX_LOST_CRS) 4510a3360e1SGeert Uytterhoeven dev->stats.tx_carrier_errors++; 4520a3360e1SGeert Uytterhoeven if (status & TX_SQE_ERROR) 4530a3360e1SGeert Uytterhoeven dev->stats.tx_heartbeat_errors++; 4540a3360e1SGeert Uytterhoeven if (status & TX_LATE_COL) 4550a3360e1SGeert Uytterhoeven dev->stats.tx_window_errors++; 4560a3360e1SGeert Uytterhoeven if (status & TX_16_COL) 4570a3360e1SGeert Uytterhoeven dev->stats.tx_aborted_errors++; 4580a3360e1SGeert Uytterhoeven break; 4590a3360e1SGeert Uytterhoeven case ISQ_BUFFER_EVENT: 4600a3360e1SGeert Uytterhoeven if (status & READY_FOR_TX) { 4610a3360e1SGeert Uytterhoeven /* we tried to transmit a packet earlier, 4620a3360e1SGeert Uytterhoeven but inexplicably ran out of buffers. 4630a3360e1SGeert Uytterhoeven That shouldn't happen since we only ever 4640a3360e1SGeert Uytterhoeven load one packet. Shrug. Do the right 4650a3360e1SGeert Uytterhoeven thing anyway. */ 4660a3360e1SGeert Uytterhoeven netif_wake_queue(dev); 4670a3360e1SGeert Uytterhoeven } 4680a3360e1SGeert Uytterhoeven if (status & TX_UNDERRUN) { 4690a3360e1SGeert Uytterhoeven if (net_debug > 0) printk("%s: transmit underrun\n", dev->name); 4700a3360e1SGeert Uytterhoeven lp->send_underrun++; 4710a3360e1SGeert Uytterhoeven if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; 4720a3360e1SGeert Uytterhoeven else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; 4730a3360e1SGeert Uytterhoeven } 4740a3360e1SGeert Uytterhoeven break; 4750a3360e1SGeert Uytterhoeven case ISQ_RX_MISS_EVENT: 4760a3360e1SGeert Uytterhoeven dev->stats.rx_missed_errors += (status >> 6); 4770a3360e1SGeert Uytterhoeven break; 4780a3360e1SGeert Uytterhoeven case ISQ_TX_COL_EVENT: 4790a3360e1SGeert Uytterhoeven dev->stats.collisions += (status >> 6); 4800a3360e1SGeert Uytterhoeven break; 4810a3360e1SGeert Uytterhoeven } 4820a3360e1SGeert Uytterhoeven } 4830a3360e1SGeert Uytterhoeven return IRQ_HANDLED; 4840a3360e1SGeert Uytterhoeven } 4850a3360e1SGeert Uytterhoeven 4860a3360e1SGeert Uytterhoeven /* We have a good packet(s), get it/them out of the buffers. */ 4870a3360e1SGeert Uytterhoeven static void 4880a3360e1SGeert Uytterhoeven net_rx(struct net_device *dev) 4890a3360e1SGeert Uytterhoeven { 4900a3360e1SGeert Uytterhoeven struct sk_buff *skb; 4910a3360e1SGeert Uytterhoeven int status, length; 4920a3360e1SGeert Uytterhoeven 4930a3360e1SGeert Uytterhoeven status = readreg(dev, PP_RxStatus); 4940a3360e1SGeert Uytterhoeven if ((status & RX_OK) == 0) { 4950a3360e1SGeert Uytterhoeven dev->stats.rx_errors++; 4960a3360e1SGeert Uytterhoeven if (status & RX_RUNT) 4970a3360e1SGeert Uytterhoeven dev->stats.rx_length_errors++; 4980a3360e1SGeert Uytterhoeven if (status & RX_EXTRA_DATA) 4990a3360e1SGeert Uytterhoeven dev->stats.rx_length_errors++; 5000a3360e1SGeert Uytterhoeven if ((status & RX_CRC_ERROR) && 5010a3360e1SGeert Uytterhoeven !(status & (RX_EXTRA_DATA|RX_RUNT))) 5020a3360e1SGeert Uytterhoeven /* per str 172 */ 5030a3360e1SGeert Uytterhoeven dev->stats.rx_crc_errors++; 5040a3360e1SGeert Uytterhoeven if (status & RX_DRIBBLE) 5050a3360e1SGeert Uytterhoeven dev->stats.rx_frame_errors++; 5060a3360e1SGeert Uytterhoeven return; 5070a3360e1SGeert Uytterhoeven } 5080a3360e1SGeert Uytterhoeven 5090a3360e1SGeert Uytterhoeven length = readreg(dev, PP_RxLength); 5100a3360e1SGeert Uytterhoeven /* Malloc up new buffer. */ 5110a3360e1SGeert Uytterhoeven skb = alloc_skb(length, GFP_ATOMIC); 5120a3360e1SGeert Uytterhoeven if (skb == NULL) { 5130a3360e1SGeert Uytterhoeven printk("%s: Memory squeeze, dropping packet.\n", dev->name); 5140a3360e1SGeert Uytterhoeven dev->stats.rx_dropped++; 5150a3360e1SGeert Uytterhoeven return; 5160a3360e1SGeert Uytterhoeven } 5170a3360e1SGeert Uytterhoeven skb_put(skb, length); 5180a3360e1SGeert Uytterhoeven 5190a3360e1SGeert Uytterhoeven skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame), 5200a3360e1SGeert Uytterhoeven length); 5210a3360e1SGeert Uytterhoeven 5220a3360e1SGeert Uytterhoeven if (net_debug > 3)printk("%s: received %d byte packet of type %x\n", 5230a3360e1SGeert Uytterhoeven dev->name, length, 5240a3360e1SGeert Uytterhoeven (skb->data[ETH_ALEN+ETH_ALEN] << 8) 5250a3360e1SGeert Uytterhoeven | skb->data[ETH_ALEN+ETH_ALEN+1]); 5260a3360e1SGeert Uytterhoeven 5270a3360e1SGeert Uytterhoeven skb->protocol=eth_type_trans(skb,dev); 5280a3360e1SGeert Uytterhoeven netif_rx(skb); 5290a3360e1SGeert Uytterhoeven dev->stats.rx_packets++; 5300a3360e1SGeert Uytterhoeven dev->stats.rx_bytes += length; 5310a3360e1SGeert Uytterhoeven } 5320a3360e1SGeert Uytterhoeven 5330a3360e1SGeert Uytterhoeven /* The inverse routine to net_open(). */ 5340a3360e1SGeert Uytterhoeven static int 5350a3360e1SGeert Uytterhoeven net_close(struct net_device *dev) 5360a3360e1SGeert Uytterhoeven { 5370a3360e1SGeert Uytterhoeven 5380a3360e1SGeert Uytterhoeven writereg(dev, PP_RxCFG, 0); 5390a3360e1SGeert Uytterhoeven writereg(dev, PP_TxCFG, 0); 5400a3360e1SGeert Uytterhoeven writereg(dev, PP_BufCFG, 0); 5410a3360e1SGeert Uytterhoeven writereg(dev, PP_BusCTL, 0); 5420a3360e1SGeert Uytterhoeven 5430a3360e1SGeert Uytterhoeven netif_stop_queue(dev); 5440a3360e1SGeert Uytterhoeven 5450a3360e1SGeert Uytterhoeven free_irq(dev->irq, dev); 5460a3360e1SGeert Uytterhoeven 5470a3360e1SGeert Uytterhoeven /* Update the statistics here. */ 5480a3360e1SGeert Uytterhoeven 5490a3360e1SGeert Uytterhoeven return 0; 5500a3360e1SGeert Uytterhoeven 5510a3360e1SGeert Uytterhoeven } 5520a3360e1SGeert Uytterhoeven 5530a3360e1SGeert Uytterhoeven /* Get the current statistics. This may be called with the card open or 5540a3360e1SGeert Uytterhoeven closed. */ 5550a3360e1SGeert Uytterhoeven static struct net_device_stats * 5560a3360e1SGeert Uytterhoeven net_get_stats(struct net_device *dev) 5570a3360e1SGeert Uytterhoeven { 5580a3360e1SGeert Uytterhoeven unsigned long flags; 5590a3360e1SGeert Uytterhoeven 5600a3360e1SGeert Uytterhoeven local_irq_save(flags); 5610a3360e1SGeert Uytterhoeven /* Update the statistics from the device registers. */ 5620a3360e1SGeert Uytterhoeven dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); 5630a3360e1SGeert Uytterhoeven dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6); 5640a3360e1SGeert Uytterhoeven local_irq_restore(flags); 5650a3360e1SGeert Uytterhoeven 5660a3360e1SGeert Uytterhoeven return &dev->stats; 5670a3360e1SGeert Uytterhoeven } 5680a3360e1SGeert Uytterhoeven 5690a3360e1SGeert Uytterhoeven static void set_multicast_list(struct net_device *dev) 5700a3360e1SGeert Uytterhoeven { 5710a3360e1SGeert Uytterhoeven struct net_local *lp = netdev_priv(dev); 5720a3360e1SGeert Uytterhoeven 5730a3360e1SGeert Uytterhoeven if(dev->flags&IFF_PROMISC) 5740a3360e1SGeert Uytterhoeven { 5750a3360e1SGeert Uytterhoeven lp->rx_mode = RX_ALL_ACCEPT; 5760a3360e1SGeert Uytterhoeven } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { 5770a3360e1SGeert Uytterhoeven /* The multicast-accept list is initialized to accept-all, and we 5780a3360e1SGeert Uytterhoeven rely on higher-level filtering for now. */ 5790a3360e1SGeert Uytterhoeven lp->rx_mode = RX_MULTCAST_ACCEPT; 5800a3360e1SGeert Uytterhoeven } 5810a3360e1SGeert Uytterhoeven else 5820a3360e1SGeert Uytterhoeven lp->rx_mode = 0; 5830a3360e1SGeert Uytterhoeven 5840a3360e1SGeert Uytterhoeven writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); 5850a3360e1SGeert Uytterhoeven 5860a3360e1SGeert Uytterhoeven /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */ 5870a3360e1SGeert Uytterhoeven writereg(dev, PP_RxCFG, lp->curr_rx_cfg | 5880a3360e1SGeert Uytterhoeven (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); 5890a3360e1SGeert Uytterhoeven } 5900a3360e1SGeert Uytterhoeven 5910a3360e1SGeert Uytterhoeven 5920a3360e1SGeert Uytterhoeven static int set_mac_address(struct net_device *dev, void *addr) 5930a3360e1SGeert Uytterhoeven { 59466dc92edSDanny Kukawka struct sockaddr *saddr = addr; 595*9100eb01SDavid S. Miller int i; 59666dc92edSDanny Kukawka 597*9100eb01SDavid S. Miller if (!is_valid_ether_addr(saddr->sa_data)) 59866dc92edSDanny Kukawka return -EADDRNOTAVAIL; 59966dc92edSDanny Kukawka 600*9100eb01SDavid S. Miller memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN); 60166dc92edSDanny Kukawka printk("%s: Setting MAC address to %pM\n", dev->name, dev->dev_addr); 60266dc92edSDanny Kukawka 6030a3360e1SGeert Uytterhoeven /* set the Ethernet address */ 6040a3360e1SGeert Uytterhoeven for (i=0; i < ETH_ALEN/2; i++) 6050a3360e1SGeert Uytterhoeven writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); 6060a3360e1SGeert Uytterhoeven 6070a3360e1SGeert Uytterhoeven return 0; 6080a3360e1SGeert Uytterhoeven } 6090a3360e1SGeert Uytterhoeven 6100a3360e1SGeert Uytterhoeven #ifdef MODULE 6110a3360e1SGeert Uytterhoeven 6120a3360e1SGeert Uytterhoeven static struct net_device *dev_cs89x0; 6130a3360e1SGeert Uytterhoeven static int debug; 6140a3360e1SGeert Uytterhoeven 6150a3360e1SGeert Uytterhoeven module_param(debug, int, 0); 6160a3360e1SGeert Uytterhoeven MODULE_PARM_DESC(debug, "CS89[02]0 debug level (0-5)"); 6170a3360e1SGeert Uytterhoeven MODULE_LICENSE("GPL"); 6180a3360e1SGeert Uytterhoeven 6190a3360e1SGeert Uytterhoeven int __init 6200a3360e1SGeert Uytterhoeven init_module(void) 6210a3360e1SGeert Uytterhoeven { 6220a3360e1SGeert Uytterhoeven net_debug = debug; 6230a3360e1SGeert Uytterhoeven dev_cs89x0 = mac89x0_probe(-1); 6240a3360e1SGeert Uytterhoeven if (IS_ERR(dev_cs89x0)) { 6250a3360e1SGeert Uytterhoeven printk(KERN_WARNING "mac89x0.c: No card found\n"); 6260a3360e1SGeert Uytterhoeven return PTR_ERR(dev_cs89x0); 6270a3360e1SGeert Uytterhoeven } 6280a3360e1SGeert Uytterhoeven return 0; 6290a3360e1SGeert Uytterhoeven } 6300a3360e1SGeert Uytterhoeven 6310a3360e1SGeert Uytterhoeven void 6320a3360e1SGeert Uytterhoeven cleanup_module(void) 6330a3360e1SGeert Uytterhoeven { 6340a3360e1SGeert Uytterhoeven unregister_netdev(dev_cs89x0); 6350a3360e1SGeert Uytterhoeven nubus_writew(0, dev_cs89x0->base_addr + ADD_PORT); 6360a3360e1SGeert Uytterhoeven free_netdev(dev_cs89x0); 6370a3360e1SGeert Uytterhoeven } 6380a3360e1SGeert Uytterhoeven #endif /* MODULE */ 639