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