bf009e5bbeec0f73c5fdc4c06b9b47d9ae5e187e
[openwrt/svn-archive/archive.git] / package / broadcom-wl / src / glue / wl_glue.c
1 /*
2 * wl_glue.c: Broadcom WL support module providing a unified SSB/BCMA handling.
3 * Copyright (C) 2011 Jo-Philipp Wich <jow@openwrt.org>
4 */
5
6 #include "wl_glue.h"
7
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/init.h>
11
12 #ifdef CONFIG_BCM47XX
13 #include <bcm47xx.h>
14 #endif
15
16 #ifdef CONFIG_SSB
17 #include <linux/ssb/ssb.h>
18 #endif
19
20 #ifdef CONFIG_BCMA
21 #include <linux/bcma/bcma.h>
22 #endif
23
24 MODULE_AUTHOR("Jo-Philipp Wich (jow@openwrt.org)");
25 MODULE_DESCRIPTION("Broadcom WL SSB/BCMA compatibility layer");
26 MODULE_LICENSE("GPL");
27
28 static wl_glue_attach_cb_t attach_cb = NULL;
29 static wl_glue_remove_cb_t remove_cb = NULL;
30 static enum wl_glue_bus_type active_bus_type = WL_GLUE_BUS_TYPE_UNSPEC;
31 static int wl_glue_attached = 0;
32
33
34 #ifdef CONFIG_SSB
35 static int wl_glue_ssb_probe(struct ssb_device *dev, const struct ssb_device_id *id)
36 {
37 void *mmio;
38 void *wldev;
39
40 if (!attach_cb)
41 {
42 pr_err("No attach callback registered\n");
43 return -ENOSYS;
44 }
45
46 if (dev->bus->bustype != SSB_BUSTYPE_SSB)
47 {
48 pr_err("Attaching to SSB behind PCI is not supported. Please remove the b43 ssb bridge\n");
49 return -EINVAL;
50 }
51
52 mmio = (void *) 0x18000000 + dev->core_index * 0x1000;
53 wldev = attach_cb(id->vendor, id->coreid, (ulong)mmio, dev, dev->irq);
54
55 if (!wldev)
56 {
57 pr_err("The attach callback failed, SSB probe aborted\n");
58 return -ENODEV;
59 }
60
61 ssb_set_drvdata(dev, wldev);
62 return 0;
63 }
64
65 static void wl_glue_ssb_remove(struct ssb_device *dev)
66 {
67 void *wldev = ssb_get_drvdata(dev);
68
69 if (remove_cb)
70 remove_cb(wldev);
71
72 ssb_set_drvdata(dev, NULL);
73 }
74
75 static const struct ssb_device_id wl_glue_ssb_tbl[] = {
76 SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, SSB_ANY_REV),
77 SSB_DEVTABLE_END
78 };
79
80 static struct ssb_driver wl_glue_ssb_driver = {
81 .name = KBUILD_MODNAME,
82 .id_table = wl_glue_ssb_tbl,
83 .probe = wl_glue_ssb_probe,
84 .remove = wl_glue_ssb_remove,
85 };
86 #endif /* CONFIG_SSB */
87
88 #ifdef CONFIG_BCMA
89 static int wl_glue_bcma_probe(struct bcma_device *dev)
90 {
91 void *wldev;
92
93 if (!attach_cb)
94 {
95 pr_err("No attach callback registered\n");
96 return -ENOSYS;
97 }
98
99 if (dev->bus->hosttype != BCMA_HOSTTYPE_SOC)
100 {
101 pr_err("Unsupported BCMA bus type %d\n", dev->bus->hosttype);
102 return -EINVAL;
103 }
104
105 /*
106 * NB:
107 * 0x18000000 = BCMA_ADDR_BASE
108 * 0x1000 = BCMA_CORE_SIZE
109 */
110
111 wldev = attach_cb(dev->id.manuf, dev->id.id, (ulong)dev->io_addr, dev, dev->irq);
112
113 if (!wldev)
114 {
115 pr_err("The attach callback failed, BCMA probe aborted\n");
116 return -ENODEV;
117 }
118
119 bcma_set_drvdata(dev, wldev);
120 return 0;
121 }
122
123 static void wl_glue_bcma_remove(struct bcma_device *dev)
124 {
125 void *wldev = bcma_get_drvdata(dev);
126
127 if (remove_cb)
128 remove_cb(wldev);
129
130 bcma_set_drvdata(dev, NULL);
131 }
132
133 static const struct bcma_device_id wl_glue_bcma_tbl[] = {
134 BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, BCMA_ANY_REV, BCMA_ANY_CLASS),
135 BCMA_CORETABLE_END
136 };
137
138 static struct bcma_driver wl_glue_bcma_driver = {
139 .name = KBUILD_MODNAME,
140 .id_table = wl_glue_bcma_tbl,
141 .probe = wl_glue_bcma_probe,
142 .remove = wl_glue_bcma_remove,
143 };
144 #endif /* CONFIG_BCMA */
145
146
147 void wl_glue_set_attach_callback(wl_glue_attach_cb_t cb)
148 {
149 attach_cb = cb;
150 }
151 EXPORT_SYMBOL(wl_glue_set_attach_callback);
152
153 void wl_glue_set_remove_callback(wl_glue_remove_cb_t cb)
154 {
155 remove_cb = cb;
156 }
157 EXPORT_SYMBOL(wl_glue_set_remove_callback);
158
159 int wl_glue_register(void)
160 {
161 int err;
162
163 switch(active_bus_type)
164 {
165 #ifdef CONFIG_SSB
166 case WL_GLUE_BUS_TYPE_SSB:
167 err = ssb_driver_register(&wl_glue_ssb_driver);
168 break;
169 #endif /* CONFIG_SSB */
170
171 #ifdef CONFIG_BCMA
172 case WL_GLUE_BUS_TYPE_BCMA:
173 err = bcma_driver_register(&wl_glue_bcma_driver);
174 break;
175 #endif /* CONFIG_BCMA */
176
177 default:
178 pr_err("Not attaching through glue driver due to unsupported bus\n");
179 err = -ENOSYS;
180 break;
181 }
182
183 if (!err)
184 {
185 pr_info("SSB/BCMA glue driver successfully attached\n");
186 wl_glue_attached = 1;
187 }
188
189 return err;
190 }
191 EXPORT_SYMBOL(wl_glue_register);
192
193 int wl_glue_unregister(void)
194 {
195 int err;
196
197 if (!wl_glue_attached)
198 return -ENOSYS;
199
200 switch (active_bus_type)
201 {
202 #ifdef CONFIG_SSB
203 case WL_GLUE_BUS_TYPE_SSB:
204 ssb_driver_unregister(&wl_glue_ssb_driver);
205 err = 0;
206 break;
207 #endif /* CONFIG_SSB */
208
209 #ifdef CONFIG_BCMA
210 case WL_GLUE_BUS_TYPE_BCMA:
211 bcma_driver_unregister(&wl_glue_bcma_driver);
212 err = 0;
213 break;
214 #endif /* CONFIG_BCMA */
215
216 default:
217 pr_err("Not removing glue driver due to unsupported bus\n");
218 err = -ENOSYS;
219 break;
220 }
221
222 if (!err)
223 {
224 pr_info("SSB/BCMA glue driver successfully detached\n");
225 wl_glue_attached = 0;
226 }
227
228 return err;
229 }
230 EXPORT_SYMBOL(wl_glue_unregister);
231
232 struct device * wl_glue_get_dmadev(void *dev)
233 {
234 struct device *dma_dev;
235
236 switch (active_bus_type)
237 {
238 #ifdef CONFIG_SSB
239 case WL_GLUE_BUS_TYPE_SSB:
240 dma_dev = ((struct ssb_device *)dev)->dma_dev;
241 break;
242 #endif /* CONFIG_SSB */
243
244 #ifdef CONFIG_BCMA
245 case WL_GLUE_BUS_TYPE_BCMA:
246 dma_dev = ((struct bcma_device *)dev)->dma_dev;
247 break;
248 #endif /* CONFIG_BCMA */
249
250 default:
251 BUG();
252 dma_dev = NULL;
253 break;
254 }
255
256 return dma_dev;
257 }
258 EXPORT_SYMBOL(wl_glue_get_dmadev);
259
260
261 static int __init wl_glue_init(void)
262 {
263 #ifdef CONFIG_BCM47XX
264 /*
265 * BCM47xx currently supports either SSB or BCMA bus,
266 * determine the used one from the info set by the
267 * platform setup code.
268 */
269 switch (bcm47xx_bus_type)
270 {
271 #ifdef CONFIG_SSB
272 case BCM47XX_BUS_TYPE_SSB:
273 active_bus_type = WL_GLUE_BUS_TYPE_SSB;
274 break;
275 #endif /* CONFIG_SSB */
276
277 #ifdef CONFIG_BCMA
278 case BCM47XX_BUS_TYPE_BCMA:
279 active_bus_type = WL_GLUE_BUS_TYPE_BCMA;
280 break;
281 #endif /* CONFIG_BCMA */
282 }
283 #endif /* CONFIG_BCM47XX */
284
285 #ifdef CONFIG_BCM63XX
286 #ifdef CONFIG_SSB
287 /*
288 * BCM63xx currently only uses SSB, so assume that.
289 */
290 active_bus_type = WL_GLUE_BUS_TYPE_SSB;
291 #endif /* CONFIG_SSB */
292 #endif /* CONFIG_BCM63XX */
293
294 /* do not fail here, let wl_glue_register() return -ENOSYS later */
295 if (active_bus_type == WL_GLUE_BUS_TYPE_UNSPEC)
296 pr_err("Unable to determine used system bus type\n");
297
298 return 0;
299 }
300
301 static void __exit wl_glue_exit(void)
302 {
303 if (wl_glue_attached)
304 {
305 if (wl_glue_unregister())
306 pr_err("Failed to unregister glue driver\n");
307
308 wl_glue_attached = 0;
309 }
310
311 return;
312 }
313
314 module_init(wl_glue_init);
315 module_exit(wl_glue_exit);