Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* orinoco_plx.c |
2 | * | |
3 | * Driver for Prism II devices which would usually be driven by orinoco_cs, | |
4 | * but are connected to the PCI bus by a PLX9052. | |
5 | * | |
6 | * Current maintainers (as of 29 September 2003) are: | |
7 | * Pavel Roskin <proski AT gnu.org> | |
8 | * and David Gibson <hermes AT gibson.dropbear.id.au> | |
9 | * | |
10 | * (C) Copyright David Gibson, IBM Corp. 2001-2003. | |
11 | * Copyright (C) 2001 Daniel Barlow | |
12 | * | |
13 | * The contents of this file are subject to the Mozilla Public License | |
14 | * Version 1.1 (the "License"); you may not use this file except in | |
15 | * compliance with the License. You may obtain a copy of the License | |
16 | * at http://www.mozilla.org/MPL/ | |
17 | * | |
18 | * Software distributed under the License is distributed on an "AS IS" | |
19 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | |
20 | * the License for the specific language governing rights and | |
21 | * limitations under the License. | |
22 | * | |
23 | * Alternatively, the contents of this file may be used under the | |
24 | * terms of the GNU General Public License version 2 (the "GPL"), in | |
25 | * which case the provisions of the GPL are applicable instead of the | |
26 | * above. If you wish to allow the use of your version of this file | |
27 | * only under the terms of the GPL and not to allow others to use your | |
28 | * version of this file under the MPL, indicate your decision by | |
29 | * deleting the provisions above and replace them with the notice and | |
30 | * other provisions required by the GPL. If you do not delete the | |
31 | * provisions above, a recipient may use your version of this file | |
32 | * under either the MPL or the GPL. | |
33 | ||
34 | * Caution: this is experimental and probably buggy. For success and | |
35 | * failure reports for different cards and adaptors, see | |
36 | * orinoco_plx_pci_id_table near the end of the file. If you have a | |
37 | * card we don't have the PCI id for, and looks like it should work, | |
38 | * drop me mail with the id and "it works"/"it doesn't work". | |
39 | * | |
40 | * Note: if everything gets detected fine but it doesn't actually send | |
41 | * or receive packets, your first port of call should probably be to | |
42 | * try newer firmware in the card. Especially if you're doing Ad-Hoc | |
43 | * modes. | |
44 | * | |
45 | * The actual driving is done by orinoco.c, this is just resource | |
46 | * allocation stuff. The explanation below is courtesy of Ryan Niemi | |
47 | * on the linux-wlan-ng list at | |
48 | * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html | |
49 | * | |
50 | * The PLX9052-based cards (WL11000 and several others) are a | |
51 | * different beast than the usual PCMCIA-based PRISM2 configuration | |
52 | * expected by wlan-ng. Here's the general details on how the WL11000 | |
53 | * PCI adapter works: | |
54 | * | |
55 | * - Two PCI I/O address spaces, one 0x80 long which contains the | |
56 | * PLX9052 registers, and one that's 0x40 long mapped to the PCMCIA | |
57 | * slot I/O address space. | |
58 | * | |
59 | * - One PCI memory address space, mapped to the PCMCIA memory space | |
60 | * (containing the CIS). | |
61 | * | |
62 | * After identifying the I/O and memory space, you can read through | |
63 | * the memory space to confirm the CIS's device ID or manufacturer ID | |
64 | * to make sure it's the expected card. qKeep in mind that the PCMCIA | |
65 | * spec specifies the CIS as the lower 8 bits of each word read from | |
66 | * the CIS, so to read the bytes of the CIS, read every other byte | |
67 | * (0,2,4,...). Passing that test, you need to enable the I/O address | |
68 | * space on the PCMCIA card via the PCMCIA COR register. This is the | |
69 | * first byte following the CIS. In my case (which may not have any | |
70 | * relation to what's on the PRISM2 cards), COR was at offset 0x800 | |
71 | * within the PCI memory space. Write 0x41 to the COR register to | |
72 | * enable I/O mode and to select level triggered interrupts. To | |
73 | * confirm you actually succeeded, read the COR register back and make | |
74 | * sure it actually got set to 0x41, incase you have an unexpected | |
75 | * card inserted. | |
76 | * | |
77 | * Following that, you can treat the second PCI I/O address space (the | |
78 | * one that's not 0x80 in length) as the PCMCIA I/O space. | |
79 | * | |
80 | * Note that in the Eumitcom's source for their drivers, they register | |
81 | * the interrupt as edge triggered when registering it with the | |
82 | * Windows kernel. I don't recall how to register edge triggered on | |
83 | * Linux (if it can be done at all). But in some experimentation, I | |
84 | * don't see much operational difference between using either | |
85 | * interrupt mode. Don't mess with the interrupt mode in the COR | |
86 | * register though, as the PLX9052 wants level triggers with the way | |
87 | * the serial EEPROM configures it on the WL11000. | |
88 | * | |
89 | * There's some other little quirks related to timing that I bumped | |
90 | * into, but I don't recall right now. Also, there's two variants of | |
91 | * the WL11000 I've seen, revision A1 and T2. These seem to differ | |
92 | * slightly in the timings configured in the wait-state generator in | |
93 | * the PLX9052. There have also been some comments from Eumitcom that | |
94 | * cards shouldn't be hot swapped, apparently due to risk of cooking | |
95 | * the PLX9052. I'm unsure why they believe this, as I can't see | |
96 | * anything in the design that would really cause a problem, except | |
97 | * for crashing drivers not written to expect it. And having developed | |
98 | * drivers for the WL11000, I'd say it's quite tricky to write code | |
99 | * that will successfully deal with a hot unplug. Very odd things | |
100 | * happen on the I/O side of things. But anyway, be warned. Despite | |
101 | * that, I've hot-swapped a number of times during debugging and | |
102 | * driver development for various reasons (stuck WAIT# line after the | |
103 | * radio card's firmware locks up). | |
104 | * | |
105 | * Hope this is enough info for someone to add PLX9052 support to the | |
106 | * wlan-ng card. In the case of the WL11000, the PCI ID's are | |
107 | * 0x1639/0x0200, with matching subsystem ID's. Other PLX9052-based | |
108 | * manufacturers other than Eumitcom (or on cards other than the | |
109 | * WL11000) may have different PCI ID's. | |
110 | * | |
111 | * If anyone needs any more specific info, let me know. I haven't had | |
112 | * time to implement support myself yet, and with the way things are | |
113 | * going, might not have time for a while.. | |
114 | */ | |
115 | ||
116 | #define DRIVER_NAME "orinoco_plx" | |
117 | #define PFX DRIVER_NAME ": " | |
118 | ||
119 | #include <linux/config.h> | |
1da177e4 LT |
120 | #include <linux/module.h> |
121 | #include <linux/kernel.h> | |
122 | #include <linux/init.h> | |
ef846bf0 | 123 | #include <linux/delay.h> |
1da177e4 | 124 | #include <linux/pci.h> |
1da177e4 LT |
125 | #include <pcmcia/cisreg.h> |
126 | ||
1da177e4 LT |
127 | #include "orinoco.h" |
128 | ||
129 | #define COR_OFFSET (0x3e0) /* COR attribute offset of Prism2 PC card */ | |
130 | #define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ | |
131 | #define COR_RESET (0x80) /* reset bit in the COR register */ | |
132 | #define PLX_RESET_TIME (500) /* milliseconds */ | |
133 | ||
134 | #define PLX_INTCSR 0x4c /* Interrupt Control & Status Register */ | |
135 | #define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */ | |
136 | ||
137 | static const u8 cis_magic[] = { | |
138 | 0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67 | |
139 | }; | |
140 | ||
141 | /* Orinoco PLX specific data */ | |
142 | struct orinoco_plx_card { | |
143 | void __iomem *attr_mem; | |
144 | }; | |
145 | ||
146 | /* | |
147 | * Do a soft reset of the card using the Configuration Option Register | |
148 | */ | |
149 | static int orinoco_plx_cor_reset(struct orinoco_private *priv) | |
150 | { | |
151 | hermes_t *hw = &priv->hw; | |
152 | struct orinoco_plx_card *card = priv->card; | |
153 | u8 __iomem *attr_mem = card->attr_mem; | |
154 | unsigned long timeout; | |
155 | u16 reg; | |
156 | ||
157 | writeb(COR_VALUE | COR_RESET, attr_mem + COR_OFFSET); | |
158 | mdelay(1); | |
159 | ||
160 | writeb(COR_VALUE, attr_mem + COR_OFFSET); | |
161 | mdelay(1); | |
162 | ||
163 | /* Just in case, wait more until the card is no longer busy */ | |
164 | timeout = jiffies + (PLX_RESET_TIME * HZ / 1000); | |
165 | reg = hermes_read_regn(hw, CMD); | |
166 | while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { | |
167 | mdelay(1); | |
168 | reg = hermes_read_regn(hw, CMD); | |
169 | } | |
170 | ||
171 | /* Did we timeout ? */ | |
172 | if (reg & HERMES_CMD_BUSY) { | |
173 | printk(KERN_ERR PFX "Busy timeout\n"); | |
174 | return -ETIMEDOUT; | |
175 | } | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | ||
181 | static int orinoco_plx_init_one(struct pci_dev *pdev, | |
182 | const struct pci_device_id *ent) | |
183 | { | |
184 | int err = 0; | |
185 | u8 __iomem *attr_mem = NULL; | |
186 | u32 csr_reg, plx_addr; | |
187 | struct orinoco_private *priv = NULL; | |
188 | struct orinoco_plx_card *card; | |
189 | unsigned long pccard_ioaddr = 0; | |
190 | unsigned long pccard_iolen = 0; | |
191 | struct net_device *dev = NULL; | |
192 | void __iomem *mem; | |
193 | int i; | |
194 | ||
195 | err = pci_enable_device(pdev); | |
196 | if (err) { | |
197 | printk(KERN_ERR PFX "Cannot enable PCI device\n"); | |
198 | return err; | |
199 | } | |
200 | ||
201 | err = pci_request_regions(pdev, DRIVER_NAME); | |
202 | if (err != 0) { | |
203 | printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); | |
204 | goto fail_resources; | |
205 | } | |
206 | ||
207 | /* Resource 1 is mapped to PLX-specific registers */ | |
208 | plx_addr = pci_resource_start(pdev, 1); | |
209 | ||
210 | /* Resource 2 is mapped to the PCMCIA attribute memory */ | |
211 | attr_mem = ioremap(pci_resource_start(pdev, 2), | |
212 | pci_resource_len(pdev, 2)); | |
213 | if (!attr_mem) { | |
214 | printk(KERN_ERR PFX "Cannot remap PCMCIA space\n"); | |
215 | goto fail_map_attr; | |
216 | } | |
217 | ||
218 | /* Resource 3 is mapped to the PCMCIA I/O address space */ | |
219 | pccard_ioaddr = pci_resource_start(pdev, 3); | |
220 | pccard_iolen = pci_resource_len(pdev, 3); | |
221 | ||
222 | mem = pci_iomap(pdev, 3, 0); | |
223 | if (!mem) { | |
224 | err = -ENOMEM; | |
225 | goto fail_map_io; | |
226 | } | |
227 | ||
228 | /* Allocate network device */ | |
229 | dev = alloc_orinocodev(sizeof(*card), orinoco_plx_cor_reset); | |
230 | if (!dev) { | |
231 | printk(KERN_ERR PFX "Cannot allocate network device\n"); | |
232 | err = -ENOMEM; | |
233 | goto fail_alloc; | |
234 | } | |
235 | ||
236 | priv = netdev_priv(dev); | |
237 | card = priv->card; | |
238 | card->attr_mem = attr_mem; | |
239 | dev->base_addr = pccard_ioaddr; | |
240 | SET_MODULE_OWNER(dev); | |
241 | SET_NETDEV_DEV(dev, &pdev->dev); | |
242 | ||
243 | hermes_struct_init(&priv->hw, mem, HERMES_16BIT_REGSPACING); | |
244 | ||
245 | printk(KERN_DEBUG PFX "Detected Orinoco/Prism2 PLX device " | |
246 | "at %s irq:%d, io addr:0x%lx\n", pci_name(pdev), pdev->irq, | |
247 | pccard_ioaddr); | |
248 | ||
249 | err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, | |
250 | dev->name, dev); | |
251 | if (err) { | |
252 | printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); | |
253 | err = -EBUSY; | |
254 | goto fail_irq; | |
255 | } | |
256 | dev->irq = pdev->irq; | |
257 | ||
258 | /* bjoern: We need to tell the card to enable interrupts, in | |
259 | case the serial eprom didn't do this already. See the | |
260 | PLX9052 data book, p8-1 and 8-24 for reference. */ | |
261 | csr_reg = inl(plx_addr + PLX_INTCSR); | |
262 | if (!(csr_reg & PLX_INTCSR_INTEN)) { | |
263 | csr_reg |= PLX_INTCSR_INTEN; | |
264 | outl(csr_reg, plx_addr + PLX_INTCSR); | |
265 | csr_reg = inl(plx_addr + PLX_INTCSR); | |
266 | if (!(csr_reg & PLX_INTCSR_INTEN)) { | |
267 | printk(KERN_ERR PFX "Cannot enable interrupts\n"); | |
268 | goto fail; | |
269 | } | |
270 | } | |
271 | ||
272 | err = orinoco_plx_cor_reset(priv); | |
273 | if (err) { | |
274 | printk(KERN_ERR PFX "Initial reset failed\n"); | |
275 | goto fail; | |
276 | } | |
277 | ||
278 | printk(KERN_DEBUG PFX "CIS: "); | |
279 | for (i = 0; i < 16; i++) { | |
280 | printk("%02X:", readb(attr_mem + 2*i)); | |
281 | } | |
282 | printk("\n"); | |
283 | ||
284 | /* Verify whether a supported PC card is present */ | |
285 | /* FIXME: we probably need to be smarted about this */ | |
286 | for (i = 0; i < sizeof(cis_magic); i++) { | |
287 | if (cis_magic[i] != readb(attr_mem +2*i)) { | |
288 | printk(KERN_ERR PFX "The CIS value of Prism2 PC " | |
289 | "card is unexpected\n"); | |
290 | err = -EIO; | |
291 | goto fail; | |
292 | } | |
293 | } | |
294 | ||
295 | err = register_netdev(dev); | |
296 | if (err) { | |
297 | printk(KERN_ERR PFX "Cannot register network device\n"); | |
298 | goto fail; | |
299 | } | |
300 | ||
301 | pci_set_drvdata(pdev, dev); | |
302 | ||
303 | return 0; | |
304 | ||
305 | fail: | |
306 | free_irq(pdev->irq, dev); | |
307 | ||
308 | fail_irq: | |
309 | pci_set_drvdata(pdev, NULL); | |
310 | free_orinocodev(dev); | |
311 | ||
312 | fail_alloc: | |
313 | pci_iounmap(pdev, mem); | |
314 | ||
315 | fail_map_io: | |
316 | iounmap(attr_mem); | |
317 | ||
318 | fail_map_attr: | |
319 | pci_release_regions(pdev); | |
320 | ||
321 | fail_resources: | |
322 | pci_disable_device(pdev); | |
323 | ||
324 | return err; | |
325 | } | |
326 | ||
327 | static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev) | |
328 | { | |
329 | struct net_device *dev = pci_get_drvdata(pdev); | |
330 | struct orinoco_private *priv = netdev_priv(dev); | |
331 | struct orinoco_plx_card *card = priv->card; | |
332 | u8 __iomem *attr_mem = card->attr_mem; | |
333 | ||
334 | BUG_ON(! dev); | |
335 | ||
336 | unregister_netdev(dev); | |
337 | free_irq(dev->irq, dev); | |
338 | pci_set_drvdata(pdev, NULL); | |
339 | free_orinocodev(dev); | |
340 | pci_iounmap(pdev, priv->hw.iobase); | |
341 | iounmap(attr_mem); | |
342 | pci_release_regions(pdev); | |
343 | pci_disable_device(pdev); | |
344 | } | |
345 | ||
346 | ||
347 | static struct pci_device_id orinoco_plx_pci_id_table[] = { | |
348 | {0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */ | |
349 | {0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */ | |
350 | {0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */ | |
351 | {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W, | |
352 | Eumitcom PCI WL11000, | |
353 | Addtron AWA-100 */ | |
354 | {0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* Global Sun Tech GL24110P */ | |
355 | {0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,}, /* Reported working, but unknown */ | |
356 | {0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */ | |
357 | {0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */ | |
358 | {0xec80, 0xec00, PCI_ANY_ID, PCI_ANY_ID,}, /* Belkin F5D6000 tested by | |
359 | Brendan W. McAdams <rit AT jacked-in.org> */ | |
360 | {0x10b7, 0x7770, PCI_ANY_ID, PCI_ANY_ID,}, /* 3Com AirConnect PCI tested by | |
361 | Damien Persohn <damien AT persohn.net> */ | |
362 | {0,}, | |
363 | }; | |
364 | ||
365 | MODULE_DEVICE_TABLE(pci, orinoco_plx_pci_id_table); | |
366 | ||
367 | static struct pci_driver orinoco_plx_driver = { | |
368 | .name = DRIVER_NAME, | |
369 | .id_table = orinoco_plx_pci_id_table, | |
370 | .probe = orinoco_plx_init_one, | |
371 | .remove = __devexit_p(orinoco_plx_remove_one), | |
372 | }; | |
373 | ||
374 | static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION | |
375 | " (Pavel Roskin <proski@gnu.org>," | |
376 | " David Gibson <hermes@gibson.dropbear.id.au>," | |
377 | " Daniel Barlow <dan@telent.net>)"; | |
378 | MODULE_AUTHOR("Daniel Barlow <dan@telent.net>"); | |
379 | MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); | |
380 | MODULE_LICENSE("Dual MPL/GPL"); | |
381 | ||
382 | static int __init orinoco_plx_init(void) | |
383 | { | |
384 | printk(KERN_DEBUG "%s\n", version); | |
385 | return pci_module_init(&orinoco_plx_driver); | |
386 | } | |
387 | ||
388 | static void __exit orinoco_plx_exit(void) | |
389 | { | |
390 | pci_unregister_driver(&orinoco_plx_driver); | |
391 | ssleep(1); | |
392 | } | |
393 | ||
394 | module_init(orinoco_plx_init); | |
395 | module_exit(orinoco_plx_exit); | |
396 | ||
397 | /* | |
398 | * Local variables: | |
399 | * c-indent-level: 8 | |
400 | * c-basic-offset: 8 | |
401 | * tab-width: 8 | |
402 | * End: | |
403 | */ |