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