1 // SPDX-License-Identifier: GPL-2.0-only
2 #define PRISM2_PLX
3 
4 /* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
5  * based on:
6  * - Host AP driver patch from james@madingley.org
7  * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
8  */
9 
10 
11 #include <linux/module.h>
12 #include <linux/if.h>
13 #include <linux/skbuff.h>
14 #include <linux/netdevice.h>
15 #include <linux/slab.h>
16 #include <linux/workqueue.h>
17 #include <linux/wireless.h>
18 #include <net/iw_handler.h>
19 
20 #include <linux/ioport.h>
21 #include <linux/pci.h>
22 #include <asm/io.h>
23 
24 #include "hostap_wlan.h"
25 
26 
27 static char *dev_info = "hostap_plx";
28 
29 
30 MODULE_AUTHOR("Jouni Malinen");
31 MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
32 		   "cards (PLX).");
33 MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
34 MODULE_LICENSE("GPL");
35 
36 
37 static int ignore_cis;
38 module_param(ignore_cis, int, 0444);
39 MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
40 
41 
42 /* struct local_info::hw_priv */
43 struct hostap_plx_priv {
44 	void __iomem *attr_mem;
45 	unsigned int cor_offset;
46 };
47 
48 
49 #define PLX_MIN_ATTR_LEN 512	/* at least 2 x 256 is needed for CIS */
50 #define COR_SRESET       0x80
51 #define COR_LEVLREQ      0x40
52 #define COR_ENABLE_FUNC  0x01
53 /* PCI Configuration Registers */
54 #define PLX_PCIIPR       0x3d   /* PCI Interrupt Pin */
55 /* Local Configuration Registers */
56 #define PLX_INTCSR       0x4c   /* Interrupt Control/Status Register */
57 #define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
58 #define PLX_CNTRL        0x50
59 #define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
60 
61 
62 #define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
63 
64 static const struct pci_device_id prism2_plx_id_table[] = {
65 	PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
66 	PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
67 	PLXDEV(0x126c, 0x8030, "Nortel emobility"),
68 	PLXDEV(0x1562, 0x0001, "Symbol LA-4123"),
69 	PLXDEV(0x1385, 0x4100, "Netgear MA301"),
70 	PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
71 	PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
72 	PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
73 	PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"),
74 	PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
75 	PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
76 	PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
77 	PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"),
78 	PLXDEV(0xec80, 0xec00, "Belkin F5D6000"),
79 	{ 0 }
80 };
81 
82 
83 /* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid
84  * is not listed here, you will need to add it here to get the driver
85  * initialized. */
86 static struct prism2_plx_manfid {
87 	u16 manfid1, manfid2;
88 } prism2_plx_known_manfids[] = {
89 	{ 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */,
90 	{ 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */,
91 	{ 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */,
92 	{ 0x0126, 0x8000 } /* Proxim RangeLAN */,
93 	{ 0x0138, 0x0002 } /* Compaq WL100 */,
94 	{ 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */,
95 	{ 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */,
96 	{ 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */,
97 	{ 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */,
98 	{ 0x028a, 0x0002 } /* D-Link DRC-650 */,
99 	{ 0x0250, 0x0002 } /* Samsung SWL2000-N */,
100 	{ 0xc250, 0x0002 } /* EMTAC A2424i */,
101 	{ 0xd601, 0x0002 } /* Z-Com XI300 */,
102 	{ 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */,
103 	{ 0, 0}
104 };
105 
106 
107 #ifdef PRISM2_IO_DEBUG
108 
109 static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
110 {
111 	struct hostap_interface *iface;
112 	local_info_t *local;
113 	unsigned long flags;
114 
115 	iface = netdev_priv(dev);
116 	local = iface->local;
117 
118 	spin_lock_irqsave(&local->lock, flags);
119 	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
120 	outb(v, dev->base_addr + a);
121 	spin_unlock_irqrestore(&local->lock, flags);
122 }
123 
124 static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
125 {
126 	struct hostap_interface *iface;
127 	local_info_t *local;
128 	unsigned long flags;
129 	u8 v;
130 
131 	iface = netdev_priv(dev);
132 	local = iface->local;
133 
134 	spin_lock_irqsave(&local->lock, flags);
135 	v = inb(dev->base_addr + a);
136 	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
137 	spin_unlock_irqrestore(&local->lock, flags);
138 	return v;
139 }
140 
141 static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
142 {
143 	struct hostap_interface *iface;
144 	local_info_t *local;
145 	unsigned long flags;
146 
147 	iface = netdev_priv(dev);
148 	local = iface->local;
149 
150 	spin_lock_irqsave(&local->lock, flags);
151 	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
152 	outw(v, dev->base_addr + a);
153 	spin_unlock_irqrestore(&local->lock, flags);
154 }
155 
156 static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
157 {
158 	struct hostap_interface *iface;
159 	local_info_t *local;
160 	unsigned long flags;
161 	u16 v;
162 
163 	iface = netdev_priv(dev);
164 	local = iface->local;
165 
166 	spin_lock_irqsave(&local->lock, flags);
167 	v = inw(dev->base_addr + a);
168 	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
169 	spin_unlock_irqrestore(&local->lock, flags);
170 	return v;
171 }
172 
173 static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
174 				       u8 *buf, int wc)
175 {
176 	struct hostap_interface *iface;
177 	local_info_t *local;
178 	unsigned long flags;
179 
180 	iface = netdev_priv(dev);
181 	local = iface->local;
182 
183 	spin_lock_irqsave(&local->lock, flags);
184 	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
185 	outsw(dev->base_addr + a, buf, wc);
186 	spin_unlock_irqrestore(&local->lock, flags);
187 }
188 
189 static inline void hfa384x_insw_debug(struct net_device *dev, int a,
190 				      u8 *buf, int wc)
191 {
192 	struct hostap_interface *iface;
193 	local_info_t *local;
194 	unsigned long flags;
195 
196 	iface = netdev_priv(dev);
197 	local = iface->local;
198 
199 	spin_lock_irqsave(&local->lock, flags);
200 	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
201 	insw(dev->base_addr + a, buf, wc);
202 	spin_unlock_irqrestore(&local->lock, flags);
203 }
204 
205 #define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
206 #define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
207 #define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
208 #define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
209 #define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
210 #define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
211 
212 #else /* PRISM2_IO_DEBUG */
213 
214 #define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
215 #define HFA384X_INB(a) inb(dev->base_addr + (a))
216 #define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
217 #define HFA384X_INW(a) inw(dev->base_addr + (a))
218 #define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
219 #define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
220 
221 #endif /* PRISM2_IO_DEBUG */
222 
223 
224 static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
225 			    int len)
226 {
227 	u16 d_off;
228 	u16 *pos;
229 
230 	d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
231 	pos = (u16 *) buf;
232 
233 	if (len / 2)
234 		HFA384X_INSW(d_off, buf, len / 2);
235 	pos += len / 2;
236 
237 	if (len & 1)
238 		*((char *) pos) = HFA384X_INB(d_off);
239 
240 	return 0;
241 }
242 
243 
244 static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
245 {
246 	u16 d_off;
247 	u16 *pos;
248 
249 	d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
250 	pos = (u16 *) buf;
251 
252 	if (len / 2)
253 		HFA384X_OUTSW(d_off, buf, len / 2);
254 	pos += len / 2;
255 
256 	if (len & 1)
257 		HFA384X_OUTB(*((char *) pos), d_off);
258 
259 	return 0;
260 }
261 
262 
263 /* FIX: This might change at some point.. */
264 #include "hostap_hw.c"
265 
266 
267 static void prism2_plx_cor_sreset(local_info_t *local)
268 {
269 	unsigned char corsave;
270 	struct hostap_plx_priv *hw_priv = local->hw_priv;
271 
272 	printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
273 	       dev_info);
274 
275 	/* Set sreset bit of COR and clear it after hold time */
276 
277 	if (hw_priv->attr_mem == NULL) {
278 		/* TMD7160 - COR at card's first I/O addr */
279 		corsave = inb(hw_priv->cor_offset);
280 		outb(corsave | COR_SRESET, hw_priv->cor_offset);
281 		mdelay(2);
282 		outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
283 		mdelay(2);
284 	} else {
285 		/* PLX9052 */
286 		corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
287 		writeb(corsave | COR_SRESET,
288 		       hw_priv->attr_mem + hw_priv->cor_offset);
289 		mdelay(2);
290 		writeb(corsave & ~COR_SRESET,
291 		       hw_priv->attr_mem + hw_priv->cor_offset);
292 		mdelay(2);
293 	}
294 }
295 
296 
297 static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
298 {
299 	unsigned char corsave;
300 	struct hostap_plx_priv *hw_priv = local->hw_priv;
301 
302 	if (hw_priv->attr_mem == NULL) {
303 		/* TMD7160 - COR at card's first I/O addr */
304 		corsave = inb(hw_priv->cor_offset);
305 		outb(corsave | COR_SRESET, hw_priv->cor_offset);
306 		mdelay(10);
307 		outb(hcr, hw_priv->cor_offset + 2);
308 		mdelay(10);
309 		outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
310 		mdelay(10);
311 	} else {
312 		/* PLX9052 */
313 		corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
314 		writeb(corsave | COR_SRESET,
315 		       hw_priv->attr_mem + hw_priv->cor_offset);
316 		mdelay(10);
317 		writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
318 		mdelay(10);
319 		writeb(corsave & ~COR_SRESET,
320 		       hw_priv->attr_mem + hw_priv->cor_offset);
321 		mdelay(10);
322 	}
323 }
324 
325 
326 static struct prism2_helper_functions prism2_plx_funcs =
327 {
328 	.card_present	= NULL,
329 	.cor_sreset	= prism2_plx_cor_sreset,
330 	.genesis_reset	= prism2_plx_genesis_reset,
331 	.hw_type	= HOSTAP_HW_PLX,
332 };
333 
334 
335 static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
336 				unsigned int *cor_offset,
337 				unsigned int *cor_index)
338 {
339 #define CISTPL_CONFIG 0x1A
340 #define CISTPL_MANFID 0x20
341 #define CISTPL_END 0xFF
342 #define CIS_MAX_LEN 256
343 	u8 *cis;
344 	int i, pos;
345 	unsigned int rmsz, rasz, manfid1, manfid2;
346 	struct prism2_plx_manfid *manfid;
347 
348 	cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL);
349 	if (cis == NULL)
350 		return -ENOMEM;
351 
352 	/* read CIS; it is in even offsets in the beginning of attr_mem */
353 	for (i = 0; i < CIS_MAX_LEN; i++)
354 		cis[i] = readb(attr_mem + 2 * i);
355 	printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n",
356 	       dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]);
357 
358 	/* set reasonable defaults for Prism2 cards just in case CIS parsing
359 	 * fails */
360 	*cor_offset = 0x3e0;
361 	*cor_index = 0x01;
362 	manfid1 = manfid2 = 0;
363 
364 	pos = 0;
365 	while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
366 		if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN)
367 			goto cis_error;
368 
369 		switch (cis[pos]) {
370 		case CISTPL_CONFIG:
371 			if (cis[pos + 1] < 2)
372 				goto cis_error;
373 			rmsz = (cis[pos + 2] & 0x3c) >> 2;
374 			rasz = cis[pos + 2] & 0x03;
375 			if (4 + rasz + rmsz > cis[pos + 1])
376 				goto cis_error;
377 			*cor_index = cis[pos + 3] & 0x3F;
378 			*cor_offset = 0;
379 			for (i = 0; i <= rasz; i++)
380 				*cor_offset += cis[pos + 4 + i] << (8 * i);
381 			printk(KERN_DEBUG "%s: cor_index=0x%x "
382 			       "cor_offset=0x%x\n", dev_info,
383 			       *cor_index, *cor_offset);
384 			if (*cor_offset > attr_len) {
385 				printk(KERN_ERR "%s: COR offset not within "
386 				       "attr_mem\n", dev_info);
387 				kfree(cis);
388 				return -1;
389 			}
390 			break;
391 
392 		case CISTPL_MANFID:
393 			if (cis[pos + 1] < 4)
394 				goto cis_error;
395 			manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
396 			manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
397 			printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
398 			       dev_info, manfid1, manfid2);
399 			break;
400 		}
401 
402 		pos += cis[pos + 1] + 2;
403 	}
404 
405 	if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
406 		goto cis_error;
407 
408 	for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
409 		if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
410 			kfree(cis);
411 			return 0;
412 		}
413 
414 	printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
415 	       " not supported card\n", dev_info, manfid1, manfid2);
416 	goto fail;
417 
418  cis_error:
419 	printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
420 
421  fail:
422 	kfree(cis);
423 	if (ignore_cis) {
424 		printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
425 		       "errors during CIS verification\n", dev_info);
426 		return 0;
427 	}
428 	return -1;
429 }
430 
431 
432 static int prism2_plx_probe(struct pci_dev *pdev,
433 			    const struct pci_device_id *id)
434 {
435 	unsigned int pccard_ioaddr, plx_ioaddr;
436 	unsigned long pccard_attr_mem;
437 	unsigned int pccard_attr_len;
438 	void __iomem *attr_mem = NULL;
439 	unsigned int cor_offset = 0, cor_index = 0;
440 	u32 reg;
441 	local_info_t *local = NULL;
442 	struct net_device *dev = NULL;
443 	struct hostap_interface *iface;
444 	static int cards_found /* = 0 */;
445 	int irq_registered = 0;
446 	int tmd7160;
447 	struct hostap_plx_priv *hw_priv;
448 
449 	hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
450 	if (hw_priv == NULL)
451 		return -ENOMEM;
452 
453 	if (pci_enable_device(pdev))
454 		goto err_out_free;
455 
456 	/* National Datacomm NCP130 based on TMD7160, not PLX9052. */
457 	tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
458 
459 	plx_ioaddr = pci_resource_start(pdev, 1);
460 	pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
461 
462 	if (tmd7160) {
463 		/* TMD7160 */
464 		attr_mem = NULL; /* no access to PC Card attribute memory */
465 
466 		printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
467 		       "irq=%d, pccard_io=0x%x\n",
468 		       plx_ioaddr, pdev->irq, pccard_ioaddr);
469 
470 		cor_offset = plx_ioaddr;
471 		cor_index = 0x04;
472 
473 		outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
474 		mdelay(1);
475 		reg = inb(plx_ioaddr);
476 		if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
477 			printk(KERN_ERR "%s: Error setting COR (expected="
478 			       "0x%02x, was=0x%02x)\n", dev_info,
479 			       cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
480 			goto fail;
481 		}
482 	} else {
483 		/* PLX9052 */
484 		pccard_attr_mem = pci_resource_start(pdev, 2);
485 		pccard_attr_len = pci_resource_len(pdev, 2);
486 		if (pccard_attr_len < PLX_MIN_ATTR_LEN)
487 			goto fail;
488 
489 
490 		attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
491 		if (attr_mem == NULL) {
492 			printk(KERN_ERR "%s: cannot remap attr_mem\n",
493 			       dev_info);
494 			goto fail;
495 		}
496 
497 		printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
498 		       "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
499 		       pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
500 
501 		if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
502 					 &cor_offset, &cor_index)) {
503 			printk(KERN_INFO "Unknown PC Card CIS - not a "
504 			       "Prism2/2.5 card?\n");
505 			goto fail;
506 		}
507 
508 		printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
509 		       "adapter\n");
510 
511 		/* Write COR to enable PC Card */
512 		writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
513 		       attr_mem + cor_offset);
514 
515 		/* Enable PCI interrupts if they are not already enabled */
516 		reg = inl(plx_ioaddr + PLX_INTCSR);
517 		printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
518 		if (!(reg & PLX_INTCSR_PCI_INTEN)) {
519 			outl(reg | PLX_INTCSR_PCI_INTEN,
520 			     plx_ioaddr + PLX_INTCSR);
521 			if (!(inl(plx_ioaddr + PLX_INTCSR) &
522 			      PLX_INTCSR_PCI_INTEN)) {
523 				printk(KERN_WARNING "%s: Could not enable "
524 				       "Local Interrupts\n", dev_info);
525 				goto fail;
526 			}
527 		}
528 
529 		reg = inl(plx_ioaddr + PLX_CNTRL);
530 		printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
531 		       "present=%d)\n",
532 		       reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
533 		/* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
534 		 * not present; but are there really such cards in use(?) */
535 	}
536 
537 	dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
538 				     &pdev->dev);
539 	if (dev == NULL)
540 		goto fail;
541 	iface = netdev_priv(dev);
542 	local = iface->local;
543 	local->hw_priv = hw_priv;
544 	cards_found++;
545 
546 	dev->irq = pdev->irq;
547 	dev->base_addr = pccard_ioaddr;
548 	hw_priv->attr_mem = attr_mem;
549 	hw_priv->cor_offset = cor_offset;
550 
551 	pci_set_drvdata(pdev, dev);
552 
553 	if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
554 			dev)) {
555 		printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
556 		goto fail;
557 	} else
558 		irq_registered = 1;
559 
560 	if (prism2_hw_config(dev, 1)) {
561 		printk(KERN_DEBUG "%s: hardware initialization failed\n",
562 		       dev_info);
563 		goto fail;
564 	}
565 
566 	return hostap_hw_ready(dev);
567 
568  fail:
569 	if (irq_registered && dev)
570 		free_irq(dev->irq, dev);
571 
572 	if (attr_mem)
573 		iounmap(attr_mem);
574 
575 	pci_disable_device(pdev);
576 	prism2_free_local_data(dev);
577 
578  err_out_free:
579 	kfree(hw_priv);
580 
581 	return -ENODEV;
582 }
583 
584 
585 static void prism2_plx_remove(struct pci_dev *pdev)
586 {
587 	struct net_device *dev;
588 	struct hostap_interface *iface;
589 	struct hostap_plx_priv *hw_priv;
590 
591 	dev = pci_get_drvdata(pdev);
592 	iface = netdev_priv(dev);
593 	hw_priv = iface->local->hw_priv;
594 
595 	/* Reset the hardware, and ensure interrupts are disabled. */
596 	prism2_plx_cor_sreset(iface->local);
597 	hfa384x_disable_interrupts(dev);
598 
599 	if (hw_priv->attr_mem)
600 		iounmap(hw_priv->attr_mem);
601 	if (dev->irq)
602 		free_irq(dev->irq, dev);
603 
604 	prism2_free_local_data(dev);
605 	kfree(hw_priv);
606 	pci_disable_device(pdev);
607 }
608 
609 
610 MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
611 
612 static struct pci_driver prism2_plx_driver = {
613 	.name		= "hostap_plx",
614 	.id_table	= prism2_plx_id_table,
615 	.probe		= prism2_plx_probe,
616 	.remove		= prism2_plx_remove,
617 };
618 
619 module_pci_driver(prism2_plx_driver);
620