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 | * | |
dc3437d2 | 6 | * Current maintainers are: |
933d5943 | 7 | * Pavel Roskin <proski AT gnu.org> |
1da177e4 LT |
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. | |
1da177e4 | 33 | * |
dc3437d2 | 34 | * Here's the general details on how the PLX9052 adapter works: |
1da177e4 LT |
35 | * |
36 | * - Two PCI I/O address spaces, one 0x80 long which contains the | |
37 | * PLX9052 registers, and one that's 0x40 long mapped to the PCMCIA | |
38 | * slot I/O address space. | |
39 | * | |
dc3437d2 | 40 | * - One PCI memory address space, mapped to the PCMCIA attribute space |
1da177e4 LT |
41 | * (containing the CIS). |
42 | * | |
dc3437d2 PR |
43 | * Using the later, you can read through the CIS data to make sure the |
44 | * card is compatible with the driver. Keep in mind that the PCMCIA | |
1da177e4 LT |
45 | * spec specifies the CIS as the lower 8 bits of each word read from |
46 | * the CIS, so to read the bytes of the CIS, read every other byte | |
47 | * (0,2,4,...). Passing that test, you need to enable the I/O address | |
48 | * space on the PCMCIA card via the PCMCIA COR register. This is the | |
49 | * first byte following the CIS. In my case (which may not have any | |
50 | * relation to what's on the PRISM2 cards), COR was at offset 0x800 | |
51 | * within the PCI memory space. Write 0x41 to the COR register to | |
52 | * enable I/O mode and to select level triggered interrupts. To | |
53 | * confirm you actually succeeded, read the COR register back and make | |
dc3437d2 | 54 | * sure it actually got set to 0x41, in case you have an unexpected |
1da177e4 LT |
55 | * card inserted. |
56 | * | |
57 | * Following that, you can treat the second PCI I/O address space (the | |
58 | * one that's not 0x80 in length) as the PCMCIA I/O space. | |
59 | * | |
60 | * Note that in the Eumitcom's source for their drivers, they register | |
61 | * the interrupt as edge triggered when registering it with the | |
62 | * Windows kernel. I don't recall how to register edge triggered on | |
63 | * Linux (if it can be done at all). But in some experimentation, I | |
64 | * don't see much operational difference between using either | |
65 | * interrupt mode. Don't mess with the interrupt mode in the COR | |
66 | * register though, as the PLX9052 wants level triggers with the way | |
67 | * the serial EEPROM configures it on the WL11000. | |
68 | * | |
69 | * There's some other little quirks related to timing that I bumped | |
70 | * into, but I don't recall right now. Also, there's two variants of | |
71 | * the WL11000 I've seen, revision A1 and T2. These seem to differ | |
72 | * slightly in the timings configured in the wait-state generator in | |
73 | * the PLX9052. There have also been some comments from Eumitcom that | |
74 | * cards shouldn't be hot swapped, apparently due to risk of cooking | |
75 | * the PLX9052. I'm unsure why they believe this, as I can't see | |
76 | * anything in the design that would really cause a problem, except | |
77 | * for crashing drivers not written to expect it. And having developed | |
78 | * drivers for the WL11000, I'd say it's quite tricky to write code | |
79 | * that will successfully deal with a hot unplug. Very odd things | |
80 | * happen on the I/O side of things. But anyway, be warned. Despite | |
81 | * that, I've hot-swapped a number of times during debugging and | |
82 | * driver development for various reasons (stuck WAIT# line after the | |
83 | * radio card's firmware locks up). | |
1da177e4 LT |
84 | */ |
85 | ||
86 | #define DRIVER_NAME "orinoco_plx" | |
87 | #define PFX DRIVER_NAME ": " | |
88 | ||
1da177e4 LT |
89 | #include <linux/module.h> |
90 | #include <linux/kernel.h> | |
91 | #include <linux/init.h> | |
ef846bf0 | 92 | #include <linux/delay.h> |
1da177e4 | 93 | #include <linux/pci.h> |
1da177e4 LT |
94 | #include <pcmcia/cisreg.h> |
95 | ||
1da177e4 | 96 | #include "orinoco.h" |
b884c872 | 97 | #include "orinoco_pci.h" |
1da177e4 LT |
98 | |
99 | #define COR_OFFSET (0x3e0) /* COR attribute offset of Prism2 PC card */ | |
100 | #define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ | |
101 | #define COR_RESET (0x80) /* reset bit in the COR register */ | |
102 | #define PLX_RESET_TIME (500) /* milliseconds */ | |
103 | ||
104 | #define PLX_INTCSR 0x4c /* Interrupt Control & Status Register */ | |
933d5943 | 105 | #define PLX_INTCSR_INTEN (1 << 6) /* Interrupt Enable bit */ |
1da177e4 | 106 | |
1da177e4 LT |
107 | /* |
108 | * Do a soft reset of the card using the Configuration Option Register | |
109 | */ | |
110 | static int orinoco_plx_cor_reset(struct orinoco_private *priv) | |
111 | { | |
933d5943 | 112 | struct hermes *hw = &priv->hw; |
b884c872 | 113 | struct orinoco_pci_card *card = priv->card; |
1da177e4 LT |
114 | unsigned long timeout; |
115 | u16 reg; | |
116 | ||
b884c872 | 117 | iowrite8(COR_VALUE | COR_RESET, card->attr_io + COR_OFFSET); |
1da177e4 LT |
118 | mdelay(1); |
119 | ||
b884c872 | 120 | iowrite8(COR_VALUE, card->attr_io + COR_OFFSET); |
1da177e4 LT |
121 | mdelay(1); |
122 | ||
123 | /* Just in case, wait more until the card is no longer busy */ | |
ab458cc8 | 124 | timeout = jiffies + msecs_to_jiffies(PLX_RESET_TIME); |
1da177e4 LT |
125 | reg = hermes_read_regn(hw, CMD); |
126 | while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { | |
127 | mdelay(1); | |
128 | reg = hermes_read_regn(hw, CMD); | |
129 | } | |
130 | ||
b884c872 | 131 | /* Still busy? */ |
1da177e4 LT |
132 | if (reg & HERMES_CMD_BUSY) { |
133 | printk(KERN_ERR PFX "Busy timeout\n"); | |
134 | return -ETIMEDOUT; | |
135 | } | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
b884c872 PR |
140 | static int orinoco_plx_hw_init(struct orinoco_pci_card *card) |
141 | { | |
142 | int i; | |
143 | u32 csr_reg; | |
144 | static const u8 cis_magic[] = { | |
145 | 0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67 | |
146 | }; | |
147 | ||
148 | printk(KERN_DEBUG PFX "CIS: "); | |
d14c7c1d | 149 | for (i = 0; i < 16; i++) |
b884c872 | 150 | printk("%02X:", ioread8(card->attr_io + (i << 1))); |
b884c872 PR |
151 | printk("\n"); |
152 | ||
153 | /* Verify whether a supported PC card is present */ | |
154 | /* FIXME: we probably need to be smarted about this */ | |
155 | for (i = 0; i < sizeof(cis_magic); i++) { | |
156 | if (cis_magic[i] != ioread8(card->attr_io + (i << 1))) { | |
157 | printk(KERN_ERR PFX "The CIS value of Prism2 PC " | |
158 | "card is unexpected\n"); | |
159 | return -ENODEV; | |
160 | } | |
161 | } | |
162 | ||
163 | /* bjoern: We need to tell the card to enable interrupts, in | |
164 | case the serial eprom didn't do this already. See the | |
165 | PLX9052 data book, p8-1 and 8-24 for reference. */ | |
166 | csr_reg = ioread32(card->bridge_io + PLX_INTCSR); | |
167 | if (!(csr_reg & PLX_INTCSR_INTEN)) { | |
168 | csr_reg |= PLX_INTCSR_INTEN; | |
169 | iowrite32(csr_reg, card->bridge_io + PLX_INTCSR); | |
170 | csr_reg = ioread32(card->bridge_io + PLX_INTCSR); | |
171 | if (!(csr_reg & PLX_INTCSR_INTEN)) { | |
172 | printk(KERN_ERR PFX "Cannot enable interrupts\n"); | |
173 | return -EIO; | |
174 | } | |
175 | } | |
176 | ||
177 | return 0; | |
178 | } | |
1da177e4 LT |
179 | |
180 | static int orinoco_plx_init_one(struct pci_dev *pdev, | |
181 | const struct pci_device_id *ent) | |
182 | { | |
b884c872 PR |
183 | int err; |
184 | struct orinoco_private *priv; | |
185 | struct orinoco_pci_card *card; | |
b884c872 | 186 | void __iomem *hermes_io, *attr_io, *bridge_io; |
1da177e4 LT |
187 | |
188 | err = pci_enable_device(pdev); | |
189 | if (err) { | |
190 | printk(KERN_ERR PFX "Cannot enable PCI device\n"); | |
191 | return err; | |
192 | } | |
193 | ||
194 | err = pci_request_regions(pdev, DRIVER_NAME); | |
b884c872 | 195 | if (err) { |
1da177e4 LT |
196 | printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); |
197 | goto fail_resources; | |
198 | } | |
199 | ||
b884c872 PR |
200 | bridge_io = pci_iomap(pdev, 1, 0); |
201 | if (!bridge_io) { | |
202 | printk(KERN_ERR PFX "Cannot map bridge registers\n"); | |
203 | err = -EIO; | |
204 | goto fail_map_bridge; | |
205 | } | |
1da177e4 | 206 | |
b884c872 PR |
207 | attr_io = pci_iomap(pdev, 2, 0); |
208 | if (!attr_io) { | |
209 | printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n"); | |
210 | err = -EIO; | |
1da177e4 LT |
211 | goto fail_map_attr; |
212 | } | |
213 | ||
b884c872 PR |
214 | hermes_io = pci_iomap(pdev, 3, 0); |
215 | if (!hermes_io) { | |
216 | printk(KERN_ERR PFX "Cannot map chipset registers\n"); | |
217 | err = -EIO; | |
218 | goto fail_map_hermes; | |
1da177e4 LT |
219 | } |
220 | ||
221 | /* Allocate network device */ | |
a2608362 DK |
222 | priv = alloc_orinocodev(sizeof(*card), &pdev->dev, |
223 | orinoco_plx_cor_reset, NULL); | |
224 | if (!priv) { | |
1da177e4 LT |
225 | printk(KERN_ERR PFX "Cannot allocate network device\n"); |
226 | err = -ENOMEM; | |
227 | goto fail_alloc; | |
228 | } | |
229 | ||
1da177e4 | 230 | card = priv->card; |
b884c872 PR |
231 | card->bridge_io = bridge_io; |
232 | card->attr_io = attr_io; | |
1da177e4 | 233 | |
b884c872 | 234 | hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); |
1da177e4 | 235 | |
1fb9df5d | 236 | err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, |
5381956b | 237 | DRIVER_NAME, priv); |
1da177e4 LT |
238 | if (err) { |
239 | printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); | |
240 | err = -EBUSY; | |
241 | goto fail_irq; | |
242 | } | |
1da177e4 | 243 | |
b884c872 PR |
244 | err = orinoco_plx_hw_init(card); |
245 | if (err) { | |
246 | printk(KERN_ERR PFX "Hardware initialization failed\n"); | |
247 | goto fail; | |
1da177e4 LT |
248 | } |
249 | ||
250 | err = orinoco_plx_cor_reset(priv); | |
251 | if (err) { | |
252 | printk(KERN_ERR PFX "Initial reset failed\n"); | |
253 | goto fail; | |
254 | } | |
255 | ||
8e638267 DK |
256 | err = orinoco_init(priv); |
257 | if (err) { | |
258 | printk(KERN_ERR PFX "orinoco_init() failed\n"); | |
259 | goto fail; | |
260 | } | |
261 | ||
593ef09c | 262 | err = orinoco_if_add(priv, 0, 0, NULL); |
1da177e4 | 263 | if (err) { |
5381956b | 264 | printk(KERN_ERR PFX "orinoco_if_add() failed\n"); |
94fdc2e6 | 265 | goto fail_wiphy; |
1da177e4 LT |
266 | } |
267 | ||
a2608362 | 268 | pci_set_drvdata(pdev, priv); |
1da177e4 LT |
269 | |
270 | return 0; | |
271 | ||
94fdc2e6 JS |
272 | fail_wiphy: |
273 | wiphy_unregister(priv_to_wiphy(priv)); | |
1da177e4 | 274 | fail: |
a2608362 | 275 | free_irq(pdev->irq, priv); |
1da177e4 LT |
276 | |
277 | fail_irq: | |
a2608362 | 278 | free_orinocodev(priv); |
1da177e4 LT |
279 | |
280 | fail_alloc: | |
b884c872 | 281 | pci_iounmap(pdev, hermes_io); |
1da177e4 | 282 | |
b884c872 PR |
283 | fail_map_hermes: |
284 | pci_iounmap(pdev, attr_io); | |
1da177e4 LT |
285 | |
286 | fail_map_attr: | |
b884c872 PR |
287 | pci_iounmap(pdev, bridge_io); |
288 | ||
289 | fail_map_bridge: | |
1da177e4 LT |
290 | pci_release_regions(pdev); |
291 | ||
292 | fail_resources: | |
293 | pci_disable_device(pdev); | |
294 | ||
295 | return err; | |
296 | } | |
297 | ||
baa366cd | 298 | static void orinoco_plx_remove_one(struct pci_dev *pdev) |
1da177e4 | 299 | { |
a2608362 | 300 | struct orinoco_private *priv = pci_get_drvdata(pdev); |
b884c872 | 301 | struct orinoco_pci_card *card = priv->card; |
1da177e4 | 302 | |
5381956b | 303 | orinoco_if_del(priv); |
94fdc2e6 | 304 | wiphy_unregister(priv_to_wiphy(priv)); |
a2608362 | 305 | free_irq(pdev->irq, priv); |
a2608362 | 306 | free_orinocodev(priv); |
1da177e4 | 307 | pci_iounmap(pdev, priv->hw.iobase); |
b884c872 PR |
308 | pci_iounmap(pdev, card->attr_io); |
309 | pci_iounmap(pdev, card->bridge_io); | |
1da177e4 LT |
310 | pci_release_regions(pdev); |
311 | pci_disable_device(pdev); | |
312 | } | |
313 | ||
9baa3c34 | 314 | static const struct pci_device_id orinoco_plx_id_table[] = { |
1da177e4 LT |
315 | {0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */ |
316 | {0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */ | |
317 | {0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */ | |
318 | {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W, | |
319 | Eumitcom PCI WL11000, | |
320 | Addtron AWA-100 */ | |
321 | {0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* Global Sun Tech GL24110P */ | |
322 | {0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,}, /* Reported working, but unknown */ | |
323 | {0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */ | |
324 | {0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */ | |
325 | {0xec80, 0xec00, PCI_ANY_ID, PCI_ANY_ID,}, /* Belkin F5D6000 tested by | |
326 | Brendan W. McAdams <rit AT jacked-in.org> */ | |
327 | {0x10b7, 0x7770, PCI_ANY_ID, PCI_ANY_ID,}, /* 3Com AirConnect PCI tested by | |
328 | Damien Persohn <damien AT persohn.net> */ | |
329 | {0,}, | |
330 | }; | |
331 | ||
b884c872 | 332 | MODULE_DEVICE_TABLE(pci, orinoco_plx_id_table); |
1da177e4 LT |
333 | |
334 | static struct pci_driver orinoco_plx_driver = { | |
335 | .name = DRIVER_NAME, | |
b884c872 | 336 | .id_table = orinoco_plx_id_table, |
1da177e4 | 337 | .probe = orinoco_plx_init_one, |
baa366cd | 338 | .remove = orinoco_plx_remove_one, |
b884c872 PR |
339 | .suspend = orinoco_pci_suspend, |
340 | .resume = orinoco_pci_resume, | |
1da177e4 LT |
341 | }; |
342 | ||
343 | static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION | |
344 | " (Pavel Roskin <proski@gnu.org>," | |
345 | " David Gibson <hermes@gibson.dropbear.id.au>," | |
346 | " Daniel Barlow <dan@telent.net>)"; | |
347 | MODULE_AUTHOR("Daniel Barlow <dan@telent.net>"); | |
348 | MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); | |
349 | MODULE_LICENSE("Dual MPL/GPL"); | |
350 | ||
351 | static int __init orinoco_plx_init(void) | |
352 | { | |
353 | printk(KERN_DEBUG "%s\n", version); | |
29917620 | 354 | return pci_register_driver(&orinoco_plx_driver); |
1da177e4 LT |
355 | } |
356 | ||
357 | static void __exit orinoco_plx_exit(void) | |
358 | { | |
359 | pci_unregister_driver(&orinoco_plx_driver); | |
1da177e4 LT |
360 | } |
361 | ||
362 | module_init(orinoco_plx_init); | |
363 | module_exit(orinoco_plx_exit); | |
364 | ||
365 | /* | |
366 | * Local variables: | |
367 | * c-indent-level: 8 | |
368 | * c-basic-offset: 8 | |
369 | * tab-width: 8 | |
370 | * End: | |
371 | */ |