xref: /openbmc/linux/drivers/net/wireless/intersil/hostap/hostap_plx.c (revision 942baad211336efefb93a8369478888ab845c450)
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