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