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