diff options
Diffstat (limited to 'drivers/net/wireless/airport.c')
-rw-r--r-- | drivers/net/wireless/airport.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c new file mode 100644 index 000000000000..a1dc2a196087 --- /dev/null +++ b/drivers/net/wireless/airport.c | |||
@@ -0,0 +1,304 @@ | |||
1 | /* airport.c | ||
2 | * | ||
3 | * A driver for "Hermes" chipset based Apple Airport wireless | ||
4 | * card. | ||
5 | * | ||
6 | * Copyright notice & release notes in file orinoco.c | ||
7 | * | ||
8 | * Note specific to airport stub: | ||
9 | * | ||
10 | * 0.05 : first version of the new split driver | ||
11 | * 0.06 : fix possible hang on powerup, add sleep support | ||
12 | */ | ||
13 | |||
14 | #define DRIVER_NAME "airport" | ||
15 | #define PFX DRIVER_NAME ": " | ||
16 | |||
17 | #include <linux/config.h> | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/ptrace.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/timer.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <linux/netdevice.h> | ||
28 | #include <linux/if_arp.h> | ||
29 | #include <linux/etherdevice.h> | ||
30 | #include <linux/wireless.h> | ||
31 | |||
32 | #include <asm/io.h> | ||
33 | #include <asm/system.h> | ||
34 | #include <asm/current.h> | ||
35 | #include <asm/prom.h> | ||
36 | #include <asm/machdep.h> | ||
37 | #include <asm/pmac_feature.h> | ||
38 | #include <asm/irq.h> | ||
39 | #include <asm/uaccess.h> | ||
40 | |||
41 | #include "orinoco.h" | ||
42 | |||
43 | #define AIRPORT_IO_LEN (0x1000) /* one page */ | ||
44 | |||
45 | struct airport { | ||
46 | struct macio_dev *mdev; | ||
47 | void __iomem *vaddr; | ||
48 | int irq_requested; | ||
49 | int ndev_registered; | ||
50 | }; | ||
51 | |||
52 | static int | ||
53 | airport_suspend(struct macio_dev *mdev, u32 state) | ||
54 | { | ||
55 | struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); | ||
56 | struct orinoco_private *priv = netdev_priv(dev); | ||
57 | unsigned long flags; | ||
58 | int err; | ||
59 | |||
60 | printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name); | ||
61 | |||
62 | err = orinoco_lock(priv, &flags); | ||
63 | if (err) { | ||
64 | printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n", | ||
65 | dev->name); | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | err = __orinoco_down(dev); | ||
70 | if (err) | ||
71 | printk(KERN_WARNING "%s: PBOOK_SLEEP_NOW: Error %d downing interface\n", | ||
72 | dev->name, err); | ||
73 | |||
74 | netif_device_detach(dev); | ||
75 | |||
76 | priv->hw_unavailable++; | ||
77 | |||
78 | orinoco_unlock(priv, &flags); | ||
79 | |||
80 | disable_irq(dev->irq); | ||
81 | pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0); | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int | ||
87 | airport_resume(struct macio_dev *mdev) | ||
88 | { | ||
89 | struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); | ||
90 | struct orinoco_private *priv = netdev_priv(dev); | ||
91 | unsigned long flags; | ||
92 | int err; | ||
93 | |||
94 | printk(KERN_DEBUG "%s: Airport waking up\n", dev->name); | ||
95 | |||
96 | pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1); | ||
97 | msleep(200); | ||
98 | |||
99 | enable_irq(dev->irq); | ||
100 | |||
101 | err = orinoco_reinit_firmware(dev); | ||
102 | if (err) { | ||
103 | printk(KERN_ERR "%s: Error %d re-initializing firmware on PBOOK_WAKE\n", | ||
104 | dev->name, err); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | spin_lock_irqsave(&priv->lock, flags); | ||
109 | |||
110 | netif_device_attach(dev); | ||
111 | |||
112 | priv->hw_unavailable--; | ||
113 | |||
114 | if (priv->open && (! priv->hw_unavailable)) { | ||
115 | err = __orinoco_up(dev); | ||
116 | if (err) | ||
117 | printk(KERN_ERR "%s: Error %d restarting card on PBOOK_WAKE\n", | ||
118 | dev->name, err); | ||
119 | } | ||
120 | |||
121 | |||
122 | spin_unlock_irqrestore(&priv->lock, flags); | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int | ||
128 | airport_detach(struct macio_dev *mdev) | ||
129 | { | ||
130 | struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); | ||
131 | struct orinoco_private *priv = netdev_priv(dev); | ||
132 | struct airport *card = priv->card; | ||
133 | |||
134 | if (card->ndev_registered) | ||
135 | unregister_netdev(dev); | ||
136 | card->ndev_registered = 0; | ||
137 | |||
138 | if (card->irq_requested) | ||
139 | free_irq(dev->irq, dev); | ||
140 | card->irq_requested = 0; | ||
141 | |||
142 | if (card->vaddr) | ||
143 | iounmap(card->vaddr); | ||
144 | card->vaddr = NULL; | ||
145 | |||
146 | macio_release_resource(mdev, 0); | ||
147 | |||
148 | pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0); | ||
149 | ssleep(1); | ||
150 | |||
151 | macio_set_drvdata(mdev, NULL); | ||
152 | free_orinocodev(dev); | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int airport_hard_reset(struct orinoco_private *priv) | ||
158 | { | ||
159 | /* It would be nice to power cycle the Airport for a real hard | ||
160 | * reset, but for some reason although it appears to | ||
161 | * re-initialize properly, it falls in a screaming heap | ||
162 | * shortly afterwards. */ | ||
163 | #if 0 | ||
164 | struct net_device *dev = priv->ndev; | ||
165 | struct airport *card = priv->card; | ||
166 | |||
167 | /* Vitally important. If we don't do this it seems we get an | ||
168 | * interrupt somewhere during the power cycle, since | ||
169 | * hw_unavailable is already set it doesn't get ACKed, we get | ||
170 | * into an interrupt loop and the the PMU decides to turn us | ||
171 | * off. */ | ||
172 | disable_irq(dev->irq); | ||
173 | |||
174 | pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 0); | ||
175 | ssleep(1); | ||
176 | pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 1); | ||
177 | ssleep(1); | ||
178 | |||
179 | enable_irq(dev->irq); | ||
180 | ssleep(1); | ||
181 | #endif | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int | ||
187 | airport_attach(struct macio_dev *mdev, const struct of_match *match) | ||
188 | { | ||
189 | struct orinoco_private *priv; | ||
190 | struct net_device *dev; | ||
191 | struct airport *card; | ||
192 | unsigned long phys_addr; | ||
193 | hermes_t *hw; | ||
194 | |||
195 | if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) { | ||
196 | printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n"); | ||
197 | return -ENODEV; | ||
198 | } | ||
199 | |||
200 | /* Allocate space for private device-specific data */ | ||
201 | dev = alloc_orinocodev(sizeof(*card), airport_hard_reset); | ||
202 | if (! dev) { | ||
203 | printk(KERN_ERR PFX "Cannot allocate network device\n"); | ||
204 | return -ENODEV; | ||
205 | } | ||
206 | priv = netdev_priv(dev); | ||
207 | card = priv->card; | ||
208 | |||
209 | hw = &priv->hw; | ||
210 | card->mdev = mdev; | ||
211 | |||
212 | if (macio_request_resource(mdev, 0, "airport")) { | ||
213 | printk(KERN_ERR PFX "can't request IO resource !\n"); | ||
214 | free_orinocodev(dev); | ||
215 | return -EBUSY; | ||
216 | } | ||
217 | |||
218 | SET_MODULE_OWNER(dev); | ||
219 | SET_NETDEV_DEV(dev, &mdev->ofdev.dev); | ||
220 | |||
221 | macio_set_drvdata(mdev, dev); | ||
222 | |||
223 | /* Setup interrupts & base address */ | ||
224 | dev->irq = macio_irq(mdev, 0); | ||
225 | phys_addr = macio_resource_start(mdev, 0); /* Physical address */ | ||
226 | printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr); | ||
227 | dev->base_addr = phys_addr; | ||
228 | card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); | ||
229 | if (!card->vaddr) { | ||
230 | printk(KERN_ERR PFX "ioremap() failed\n"); | ||
231 | goto failed; | ||
232 | } | ||
233 | |||
234 | hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING); | ||
235 | |||
236 | /* Power up card */ | ||
237 | pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1); | ||
238 | ssleep(1); | ||
239 | |||
240 | /* Reset it before we get the interrupt */ | ||
241 | hermes_init(hw); | ||
242 | |||
243 | if (request_irq(dev->irq, orinoco_interrupt, 0, dev->name, dev)) { | ||
244 | printk(KERN_ERR PFX "Couldn't get IRQ %d\n", dev->irq); | ||
245 | goto failed; | ||
246 | } | ||
247 | card->irq_requested = 1; | ||
248 | |||
249 | /* Tell the stack we exist */ | ||
250 | if (register_netdev(dev) != 0) { | ||
251 | printk(KERN_ERR PFX "register_netdev() failed\n"); | ||
252 | goto failed; | ||
253 | } | ||
254 | printk(KERN_DEBUG PFX "Card registered for interface %s\n", dev->name); | ||
255 | card->ndev_registered = 1; | ||
256 | return 0; | ||
257 | failed: | ||
258 | airport_detach(mdev); | ||
259 | return -ENODEV; | ||
260 | } /* airport_attach */ | ||
261 | |||
262 | |||
263 | static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION | ||
264 | " (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; | ||
265 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
266 | MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); | ||
267 | MODULE_LICENSE("Dual MPL/GPL"); | ||
268 | |||
269 | static struct of_match airport_match[] = | ||
270 | { | ||
271 | { | ||
272 | .name = "radio", | ||
273 | .type = OF_ANY_MATCH, | ||
274 | .compatible = OF_ANY_MATCH | ||
275 | }, | ||
276 | {}, | ||
277 | }; | ||
278 | |||
279 | static struct macio_driver airport_driver = | ||
280 | { | ||
281 | .name = DRIVER_NAME, | ||
282 | .match_table = airport_match, | ||
283 | .probe = airport_attach, | ||
284 | .remove = airport_detach, | ||
285 | .suspend = airport_suspend, | ||
286 | .resume = airport_resume, | ||
287 | }; | ||
288 | |||
289 | static int __init | ||
290 | init_airport(void) | ||
291 | { | ||
292 | printk(KERN_DEBUG "%s\n", version); | ||
293 | |||
294 | return macio_register_driver(&airport_driver); | ||
295 | } | ||
296 | |||
297 | static void __exit | ||
298 | exit_airport(void) | ||
299 | { | ||
300 | return macio_unregister_driver(&airport_driver); | ||
301 | } | ||
302 | |||
303 | module_init(init_airport); | ||
304 | module_exit(exit_airport); | ||