blob: edaaa943eb8f54c026c436e6b62a19a9e3ee3ee8 [file] [log] [blame]
Jouni Malinenff1d2762005-05-12 22:54:16 -04001#define PRISM2_PLX
2
3/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
4 * based on:
5 * - Host AP driver patch from james@madingley.org
6 * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
7 */
8
9
10#include <linux/config.h>
Jouni Malinenff1d2762005-05-12 22:54:16 -040011#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/if.h>
14#include <linux/skbuff.h>
15#include <linux/netdevice.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
27static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
28static char *dev_info = "hostap_plx";
29
30
31MODULE_AUTHOR("Jouni Malinen");
32MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
33 "cards (PLX).");
34MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
35MODULE_LICENSE("GPL");
Jouni Malinenf06ac312005-07-30 12:50:00 -070036MODULE_VERSION(PRISM2_VERSION);
Jouni Malinenff1d2762005-05-12 22:54:16 -040037
38
39static int ignore_cis;
40module_param(ignore_cis, int, 0444);
41MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
42
43
Jouni Malinen67e0e472005-08-14 19:08:41 -070044/* struct local_info::hw_priv */
45struct hostap_plx_priv {
46 void __iomem *attr_mem;
47 unsigned int cor_offset;
48};
49
50
Jouni Malinenff1d2762005-05-12 22:54:16 -040051#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */
52#define COR_SRESET 0x80
53#define COR_LEVLREQ 0x40
54#define COR_ENABLE_FUNC 0x01
55/* PCI Configuration Registers */
56#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */
57/* Local Configuration Registers */
58#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */
59#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
60#define PLX_CNTRL 0x50
61#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
62
63
64#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
65
66static struct pci_device_id prism2_plx_id_table[] __devinitdata = {
67 PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
68 PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
69 PLXDEV(0x126c, 0x8030, "Nortel emobility"),
70 PLXDEV(0x1385, 0x4100, "Netgear MA301"),
71 PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
72 PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
73 PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
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. */
86static 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
109static 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
124static 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
141static 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
156static 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
173static 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
189static 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
224static 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
244static 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
267static void prism2_plx_cor_sreset(local_info_t *local)
268{
269 unsigned char corsave;
Jouni Malinen67e0e472005-08-14 19:08:41 -0700270 struct hostap_plx_priv *hw_priv = local->hw_priv;
Jouni Malinenff1d2762005-05-12 22:54:16 -0400271
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
Jouni Malinen67e0e472005-08-14 19:08:41 -0700277 if (hw_priv->attr_mem == NULL) {
Jouni Malinenff1d2762005-05-12 22:54:16 -0400278 /* TMD7160 - COR at card's first I/O addr */
Jouni Malinen67e0e472005-08-14 19:08:41 -0700279 corsave = inb(hw_priv->cor_offset);
280 outb(corsave | COR_SRESET, hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400281 mdelay(2);
Jouni Malinen67e0e472005-08-14 19:08:41 -0700282 outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400283 mdelay(2);
284 } else {
285 /* PLX9052 */
Jouni Malinen67e0e472005-08-14 19:08:41 -0700286 corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400287 writeb(corsave | COR_SRESET,
Jouni Malinen67e0e472005-08-14 19:08:41 -0700288 hw_priv->attr_mem + hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400289 mdelay(2);
290 writeb(corsave & ~COR_SRESET,
Jouni Malinen67e0e472005-08-14 19:08:41 -0700291 hw_priv->attr_mem + hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400292 mdelay(2);
293 }
294}
295
296
297static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
298{
299 unsigned char corsave;
Jouni Malinen67e0e472005-08-14 19:08:41 -0700300 struct hostap_plx_priv *hw_priv = local->hw_priv;
Jouni Malinenff1d2762005-05-12 22:54:16 -0400301
Jouni Malinen67e0e472005-08-14 19:08:41 -0700302 if (hw_priv->attr_mem == NULL) {
Jouni Malinenff1d2762005-05-12 22:54:16 -0400303 /* TMD7160 - COR at card's first I/O addr */
Jouni Malinen67e0e472005-08-14 19:08:41 -0700304 corsave = inb(hw_priv->cor_offset);
305 outb(corsave | COR_SRESET, hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400306 mdelay(10);
Jouni Malinen67e0e472005-08-14 19:08:41 -0700307 outb(hcr, hw_priv->cor_offset + 2);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400308 mdelay(10);
Jouni Malinen67e0e472005-08-14 19:08:41 -0700309 outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400310 mdelay(10);
311 } else {
312 /* PLX9052 */
Jouni Malinen67e0e472005-08-14 19:08:41 -0700313 corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400314 writeb(corsave | COR_SRESET,
Jouni Malinen67e0e472005-08-14 19:08:41 -0700315 hw_priv->attr_mem + hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400316 mdelay(10);
Jouni Malinen67e0e472005-08-14 19:08:41 -0700317 writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400318 mdelay(10);
319 writeb(corsave & ~COR_SRESET,
Jouni Malinen67e0e472005-08-14 19:08:41 -0700320 hw_priv->attr_mem + hw_priv->cor_offset);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400321 mdelay(10);
322 }
323}
324
325
326static struct prism2_helper_functions prism2_plx_funcs =
327{
328 .card_present = NULL,
329 .cor_sreset = prism2_plx_cor_sreset,
Jouni Malinenff1d2762005-05-12 22:54:16 -0400330 .genesis_reset = prism2_plx_genesis_reset,
331 .hw_type = HOSTAP_HW_PLX,
332};
333
334
335static 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 + cis[pos + 1] >= CIS_MAX_LEN)
367 goto cis_error;
368
369 switch (cis[pos]) {
370 case CISTPL_CONFIG:
Jouni Malinen3a1c42a2006-03-19 19:21:47 -0800371 if (cis[pos + 1] < 2)
Jouni Malinenff1d2762005-05-12 22:54:16 -0400372 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:
Jouni Malinen3a1c42a2006-03-19 19:21:47 -0800393 if (cis[pos + 1] < 5)
Jouni Malinenff1d2762005-05-12 22:54:16 -0400394 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
432static 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, cor_index;
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;
Jouni Malinen67e0e472005-08-14 19:08:41 -0700447 struct hostap_plx_priv *hw_priv;
448
449 hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
450 if (hw_priv == NULL)
451 return -ENOMEM;
452 memset(hw_priv, 0, sizeof(*hw_priv));
Jouni Malinenff1d2762005-05-12 22:54:16 -0400453
454 if (pci_enable_device(pdev))
Jouni Malinen93201992006-03-19 19:21:49 -0800455 goto err_out_free;
Jouni Malinenff1d2762005-05-12 22:54:16 -0400456
457 /* National Datacomm NCP130 based on TMD7160, not PLX9052. */
458 tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
459
460 plx_ioaddr = pci_resource_start(pdev, 1);
461 pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
462
463 if (tmd7160) {
464 /* TMD7160 */
465 attr_mem = NULL; /* no access to PC Card attribute memory */
466
467 printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
468 "irq=%d, pccard_io=0x%x\n",
469 plx_ioaddr, pdev->irq, pccard_ioaddr);
470
471 cor_offset = plx_ioaddr;
472 cor_index = 0x04;
473
474 outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
475 mdelay(1);
476 reg = inb(plx_ioaddr);
477 if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
478 printk(KERN_ERR "%s: Error setting COR (expected="
479 "0x%02x, was=0x%02x)\n", dev_info,
480 cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
481 goto fail;
482 }
483 } else {
484 /* PLX9052 */
485 pccard_attr_mem = pci_resource_start(pdev, 2);
486 pccard_attr_len = pci_resource_len(pdev, 2);
487 if (pccard_attr_len < PLX_MIN_ATTR_LEN)
488 goto fail;
489
490
491 attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
492 if (attr_mem == NULL) {
493 printk(KERN_ERR "%s: cannot remap attr_mem\n",
494 dev_info);
495 goto fail;
496 }
497
498 printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
499 "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
500 pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
501
502 if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
503 &cor_offset, &cor_index)) {
504 printk(KERN_INFO "Unknown PC Card CIS - not a "
505 "Prism2/2.5 card?\n");
506 goto fail;
507 }
508
509 printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
510 "adapter\n");
511
512 /* Write COR to enable PC Card */
513 writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
514 attr_mem + cor_offset);
515
516 /* Enable PCI interrupts if they are not already enabled */
517 reg = inl(plx_ioaddr + PLX_INTCSR);
518 printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
519 if (!(reg & PLX_INTCSR_PCI_INTEN)) {
520 outl(reg | PLX_INTCSR_PCI_INTEN,
521 plx_ioaddr + PLX_INTCSR);
522 if (!(inl(plx_ioaddr + PLX_INTCSR) &
523 PLX_INTCSR_PCI_INTEN)) {
524 printk(KERN_WARNING "%s: Could not enable "
525 "Local Interrupts\n", dev_info);
526 goto fail;
527 }
528 }
529
530 reg = inl(plx_ioaddr + PLX_CNTRL);
531 printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
532 "present=%d)\n",
533 reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
534 /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
535 * not present; but are there really such cards in use(?) */
536 }
537
Dave Hansen0cd545d2005-07-30 12:49:58 -0700538 dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
539 &pdev->dev);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400540 if (dev == NULL)
541 goto fail;
542 iface = netdev_priv(dev);
543 local = iface->local;
Jouni Malinen67e0e472005-08-14 19:08:41 -0700544 local->hw_priv = hw_priv;
Jouni Malinenff1d2762005-05-12 22:54:16 -0400545 cards_found++;
546
547 dev->irq = pdev->irq;
548 dev->base_addr = pccard_ioaddr;
Jouni Malinen67e0e472005-08-14 19:08:41 -0700549 hw_priv->attr_mem = attr_mem;
550 hw_priv->cor_offset = cor_offset;
Jouni Malinenff1d2762005-05-12 22:54:16 -0400551
552 pci_set_drvdata(pdev, dev);
553
554 if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
555 dev)) {
556 printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
557 goto fail;
558 } else
559 irq_registered = 1;
560
561 if (prism2_hw_config(dev, 1)) {
562 printk(KERN_DEBUG "%s: hardware initialization failed\n",
563 dev_info);
564 goto fail;
565 }
566
567 return hostap_hw_ready(dev);
568
569 fail:
Jouni Malinenff1d2762005-05-12 22:54:16 -0400570 if (irq_registered && dev)
571 free_irq(dev->irq, dev);
572
573 if (attr_mem)
574 iounmap(attr_mem);
575
576 pci_disable_device(pdev);
Jouni Malinen93201992006-03-19 19:21:49 -0800577 prism2_free_local_data(dev);
578
579 err_out_free:
580 kfree(hw_priv);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400581
582 return -ENODEV;
583}
584
585
586static void prism2_plx_remove(struct pci_dev *pdev)
587{
588 struct net_device *dev;
589 struct hostap_interface *iface;
Jouni Malinen67e0e472005-08-14 19:08:41 -0700590 struct hostap_plx_priv *hw_priv;
Jouni Malinenff1d2762005-05-12 22:54:16 -0400591
592 dev = pci_get_drvdata(pdev);
593 iface = netdev_priv(dev);
Jouni Malinen67e0e472005-08-14 19:08:41 -0700594 hw_priv = iface->local->hw_priv;
Jouni Malinenff1d2762005-05-12 22:54:16 -0400595
596 /* Reset the hardware, and ensure interrupts are disabled. */
597 prism2_plx_cor_sreset(iface->local);
598 hfa384x_disable_interrupts(dev);
599
Jouni Malinen67e0e472005-08-14 19:08:41 -0700600 if (hw_priv->attr_mem)
601 iounmap(hw_priv->attr_mem);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400602 if (dev->irq)
603 free_irq(dev->irq, dev);
604
Jouni Malinenff1d2762005-05-12 22:54:16 -0400605 prism2_free_local_data(dev);
Jouni Malinenc3551842005-10-02 17:19:00 -0700606 kfree(hw_priv);
Jouni Malinenff1d2762005-05-12 22:54:16 -0400607 pci_disable_device(pdev);
608}
609
610
611MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
612
613static struct pci_driver prism2_plx_drv_id = {
Pavel Roskin7a716532005-09-23 21:58:58 -0700614 .name = "hostap_plx",
Jouni Malinenff1d2762005-05-12 22:54:16 -0400615 .id_table = prism2_plx_id_table,
616 .probe = prism2_plx_probe,
617 .remove = prism2_plx_remove,
618 .suspend = NULL,
619 .resume = NULL,
620 .enable_wake = NULL
621};
622
623
624static int __init init_prism2_plx(void)
625{
626 printk(KERN_INFO "%s: %s\n", dev_info, version);
627
628 return pci_register_driver(&prism2_plx_drv_id);
629}
630
631
632static void __exit exit_prism2_plx(void)
633{
634 pci_unregister_driver(&prism2_plx_drv_id);
635 printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
636}
637
638
639module_init(init_prism2_plx);
640module_exit(exit_prism2_plx);