| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* hp.c: A HP LAN ethernet driver for linux. */ | 
 | 2 | /* | 
 | 3 | 	Written 1993-94 by Donald Becker. | 
 | 4 |  | 
 | 5 | 	Copyright 1993 United States Government as represented by the | 
 | 6 | 	Director, National Security Agency. | 
 | 7 |  | 
 | 8 | 	This software may be used and distributed according to the terms | 
 | 9 | 	of the GNU General Public License, incorporated herein by reference. | 
 | 10 |  | 
 | 11 | 	The author may be reached as becker@scyld.com, or C/O | 
 | 12 | 	Scyld Computing Corporation | 
 | 13 | 	410 Severn Ave., Suite 210 | 
 | 14 | 	Annapolis MD 21403 | 
 | 15 |  | 
 | 16 | 	This is a driver for the HP PC-LAN adaptors. | 
 | 17 |  | 
 | 18 | 	Sources: | 
 | 19 | 	  The Crynwr packet driver. | 
 | 20 | */ | 
 | 21 |  | 
 | 22 | static const char version[] = | 
 | 23 | 	"hp.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; | 
 | 24 |  | 
 | 25 |  | 
 | 26 | #include <linux/module.h> | 
 | 27 | #include <linux/kernel.h> | 
 | 28 | #include <linux/errno.h> | 
 | 29 | #include <linux/ioport.h> | 
 | 30 | #include <linux/netdevice.h> | 
 | 31 | #include <linux/etherdevice.h> | 
 | 32 | #include <linux/init.h> | 
 | 33 | #include <linux/delay.h> | 
 | 34 |  | 
 | 35 | #include <asm/system.h> | 
 | 36 | #include <asm/io.h> | 
 | 37 |  | 
 | 38 | #include "8390.h" | 
 | 39 |  | 
 | 40 | #define DRV_NAME "hp" | 
 | 41 |  | 
 | 42 | /* A zero-terminated list of I/O addresses to be probed. */ | 
 | 43 | static unsigned int hppclan_portlist[] __initdata = | 
 | 44 | { 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0}; | 
 | 45 |  | 
 | 46 | #define HP_IO_EXTENT	32 | 
 | 47 |  | 
 | 48 | #define HP_DATAPORT		0x0c	/* "Remote DMA" data port. */ | 
 | 49 | #define HP_ID			0x07 | 
 | 50 | #define HP_CONFIGURE	0x08	/* Configuration register. */ | 
 | 51 | #define	 HP_RUN			0x01	/* 1 == Run, 0 == reset. */ | 
 | 52 | #define	 HP_IRQ			0x0E	/* Mask for software-configured IRQ line. */ | 
 | 53 | #define	 HP_DATAON		0x10	/* Turn on dataport */ | 
 | 54 | #define NIC_OFFSET		0x10	/* Offset the 8390 registers. */ | 
 | 55 |  | 
 | 56 | #define HP_START_PG		0x00	/* First page of TX buffer */ | 
 | 57 | #define HP_8BSTOP_PG	0x80	/* Last page +1 of RX ring */ | 
 | 58 | #define HP_16BSTOP_PG	0xFF	/* Same, for 16 bit cards. */ | 
 | 59 |  | 
 | 60 | static int hp_probe1(struct net_device *dev, int ioaddr); | 
 | 61 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 62 | static void hp_reset_8390(struct net_device *dev); | 
 | 63 | static void hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, | 
 | 64 | 					int ring_page); | 
 | 65 | static void hp_block_input(struct net_device *dev, int count, | 
 | 66 | 					struct sk_buff *skb , int ring_offset); | 
 | 67 | static void hp_block_output(struct net_device *dev, int count, | 
 | 68 | 							const unsigned char *buf, int start_page); | 
 | 69 |  | 
 | 70 | static void hp_init_card(struct net_device *dev); | 
 | 71 |  | 
 | 72 | /* The map from IRQ number to HP_CONFIGURE register setting. */ | 
 | 73 | /* My default is IRQ5	             0  1  2  3  4  5  6  7  8  9 10 11 */ | 
 | 74 | static char irqmap[16] __initdata= { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0}; | 
 | 75 |  | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 76 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 77 | /*	Probe for an HP LAN adaptor. | 
 | 78 | 	Also initialize the card and fill in STATION_ADDR with the station | 
 | 79 | 	address. */ | 
 | 80 |  | 
 | 81 | static int __init do_hp_probe(struct net_device *dev) | 
 | 82 | { | 
 | 83 | 	int i; | 
 | 84 | 	int base_addr = dev->base_addr; | 
 | 85 | 	int irq = dev->irq; | 
 | 86 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 87 | 	if (base_addr > 0x1ff)		/* Check a single specified location. */ | 
 | 88 | 		return hp_probe1(dev, base_addr); | 
 | 89 | 	else if (base_addr != 0)	/* Don't probe at all. */ | 
 | 90 | 		return -ENXIO; | 
 | 91 |  | 
 | 92 | 	for (i = 0; hppclan_portlist[i]; i++) { | 
 | 93 | 		if (hp_probe1(dev, hppclan_portlist[i]) == 0) | 
 | 94 | 			return 0; | 
 | 95 | 		dev->irq = irq; | 
 | 96 | 	} | 
 | 97 |  | 
 | 98 | 	return -ENODEV; | 
 | 99 | } | 
 | 100 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | #ifndef MODULE | 
 | 102 | struct net_device * __init hp_probe(int unit) | 
 | 103 | { | 
| Alan Cox | 055e511 | 2008-07-03 23:43:12 -0700 | [diff] [blame] | 104 | 	struct net_device *dev = alloc_eip_netdev(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | 	int err; | 
 | 106 |  | 
 | 107 | 	if (!dev) | 
 | 108 | 		return ERR_PTR(-ENOMEM); | 
 | 109 |  | 
 | 110 | 	sprintf(dev->name, "eth%d", unit); | 
 | 111 | 	netdev_boot_setup_check(dev); | 
 | 112 |  | 
 | 113 | 	err = do_hp_probe(dev); | 
 | 114 | 	if (err) | 
 | 115 | 		goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 116 | 	return dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | out: | 
 | 118 | 	free_netdev(dev); | 
 | 119 | 	return ERR_PTR(err); | 
 | 120 | } | 
 | 121 | #endif | 
 | 122 |  | 
 | 123 | static int __init hp_probe1(struct net_device *dev, int ioaddr) | 
 | 124 | { | 
 | 125 | 	int i, retval, board_id, wordmode; | 
 | 126 | 	const char *name; | 
 | 127 | 	static unsigned version_printed; | 
 | 128 |  | 
 | 129 | 	if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME)) | 
 | 130 | 		return -EBUSY; | 
 | 131 |  | 
 | 132 | 	/* Check for the HP physical address, 08 00 09 xx xx xx. */ | 
 | 133 | 	/* This really isn't good enough: we may pick up HP LANCE boards | 
 | 134 | 	   also!  Avoid the lance 0x5757 signature. */ | 
 | 135 | 	if (inb(ioaddr) != 0x08 | 
 | 136 | 		|| inb(ioaddr+1) != 0x00 | 
 | 137 | 		|| inb(ioaddr+2) != 0x09 | 
 | 138 | 		|| inb(ioaddr+14) == 0x57) { | 
 | 139 | 		retval = -ENODEV; | 
 | 140 | 		goto out; | 
 | 141 | 	} | 
 | 142 |  | 
 | 143 | 	/* Set up the parameters based on the board ID. | 
 | 144 | 	   If you have additional mappings, please mail them to me -djb. */ | 
 | 145 | 	if ((board_id = inb(ioaddr + HP_ID)) & 0x80) { | 
 | 146 | 		name = "HP27247"; | 
 | 147 | 		wordmode = 1; | 
 | 148 | 	} else { | 
 | 149 | 		name = "HP27250"; | 
 | 150 | 		wordmode = 0; | 
 | 151 | 	} | 
 | 152 |  | 
 | 153 | 	if (ei_debug  &&  version_printed++ == 0) | 
 | 154 | 		printk(version); | 
 | 155 |  | 
 | 156 | 	printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr); | 
 | 157 |  | 
 | 158 | 	for(i = 0; i < ETHER_ADDR_LEN; i++) | 
| Joe Perches | 0795af5 | 2007-10-03 17:59:30 -0700 | [diff] [blame] | 159 | 		dev->dev_addr[i] = inb(ioaddr + i); | 
 | 160 |  | 
| Johannes Berg | e174961 | 2008-10-27 15:59:26 -0700 | [diff] [blame] | 161 | 	printk(" %pM", dev->dev_addr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 162 |  | 
 | 163 | 	/* Snarf the interrupt now.  Someday this could be moved to open(). */ | 
 | 164 | 	if (dev->irq < 2) { | 
| Joe Perches | b6bc765 | 2010-12-21 02:16:08 -0800 | [diff] [blame] | 165 | 		static const int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0}; | 
 | 166 | 		static const int irq_8list[] = { 7, 5, 3, 4, 9, 0}; | 
 | 167 | 		const int *irqp = wordmode ? irq_16list : irq_8list; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | 		do { | 
 | 169 | 			int irq = *irqp; | 
 | 170 | 			if (request_irq (irq, NULL, 0, "bogus", NULL) != -EBUSY) { | 
 | 171 | 				unsigned long cookie = probe_irq_on(); | 
 | 172 | 				/* Twinkle the interrupt, and check if it's seen. */ | 
 | 173 | 				outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE); | 
 | 174 | 				outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE); | 
 | 175 | 				if (irq == probe_irq_off(cookie)		 /* It's a good IRQ line! */ | 
| Alan Cox | 055e511 | 2008-07-03 23:43:12 -0700 | [diff] [blame] | 176 | 					&& request_irq (irq, eip_interrupt, 0, DRV_NAME, dev) == 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 | 					printk(" selecting IRQ %d.\n", irq); | 
 | 178 | 					dev->irq = *irqp; | 
 | 179 | 					break; | 
 | 180 | 				} | 
 | 181 | 			} | 
 | 182 | 		} while (*++irqp); | 
 | 183 | 		if (*irqp == 0) { | 
 | 184 | 			printk(" no free IRQ lines.\n"); | 
 | 185 | 			retval = -EBUSY; | 
 | 186 | 			goto out; | 
 | 187 | 		} | 
 | 188 | 	} else { | 
 | 189 | 		if (dev->irq == 2) | 
 | 190 | 			dev->irq = 9; | 
| Alan Cox | 055e511 | 2008-07-03 23:43:12 -0700 | [diff] [blame] | 191 | 		if ((retval = request_irq(dev->irq, eip_interrupt, 0, DRV_NAME, dev))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | 			printk (" unable to get IRQ %d.\n", dev->irq); | 
 | 193 | 			goto out; | 
 | 194 | 		} | 
 | 195 | 	} | 
 | 196 |  | 
 | 197 | 	/* Set the base address to point to the NIC, not the "real" base! */ | 
 | 198 | 	dev->base_addr = ioaddr + NIC_OFFSET; | 
| Stephen Hemminger | 4be7ef4 | 2008-11-25 18:24:57 -0800 | [diff] [blame] | 199 | 	dev->netdev_ops = &eip_netdev_ops; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 200 |  | 
 | 201 | 	ei_status.name = name; | 
 | 202 | 	ei_status.word16 = wordmode; | 
 | 203 | 	ei_status.tx_start_page = HP_START_PG; | 
 | 204 | 	ei_status.rx_start_page = HP_START_PG + TX_PAGES; | 
 | 205 | 	ei_status.stop_page = wordmode ? HP_16BSTOP_PG : HP_8BSTOP_PG; | 
 | 206 |  | 
| Joe Perches | c061b18 | 2010-08-23 18:20:03 +0000 | [diff] [blame] | 207 | 	ei_status.reset_8390 = hp_reset_8390; | 
 | 208 | 	ei_status.get_8390_hdr = hp_get_8390_hdr; | 
 | 209 | 	ei_status.block_input = hp_block_input; | 
 | 210 | 	ei_status.block_output = hp_block_output; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 211 | 	hp_init_card(dev); | 
 | 212 |  | 
 | b1fc550 | 2005-05-12 20:11:55 -0400 | [diff] [blame] | 213 | 	retval = register_netdev(dev); | 
 | 214 | 	if (retval) | 
 | 215 | 		goto out1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 | 	return 0; | 
 | b1fc550 | 2005-05-12 20:11:55 -0400 | [diff] [blame] | 217 | out1: | 
 | 218 | 	free_irq(dev->irq, dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 | out: | 
 | 220 | 	release_region(ioaddr, HP_IO_EXTENT); | 
 | 221 | 	return retval; | 
 | 222 | } | 
 | 223 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 224 | static void | 
 | 225 | hp_reset_8390(struct net_device *dev) | 
 | 226 | { | 
 | 227 | 	int hp_base = dev->base_addr - NIC_OFFSET; | 
 | 228 | 	int saved_config = inb_p(hp_base + HP_CONFIGURE); | 
 | 229 |  | 
 | 230 | 	if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies); | 
 | 231 | 	outb_p(0x00, hp_base + HP_CONFIGURE); | 
 | 232 | 	ei_status.txing = 0; | 
 | 233 | 	/* Pause just a few cycles for the hardware reset to take place. */ | 
 | 234 | 	udelay(5); | 
 | 235 |  | 
 | 236 | 	outb_p(saved_config, hp_base + HP_CONFIGURE); | 
 | 237 | 	udelay(5); | 
 | 238 |  | 
 | 239 | 	if ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0) | 
 | 240 | 		printk("%s: hp_reset_8390() did not complete.\n", dev->name); | 
 | 241 |  | 
 | 242 | 	if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 243 | } | 
 | 244 |  | 
 | 245 | static void | 
 | 246 | hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) | 
 | 247 | { | 
 | 248 | 	int nic_base = dev->base_addr; | 
 | 249 | 	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE); | 
 | 250 |  | 
 | 251 | 	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE); | 
 | 252 | 	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base); | 
 | 253 | 	outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); | 
 | 254 | 	outb_p(0, nic_base + EN0_RCNTHI); | 
 | 255 | 	outb_p(0, nic_base + EN0_RSARLO);	/* On page boundary */ | 
 | 256 | 	outb_p(ring_page, nic_base + EN0_RSARHI); | 
 | 257 | 	outb_p(E8390_RREAD+E8390_START, nic_base); | 
 | 258 |  | 
 | 259 | 	if (ei_status.word16) | 
 | 260 | 	  insw(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); | 
 | 261 | 	else | 
 | 262 | 	  insb(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)); | 
 | 263 |  | 
 | 264 | 	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE); | 
 | 265 | } | 
 | 266 |  | 
 | 267 | /* Block input and output, similar to the Crynwr packet driver. If you are | 
 | 268 |    porting to a new ethercard look at the packet driver source for hints. | 
 | 269 |    The HP LAN doesn't use shared memory -- we put the packet | 
 | 270 |    out through the "remote DMA" dataport. */ | 
 | 271 |  | 
 | 272 | static void | 
 | 273 | hp_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) | 
 | 274 | { | 
 | 275 | 	int nic_base = dev->base_addr; | 
 | 276 | 	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE); | 
 | 277 | 	int xfer_count = count; | 
 | 278 | 	char *buf = skb->data; | 
 | 279 |  | 
 | 280 | 	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE); | 
 | 281 | 	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base); | 
 | 282 | 	outb_p(count & 0xff, nic_base + EN0_RCNTLO); | 
 | 283 | 	outb_p(count >> 8, nic_base + EN0_RCNTHI); | 
 | 284 | 	outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); | 
 | 285 | 	outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); | 
 | 286 | 	outb_p(E8390_RREAD+E8390_START, nic_base); | 
 | 287 | 	if (ei_status.word16) { | 
 | 288 | 	  insw(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1); | 
 | 289 | 	  if (count & 0x01) | 
 | 290 | 		buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++; | 
 | 291 | 	} else { | 
 | 292 | 		insb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count); | 
 | 293 | 	} | 
 | 294 | 	/* This is for the ALPHA version only, remove for later releases. */ | 
 | 295 | 	if (ei_debug > 0) {			/* DMA termination address check... */ | 
 | 296 | 	  int high = inb_p(nic_base + EN0_RSARHI); | 
 | 297 | 	  int low = inb_p(nic_base + EN0_RSARLO); | 
 | 298 | 	  int addr = (high << 8) + low; | 
 | 299 | 	  /* Check only the lower 8 bits so we can ignore ring wrap. */ | 
 | 300 | 	  if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff)) | 
 | 301 | 		printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n", | 
 | 302 | 			   dev->name, ring_offset + xfer_count, addr); | 
 | 303 | 	} | 
 | 304 | 	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE); | 
 | 305 | } | 
 | 306 |  | 
 | 307 | static void | 
 | 308 | hp_block_output(struct net_device *dev, int count, | 
 | 309 | 				const unsigned char *buf, int start_page) | 
 | 310 | { | 
 | 311 | 	int nic_base = dev->base_addr; | 
 | 312 | 	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE); | 
 | 313 |  | 
 | 314 | 	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE); | 
 | 315 | 	/* Round the count up for word writes.	Do we need to do this? | 
 | 316 | 	   What effect will an odd byte count have on the 8390? | 
 | 317 | 	   I should check someday. */ | 
 | 318 | 	if (ei_status.word16 && (count & 0x01)) | 
 | 319 | 	  count++; | 
 | 320 | 	/* We should already be in page 0, but to be safe... */ | 
 | 321 | 	outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base); | 
 | 322 |  | 
 | 323 | #ifdef NE8390_RW_BUGFIX | 
 | 324 | 	/* Handle the read-before-write bug the same way as the | 
 | 325 | 	   Crynwr packet driver -- the NatSemi method doesn't work. */ | 
 | 326 | 	outb_p(0x42, nic_base + EN0_RCNTLO); | 
 | 327 | 	outb_p(0,	nic_base + EN0_RCNTHI); | 
 | 328 | 	outb_p(0xff, nic_base + EN0_RSARLO); | 
 | 329 | 	outb_p(0x00, nic_base + EN0_RSARHI); | 
 | 330 | #define NE_CMD	 	0x00 | 
 | 331 | 	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); | 
 | 332 | 	/* Make certain that the dummy read has occurred. */ | 
 | 333 | 	inb_p(0x61); | 
 | 334 | 	inb_p(0x61); | 
 | 335 | #endif | 
 | 336 |  | 
 | 337 | 	outb_p(count & 0xff, nic_base + EN0_RCNTLO); | 
 | 338 | 	outb_p(count >> 8,	 nic_base + EN0_RCNTHI); | 
 | 339 | 	outb_p(0x00, nic_base + EN0_RSARLO); | 
 | 340 | 	outb_p(start_page, nic_base + EN0_RSARHI); | 
 | 341 |  | 
 | 342 | 	outb_p(E8390_RWRITE+E8390_START, nic_base); | 
 | 343 | 	if (ei_status.word16) { | 
 | 344 | 		/* Use the 'rep' sequence for 16 bit boards. */ | 
 | 345 | 		outsw(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1); | 
 | 346 | 	} else { | 
 | 347 | 		outsb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count); | 
 | 348 | 	} | 
 | 349 |  | 
 | 350 | 	/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */ | 
 | 351 |  | 
 | 352 | 	/* This is for the ALPHA version only, remove for later releases. */ | 
 | 353 | 	if (ei_debug > 0) {			/* DMA termination address check... */ | 
 | 354 | 	  int high = inb_p(nic_base + EN0_RSARHI); | 
 | 355 | 	  int low  = inb_p(nic_base + EN0_RSARLO); | 
 | 356 | 	  int addr = (high << 8) + low; | 
 | 357 | 	  if ((start_page << 8) + count != addr) | 
 | 358 | 		printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n", | 
 | 359 | 			   dev->name, (start_page << 8) + count, addr); | 
 | 360 | 	} | 
 | 361 | 	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 362 | } | 
 | 363 |  | 
 | 364 | /* This function resets the ethercard if something screws up. */ | 
| Randy Dunlap | 29f9f6d | 2006-06-10 13:32:12 -0700 | [diff] [blame] | 365 | static void __init | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 366 | hp_init_card(struct net_device *dev) | 
 | 367 | { | 
 | 368 | 	int irq = dev->irq; | 
| Ingo Molnar | f0084a3 | 2008-07-22 09:23:34 +0200 | [diff] [blame] | 369 | 	NS8390p_init(dev, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 370 | 	outb_p(irqmap[irq&0x0f] | HP_RUN, | 
 | 371 | 		   dev->base_addr - NIC_OFFSET + HP_CONFIGURE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 372 | } | 
 | 373 |  | 
 | 374 | #ifdef MODULE | 
 | 375 | #define MAX_HP_CARDS	4	/* Max number of HP cards per module */ | 
 | 376 | static struct net_device *dev_hp[MAX_HP_CARDS]; | 
 | 377 | static int io[MAX_HP_CARDS]; | 
 | 378 | static int irq[MAX_HP_CARDS]; | 
 | 379 |  | 
 | 380 | module_param_array(io, int, NULL, 0); | 
 | 381 | module_param_array(irq, int, NULL, 0); | 
 | 382 | MODULE_PARM_DESC(io, "I/O base address(es)"); | 
 | 383 | MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); | 
 | 384 | MODULE_DESCRIPTION("HP PC-LAN ISA ethernet driver"); | 
 | 385 | MODULE_LICENSE("GPL"); | 
 | 386 |  | 
 | 387 | /* This is set up so that only a single autoprobe takes place per call. | 
 | 388 | ISA device autoprobes on a running machine are not recommended. */ | 
| Randy Dunlap | 29f9f6d | 2006-06-10 13:32:12 -0700 | [diff] [blame] | 389 | int __init | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 390 | init_module(void) | 
 | 391 | { | 
 | 392 | 	struct net_device *dev; | 
 | 393 | 	int this_dev, found = 0; | 
 | 394 |  | 
 | 395 | 	for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) { | 
 | 396 | 		if (io[this_dev] == 0)  { | 
 | 397 | 			if (this_dev != 0) break; /* only autoprobe 1st one */ | 
 | 398 | 			printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n"); | 
 | 399 | 		} | 
| Alan Cox | 055e511 | 2008-07-03 23:43:12 -0700 | [diff] [blame] | 400 | 		dev = alloc_eip_netdev(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 401 | 		if (!dev) | 
 | 402 | 			break; | 
 | 403 | 		dev->irq = irq[this_dev]; | 
 | 404 | 		dev->base_addr = io[this_dev]; | 
 | 405 | 		if (do_hp_probe(dev) == 0) { | 
 | b1fc550 | 2005-05-12 20:11:55 -0400 | [diff] [blame] | 406 | 			dev_hp[found++] = dev; | 
 | 407 | 			continue; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 408 | 		} | 
 | 409 | 		free_netdev(dev); | 
 | 410 | 		printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]); | 
 | 411 | 		break; | 
 | 412 | 	} | 
 | 413 | 	if (found) | 
 | 414 | 		return 0; | 
 | 415 | 	return -ENXIO; | 
 | 416 | } | 
 | 417 |  | 
| Denis Vlasenko | 64916f1 | 2006-01-05 22:45:47 -0800 | [diff] [blame] | 418 | static void cleanup_card(struct net_device *dev) | 
 | 419 | { | 
 | 420 | 	free_irq(dev->irq, dev); | 
 | 421 | 	release_region(dev->base_addr - NIC_OFFSET, HP_IO_EXTENT); | 
 | 422 | } | 
 | 423 |  | 
| Al Viro | afc8eb4 | 2006-06-14 18:50:53 -0400 | [diff] [blame] | 424 | void __exit | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 425 | cleanup_module(void) | 
 | 426 | { | 
 | 427 | 	int this_dev; | 
 | 428 |  | 
 | 429 | 	for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) { | 
 | 430 | 		struct net_device *dev = dev_hp[this_dev]; | 
 | 431 | 		if (dev) { | 
 | 432 | 			unregister_netdev(dev); | 
 | 433 | 			cleanup_card(dev); | 
 | 434 | 			free_netdev(dev); | 
 | 435 | 		} | 
 | 436 | 	} | 
 | 437 | } | 
 | 438 | #endif /* MODULE */ |