add chaos_calmer branch
[15.05/openwrt.git] / package / kernel / 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->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_BCM47XX_SSB
272         case BCM47XX_BUS_TYPE_SSB:
273                 active_bus_type = WL_GLUE_BUS_TYPE_SSB;
274                 break;
275 #endif /* CONFIG_BCM47XX_SSB */
276
277 #ifdef CONFIG_BCM47XX_BCMA
278         case BCM47XX_BUS_TYPE_BCMA:
279                 active_bus_type = WL_GLUE_BUS_TYPE_BCMA;
280                 break;
281 #endif /* CONFIG_BCM47XX_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);