Merge branch 'master'
[linux-2.6] / drivers / net / wireless / orinoco_nortel.c
1 /* orinoco_nortel.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 Nortel PCI-PCMCIA-Adapter. 
5  *
6  * Copyright (C) 2002 Tobias Hoffmann
7  *           (C) 2003 Christoph Jungegger <disdos@traum404.de>
8  *
9  * Some of this code is borrowed from orinoco_plx.c
10  *      Copyright (C) 2001 Daniel Barlow
11  * Some of this code is borrowed from orinoco_pci.c 
12  *  Copyright (C) 2001 Jean Tourrilhes
13  * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing
14  * has been copied from it. linux-wlan-ng-0.1.10 is originally :
15  *      Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
16  * 
17  * The contents of this file are subject to the Mozilla Public License
18  * Version 1.1 (the "License"); you may not use this file except in
19  * compliance with the License. You may obtain a copy of the License
20  * at http://www.mozilla.org/MPL/
21  *
22  * Software distributed under the License is distributed on an "AS IS"
23  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
24  * the License for the specific language governing rights and
25  * limitations under the License.
26  *
27  * Alternatively, the contents of this file may be used under the
28  * terms of the GNU General Public License version 2 (the "GPL"), in
29  * which case the provisions of the GPL are applicable instead of the
30  * above.  If you wish to allow the use of your version of this file
31  * only under the terms of the GPL and not to allow others to use your
32  * version of this file under the MPL, indicate your decision by
33  * deleting the provisions above and replace them with the notice and
34  * other provisions required by the GPL.  If you do not delete the
35  * provisions above, a recipient may use your version of this file
36  * under either the MPL or the GPL.
37  */
38
39 #define DRIVER_NAME "orinoco_nortel"
40 #define PFX DRIVER_NAME ": "
41
42 #include <linux/config.h>
43 #include <linux/module.h>
44 #include <linux/kernel.h>
45 #include <linux/init.h>
46 #include <linux/delay.h>
47 #include <linux/pci.h>
48 #include <pcmcia/cisreg.h>
49
50 #include "orinoco.h"
51
52 #define COR_OFFSET    (0xe0)    /* COR attribute offset of Prism2 PC card */
53 #define COR_VALUE     (COR_LEVEL_REQ | COR_FUNC_ENA)    /* Enable PC card with interrupt in level trigger */
54
55
56 /* Nortel specific data */
57 struct nortel_pci_card {
58         unsigned long iobase1;
59         unsigned long iobase2;
60 };
61
62 /*
63  * Do a soft reset of the PCI card using the Configuration Option Register
64  * We need this to get going...
65  * This is the part of the code that is strongly inspired from wlan-ng
66  *
67  * Note bis : Don't try to access HERMES_CMD during the reset phase.
68  * It just won't work !
69  */
70 static int nortel_pci_cor_reset(struct orinoco_private *priv)
71 {
72         struct nortel_pci_card *card = priv->card;
73
74         /* Assert the reset until the card notice */
75         outw_p(8, card->iobase1 + 2);
76         inw(card->iobase2 + COR_OFFSET);
77         outw_p(0x80, card->iobase2 + COR_OFFSET);
78         mdelay(1);
79
80         /* Give time for the card to recover from this hard effort */
81         outw_p(0, card->iobase2 + COR_OFFSET);
82         outw_p(0, card->iobase2 + COR_OFFSET);
83         mdelay(1);
84
85         /* set COR as usual */
86         outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
87         outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
88         mdelay(1);
89
90         outw_p(0x228, card->iobase1 + 2);
91
92         return 0;
93 }
94
95 static int nortel_pci_hw_init(struct nortel_pci_card *card)
96 {
97         int i;
98         u32 reg;
99
100         /* setup bridge */
101         if (inw(card->iobase1) & 1) {
102                 printk(KERN_ERR PFX "brg1 answer1 wrong\n");
103                 return -EBUSY;
104         }
105         outw_p(0x118, card->iobase1 + 2);
106         outw_p(0x108, card->iobase1 + 2);
107         mdelay(30);
108         outw_p(0x8, card->iobase1 + 2);
109         for (i = 0; i < 30; i++) {
110                 mdelay(30);
111                 if (inw(card->iobase1) & 0x10) {
112                         break;
113                 }
114         }
115         if (i == 30) {
116                 printk(KERN_ERR PFX "brg1 timed out\n");
117                 return -EBUSY;
118         }
119         if (inw(card->iobase2 + 0xe0) & 1) {
120                 printk(KERN_ERR PFX "brg2 answer1 wrong\n");
121                 return -EBUSY;
122         }
123         if (inw(card->iobase2 + 0xe2) & 1) {
124                 printk(KERN_ERR PFX "brg2 answer2 wrong\n");
125                 return -EBUSY;
126         }
127         if (inw(card->iobase2 + 0xe4) & 1) {
128                 printk(KERN_ERR PFX "brg2 answer3 wrong\n");
129                 return -EBUSY;
130         }
131
132         /* set the PCMCIA COR-Register */
133         outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
134         mdelay(1);
135         reg = inw(card->iobase2 + COR_OFFSET);
136         if (reg != COR_VALUE) {
137                 printk(KERN_ERR PFX "Error setting COR value (reg=%x)\n",
138                        reg);
139                 return -EBUSY;
140         }
141
142         /* set leds */
143         outw_p(1, card->iobase1 + 10);
144         return 0;
145 }
146
147 static int nortel_pci_init_one(struct pci_dev *pdev,
148                                const struct pci_device_id *ent)
149 {
150         int err;
151         struct orinoco_private *priv;
152         struct nortel_pci_card *card;
153         struct net_device *dev;
154         void __iomem *iomem;
155
156         err = pci_enable_device(pdev);
157         if (err) {
158                 printk(KERN_ERR PFX "Cannot enable PCI device\n");
159                 return err;
160         }
161
162         err = pci_request_regions(pdev, DRIVER_NAME);
163         if (err != 0) {
164                 printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
165                 goto fail_resources;
166         }
167
168         iomem = pci_iomap(pdev, 3, 0);
169         if (!iomem) {
170                 err = -ENOMEM;
171                 goto fail_map_io;
172         }
173
174         /* Allocate network device */
175         dev = alloc_orinocodev(sizeof(*card), nortel_pci_cor_reset);
176         if (!dev) {
177                 printk(KERN_ERR PFX "Cannot allocate network device\n");
178                 err = -ENOMEM;
179                 goto fail_alloc;
180         }
181
182         priv = netdev_priv(dev);
183         card = priv->card;
184         card->iobase1 = pci_resource_start(pdev, 0);
185         card->iobase2 = pci_resource_start(pdev, 1);
186         dev->base_addr = pci_resource_start(pdev, 2);
187         SET_MODULE_OWNER(dev);
188         SET_NETDEV_DEV(dev, &pdev->dev);
189
190         hermes_struct_init(&priv->hw, iomem, HERMES_16BIT_REGSPACING);
191
192         printk(KERN_DEBUG PFX "Detected Nortel PCI device at %s irq:%d, "
193                "io addr:0x%lx\n", pci_name(pdev), pdev->irq, dev->base_addr);
194
195         err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
196                           dev->name, dev);
197         if (err) {
198                 printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq);
199                 err = -EBUSY;
200                 goto fail_irq;
201         }
202         dev->irq = pdev->irq;
203
204         err = nortel_pci_hw_init(card);
205         if (err) {
206                 printk(KERN_ERR PFX "Hardware initialization failed\n");
207                 goto fail;
208         }
209
210         err = nortel_pci_cor_reset(priv);
211         if (err) {
212                 printk(KERN_ERR PFX "Initial reset failed\n");
213                 goto fail;
214         }
215
216
217         err = register_netdev(dev);
218         if (err) {
219                 printk(KERN_ERR PFX "Cannot register network device\n");
220                 goto fail;
221         }
222
223         pci_set_drvdata(pdev, dev);
224
225         return 0;
226
227  fail:
228         free_irq(pdev->irq, dev);
229
230  fail_irq:
231         pci_set_drvdata(pdev, NULL);
232         free_orinocodev(dev);
233
234  fail_alloc:
235         pci_iounmap(pdev, iomem);
236
237  fail_map_io:
238         pci_release_regions(pdev);
239
240  fail_resources:
241         pci_disable_device(pdev);
242
243         return err;
244 }
245
246 static void __devexit nortel_pci_remove_one(struct pci_dev *pdev)
247 {
248         struct net_device *dev = pci_get_drvdata(pdev);
249         struct orinoco_private *priv = netdev_priv(dev);
250         struct nortel_pci_card *card = priv->card;
251
252         /* clear leds */
253         outw_p(0, card->iobase1 + 10);
254
255         unregister_netdev(dev);
256         free_irq(dev->irq, dev);
257         pci_set_drvdata(pdev, NULL);
258         free_orinocodev(dev);
259         pci_iounmap(pdev, priv->hw.iobase);
260         pci_release_regions(pdev);
261         pci_disable_device(pdev);
262 }
263
264
265 static struct pci_device_id nortel_pci_id_table[] = {
266         /* Nortel emobility PCI */
267         {0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,},
268         {0,},
269 };
270
271 MODULE_DEVICE_TABLE(pci, nortel_pci_id_table);
272
273 static struct pci_driver nortel_pci_driver = {
274         .name = DRIVER_NAME,
275         .id_table = nortel_pci_id_table,
276         .probe = nortel_pci_init_one,
277         .remove = __devexit_p(nortel_pci_remove_one),
278 };
279
280 static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
281         " (Tobias Hoffmann & Christoph Jungegger <disdos@traum404.de>)";
282 MODULE_AUTHOR("Christoph Jungegger <disdos@traum404.de>");
283 MODULE_DESCRIPTION
284     ("Driver for wireless LAN cards using the Nortel PCI bridge");
285 MODULE_LICENSE("Dual MPL/GPL");
286
287 static int __init nortel_pci_init(void)
288 {
289         printk(KERN_DEBUG "%s\n", version);
290         return pci_module_init(&nortel_pci_driver);
291 }
292
293 static void __exit nortel_pci_exit(void)
294 {
295         pci_unregister_driver(&nortel_pci_driver);
296         ssleep(1);
297 }
298
299 module_init(nortel_pci_init);
300 module_exit(nortel_pci_exit);
301
302 /*
303  * Local variables:
304  *  c-indent-level: 8
305  *  c-basic-offset: 8
306  *  tab-width: 8
307  * End:
308  */