3 /* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
5 * - Host AP driver patch from james@madingley.org
6 * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
10 #include <linux/config.h>
11 #include <linux/version.h>
12 #include <linux/module.h>
13 #include <linux/init.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>
21 #include <linux/ioport.h>
22 #include <linux/pci.h>
25 #include "hostap_wlan.h"
28 static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
29 static char *dev_info = "hostap_plx";
32 MODULE_AUTHOR("Jouni Malinen");
33 MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
35 MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
36 MODULE_LICENSE("GPL");
39 static int ignore_cis;
40 module_param(ignore_cis, int, 0444);
41 MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
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)
57 #define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
59 static 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"),
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
79 static struct prism2_plx_manfid {
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 */,
100 #ifdef PRISM2_IO_DEBUG
102 static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
104 struct hostap_interface *iface;
108 iface = netdev_priv(dev);
109 local = iface->local;
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);
117 static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
119 struct hostap_interface *iface;
124 iface = netdev_priv(dev);
125 local = iface->local;
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);
134 static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
136 struct hostap_interface *iface;
140 iface = netdev_priv(dev);
141 local = iface->local;
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);
149 static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
151 struct hostap_interface *iface;
156 iface = netdev_priv(dev);
157 local = iface->local;
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);
166 static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
169 struct hostap_interface *iface;
173 iface = netdev_priv(dev);
174 local = iface->local;
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);
182 static inline void hfa384x_insw_debug(struct net_device *dev, int a,
185 struct hostap_interface *iface;
189 iface = netdev_priv(dev);
190 local = iface->local;
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);
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))
205 #else /* PRISM2_IO_DEBUG */
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)
214 #endif /* PRISM2_IO_DEBUG */
217 static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
223 d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
227 HFA384X_INSW(d_off, buf, len / 2);
231 *((char *) pos) = HFA384X_INB(d_off);
237 static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
242 d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
246 HFA384X_OUTSW(d_off, buf, len / 2);
250 HFA384X_OUTB(*((char *) pos), d_off);
256 /* FIX: This might change at some point.. */
257 #include "hostap_hw.c"
260 static void prism2_plx_cor_sreset(local_info_t *local)
262 unsigned char corsave;
264 printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
267 /* Set sreset bit of COR and clear it after hold time */
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);
274 outb(corsave & ~COR_SRESET, local->cor_offset);
278 corsave = readb(local->attr_mem + local->cor_offset);
279 writeb(corsave | COR_SRESET,
280 local->attr_mem + local->cor_offset);
282 writeb(corsave & ~COR_SRESET,
283 local->attr_mem + local->cor_offset);
289 static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
291 unsigned char corsave;
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);
298 outb(hcr, local->cor_offset + 2);
300 outb(corsave & ~COR_SRESET, local->cor_offset);
304 corsave = readb(local->attr_mem + local->cor_offset);
305 writeb(corsave | COR_SRESET,
306 local->attr_mem + local->cor_offset);
308 writeb(hcr, local->attr_mem + local->cor_offset + 2);
310 writeb(corsave & ~COR_SRESET,
311 local->attr_mem + local->cor_offset);
317 static struct prism2_helper_functions prism2_plx_funcs =
319 .card_present = NULL,
320 .cor_sreset = prism2_plx_cor_sreset,
323 .genesis_reset = prism2_plx_genesis_reset,
324 .hw_type = HOSTAP_HW_PLX,
328 static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
329 unsigned int *cor_offset,
330 unsigned int *cor_index)
332 #define CISTPL_CONFIG 0x1A
333 #define CISTPL_MANFID 0x20
334 #define CISTPL_END 0xFF
335 #define CIS_MAX_LEN 256
338 unsigned int rmsz, rasz, manfid1, manfid2;
339 struct prism2_plx_manfid *manfid;
341 cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL);
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]);
351 /* set reasonable defaults for Prism2 cards just in case CIS parsing
355 manfid1 = manfid2 = 0;
358 while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
359 if (pos + cis[pos + 1] >= CIS_MAX_LEN)
364 if (cis[pos + 1] < 1)
366 rmsz = (cis[pos + 2] & 0x3c) >> 2;
367 rasz = cis[pos + 2] & 0x03;
368 if (4 + rasz + rmsz > cis[pos + 1])
370 *cor_index = cis[pos + 3] & 0x3F;
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);
386 if (cis[pos + 1] < 4)
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);
395 pos += cis[pos + 1] + 2;
398 if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
401 for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
402 if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
407 printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
408 " not supported card\n", dev_info, manfid1, manfid2);
412 printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
417 printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
418 "errors during CIS verification\n", dev_info);
425 static int prism2_plx_probe(struct pci_dev *pdev,
426 const struct pci_device_id *id)
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;
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;
441 if (pci_enable_device(pdev))
444 /* National Datacomm NCP130 based on TMD7160, not PLX9052. */
445 tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
447 plx_ioaddr = pci_resource_start(pdev, 1);
448 pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
452 attr_mem = NULL; /* no access to PC Card attribute memory */
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);
458 cor_offset = plx_ioaddr;
461 outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
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);
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)
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",
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);
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");
496 printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
499 /* Write COR to enable PC Card */
500 writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
501 attr_mem + cor_offset);
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);
517 reg = inl(plx_ioaddr + PLX_CNTRL);
518 printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
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(?) */
525 dev = prism2_init_local_data(&prism2_plx_funcs, cards_found);
528 iface = netdev_priv(dev);
529 local = iface->local;
532 dev->irq = pdev->irq;
533 dev->base_addr = pccard_ioaddr;
534 local->attr_mem = attr_mem;
535 local->cor_offset = cor_offset;
537 pci_set_drvdata(pdev, dev);
539 if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
541 printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
546 if (prism2_hw_config(dev, 1)) {
547 printk(KERN_DEBUG "%s: hardware initialization failed\n",
552 return hostap_hw_ready(dev);
555 prism2_free_local_data(dev);
557 if (irq_registered && dev)
558 free_irq(dev->irq, dev);
563 pci_disable_device(pdev);
569 static void prism2_plx_remove(struct pci_dev *pdev)
571 struct net_device *dev;
572 struct hostap_interface *iface;
574 dev = pci_get_drvdata(pdev);
575 iface = netdev_priv(dev);
577 /* Reset the hardware, and ensure interrupts are disabled. */
578 prism2_plx_cor_sreset(iface->local);
579 hfa384x_disable_interrupts(dev);
581 if (iface->local->attr_mem)
582 iounmap(iface->local->attr_mem);
584 free_irq(dev->irq, dev);
586 prism2_free_local_data(dev);
587 pci_disable_device(pdev);
591 MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
593 static 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,
604 static int __init init_prism2_plx(void)
606 printk(KERN_INFO "%s: %s\n", dev_info, version);
608 return pci_register_driver(&prism2_plx_drv_id);
612 static void __exit exit_prism2_plx(void)
614 pci_unregister_driver(&prism2_plx_drv_id);
615 printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
619 module_init(init_prism2_plx);
620 module_exit(exit_prism2_plx);