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 24MODULE_AUTHOR("Jo-Philipp Wich (jow@openwrt.org)"); 25MODULE_DESCRIPTION("Broadcom WL SSB/BCMA compatibility layer"); 26MODULE_LICENSE("GPL"); 27 28static wl_glue_attach_cb_t attach_cb = NULL; 29static wl_glue_remove_cb_t remove_cb = NULL; 30static enum wl_glue_bus_type active_bus_type = WL_GLUE_BUS_TYPE_UNSPEC; 31static int wl_glue_attached = 0; 32 33 34#ifdef CONFIG_SSB 35static 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 65static 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 75static const struct ssb_device_id wl_glue_ssb_tbl[] = { 76 SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, SSB_ANY_REV), 77 {}, 78}; 79 80static 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 89static 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 123static 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 133static 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 {}, 136}; 137 138static 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 147void wl_glue_set_attach_callback(wl_glue_attach_cb_t cb) 148{ 149 attach_cb = cb; 150} 151EXPORT_SYMBOL(wl_glue_set_attach_callback); 152 153void wl_glue_set_remove_callback(wl_glue_remove_cb_t cb) 154{ 155 remove_cb = cb; 156} 157EXPORT_SYMBOL(wl_glue_set_remove_callback); 158 159int 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} 191EXPORT_SYMBOL(wl_glue_register); 192 193int 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} 230EXPORT_SYMBOL(wl_glue_unregister); 231 232struct 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} 258EXPORT_SYMBOL(wl_glue_get_dmadev); 259 260 261static 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 301static 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 314module_init(wl_glue_init); 315module_exit(wl_glue_exit); 316