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