2 * Linux platform device for DHD WLAN adapter
4 * Copyright (C) 1999-2015, Broadcom Corporation
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
24 * $Id: dhd_linux_platdev.c 401742 2013-05-13 15:03:21Z $
27 #include <linux/kernel.h>
28 #include <linux/module.h>
29 #include <linux/init.h>
30 #include <linux/platform_device.h>
32 #include <linux_osl.h>
34 #include <dngl_stats.h>
37 #include <dhd_linux.h>
38 #include <wl_android.h>
39 #if defined(CONFIG_WIFI_CONTROL_FUNC)
40 #include <linux/wlan_plat.h>
43 #if !defined(CONFIG_WIFI_CONTROL_FUNC)
44 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 58))
45 #define WLAN_PLAT_NODFS_FLAG 0x01
46 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 58) */
47 struct wifi_platform_data {
48 int (*set_power)(int val);
49 int (*set_reset)(int val);
50 int (*set_carddetect)(int val);
51 void *(*mem_prealloc)(int section, unsigned long size);
52 int (*get_mac_addr)(unsigned char *buf);
53 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 58))
54 void *(*get_country_code)(char *ccode, u32 flags);
56 void *(*get_country_code)(char *ccode);
57 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 58) */
59 #endif /* CONFIG_WIFI_CONTROL_FUNC */
61 #define WIFI_PLAT_NAME "bcmdhd_wlan"
62 #define WIFI_PLAT_NAME2 "bcm4329_wlan"
63 #define WIFI_PLAT_EXT "bcmdhd_wifi_platform"
65 bool cfg_multichip = FALSE;
66 bcmdhd_wifi_platdata_t *dhd_wifi_platdata = NULL;
67 static int wifi_plat_dev_probe_ret = 0;
68 static bool is_power_on = FALSE;
70 static bool dts_enabled = TRUE;
71 extern struct resource dhd_wlan_resources;
72 extern struct wifi_platform_data dhd_wlan_control;
74 static bool dts_enabled = FALSE;
75 struct resource dhd_wlan_resources = {0};
76 struct wifi_platform_data dhd_wlan_control = {0};
77 #endif /* CONFIG_OF && !defined(CONFIG_ARCH_MSM) */
79 static int dhd_wifi_platform_load(void);
81 extern void* wl_cfg80211_get_dhdp(void);
83 #ifdef ENABLE_4335BT_WAR
84 extern int bcm_bt_lock(int cookie);
85 extern void bcm_bt_unlock(int cookie);
86 static int lock_cookie_wifi = 'W' | 'i'<<8 | 'F'<<16 | 'i'<<24; /* cookie is "WiFi" */
87 #endif /* ENABLE_4335BT_WAR */
89 wifi_adapter_info_t* dhd_wifi_platform_get_adapter(uint32 bus_type, uint32 bus_num, uint32 slot_num)
93 if (dhd_wifi_platdata == NULL)
96 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
97 wifi_adapter_info_t *adapter = &dhd_wifi_platdata->adapters[i];
98 if ((adapter->bus_type == -1 || adapter->bus_type == bus_type) &&
99 (adapter->bus_num == -1 || adapter->bus_num == bus_num) &&
100 (adapter->slot_num == -1 || adapter->slot_num == slot_num)) {
101 DHD_TRACE(("found adapter info '%s'\n", adapter->name));
108 void* wifi_platform_prealloc(wifi_adapter_info_t *adapter, int section, unsigned long size)
110 void *alloc_ptr = NULL;
111 struct wifi_platform_data *plat_data;
113 if (!adapter || !adapter->wifi_plat_data)
115 plat_data = adapter->wifi_plat_data;
116 if (plat_data->mem_prealloc) {
117 alloc_ptr = plat_data->mem_prealloc(section, size);
119 DHD_INFO(("success alloc section %d\n", section));
121 bzero(alloc_ptr, size);
126 DHD_ERROR(("%s: failed to alloc static mem section %d\n", __FUNCTION__, section));
130 void* wifi_platform_get_prealloc_func_ptr(wifi_adapter_info_t *adapter)
132 struct wifi_platform_data *plat_data;
134 if (!adapter || !adapter->wifi_plat_data)
136 plat_data = adapter->wifi_plat_data;
137 return plat_data->mem_prealloc;
140 int wifi_platform_get_irq_number(wifi_adapter_info_t *adapter, unsigned long *irq_flags_ptr)
145 *irq_flags_ptr = adapter->intr_flags;
146 return adapter->irq_num;
149 int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long msec)
152 struct wifi_platform_data *plat_data;
154 if (!adapter || !adapter->wifi_plat_data)
156 plat_data = adapter->wifi_plat_data;
158 DHD_ERROR(("%s = %d\n", __FUNCTION__, on));
159 if (plat_data->set_power) {
160 #ifdef ENABLE_4335BT_WAR
162 printk("WiFi: trying to acquire BT lock\n");
163 if (bcm_bt_lock(lock_cookie_wifi) != 0)
164 printk("** WiFi: timeout in acquiring bt lock**\n");
165 printk("%s: btlock acquired\n", __FUNCTION__);
168 /* For a exceptional case, release btlock */
169 bcm_bt_unlock(lock_cookie_wifi);
171 #endif /* ENABLE_4335BT_WAR */
173 err = plat_data->set_power(on);
187 int wifi_platform_bus_enumerate(wifi_adapter_info_t *adapter, bool device_present)
190 struct wifi_platform_data *plat_data;
192 if (!adapter || !adapter->wifi_plat_data)
194 plat_data = adapter->wifi_plat_data;
196 DHD_ERROR(("%s device present %d\n", __FUNCTION__, device_present));
197 if (plat_data->set_carddetect) {
198 err = plat_data->set_carddetect(device_present);
204 int wifi_platform_get_mac_addr(wifi_adapter_info_t *adapter, unsigned char *buf)
206 struct wifi_platform_data *plat_data;
208 DHD_ERROR(("%s\n", __FUNCTION__));
209 if (!buf || !adapter || !adapter->wifi_plat_data)
211 plat_data = adapter->wifi_plat_data;
212 if (plat_data->get_mac_addr) {
213 return plat_data->get_mac_addr(buf);
218 void *wifi_platform_get_country_code(wifi_adapter_info_t *adapter, char *ccode)
220 /* get_country_code was added after 2.6.39 */
221 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
222 struct wifi_platform_data *plat_data;
224 if (!ccode || !adapter || !adapter->wifi_plat_data)
226 plat_data = adapter->wifi_plat_data;
228 DHD_TRACE(("%s\n", __FUNCTION__));
229 if (plat_data->get_country_code) {
230 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 58))
231 return plat_data->get_country_code(ccode, WLAN_PLAT_NODFS_FLAG);
233 return plat_data->get_country_code(ccode);
234 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 58)) */
236 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */
241 static int wifi_plat_dev_drv_probe(struct platform_device *pdev)
243 struct resource *resource;
244 wifi_adapter_info_t *adapter;
246 /* Android style wifi platform data device ("bcmdhd_wlan" or "bcm4329_wlan")
247 * is kept for backward compatibility and supports only 1 adapter
249 ASSERT(dhd_wifi_platdata != NULL);
250 ASSERT(dhd_wifi_platdata->num_adapters == 1);
251 adapter = &dhd_wifi_platdata->adapters[0];
252 adapter->wifi_plat_data = (struct wifi_platform_data *)(pdev->dev.platform_data);
254 resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq");
255 if (resource == NULL)
256 resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcm4329_wlan_irq");
258 adapter->irq_num = resource->start;
259 adapter->intr_flags = resource->flags;
262 wifi_plat_dev_probe_ret = dhd_wifi_platform_load();
263 return wifi_plat_dev_probe_ret;
266 static int wifi_plat_dev_drv_remove(struct platform_device *pdev)
268 wifi_adapter_info_t *adapter;
270 /* Android style wifi platform data device ("bcmdhd_wlan" or "bcm4329_wlan")
271 * is kept for backward compatibility and supports only 1 adapter
273 ASSERT(dhd_wifi_platdata != NULL);
274 ASSERT(dhd_wifi_platdata->num_adapters == 1);
275 adapter = &dhd_wifi_platdata->adapters[0];
277 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
278 wifi_platform_bus_enumerate(adapter, FALSE);
284 static int wifi_plat_dev_drv_suspend(struct platform_device *pdev, pm_message_t state)
286 DHD_TRACE(("##> %s\n", __FUNCTION__));
287 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY)
288 bcmsdh_oob_intr_set(0);
289 #endif /* (OOB_INTR_ONLY) */
293 static int wifi_plat_dev_drv_resume(struct platform_device *pdev)
295 DHD_TRACE(("##> %s\n", __FUNCTION__));
296 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY)
297 if (dhd_os_check_if_up(wl_cfg80211_get_dhdp()))
298 bcmsdh_oob_intr_set(1);
299 #endif /* (OOB_INTR_ONLY) */
303 static struct platform_driver wifi_platform_dev_driver = {
304 .probe = wifi_plat_dev_drv_probe,
305 .remove = wifi_plat_dev_drv_remove,
306 .suspend = wifi_plat_dev_drv_suspend,
307 .resume = wifi_plat_dev_drv_resume,
309 .name = WIFI_PLAT_NAME,
313 static struct platform_driver wifi_platform_dev_driver_legacy = {
314 .probe = wifi_plat_dev_drv_probe,
315 .remove = wifi_plat_dev_drv_remove,
316 .suspend = wifi_plat_dev_drv_suspend,
317 .resume = wifi_plat_dev_drv_resume,
319 .name = WIFI_PLAT_NAME2,
323 static int wifi_platdev_match(struct device *dev, void *data)
325 char *name = (char*)data;
326 struct platform_device *pdev = to_platform_device(dev);
328 if (strcmp(pdev->name, name) == 0) {
329 DHD_ERROR(("found wifi platform device %s\n", name));
336 static int wifi_ctrlfunc_register_drv(void)
339 struct device *dev1, *dev2;
340 wifi_adapter_info_t *adapter;
342 dev1 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME, wifi_platdev_match);
343 dev2 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME2, wifi_platdev_match);
345 if (dev1 == NULL && dev2 == NULL) {
346 DHD_ERROR(("no wifi platform data, skip\n"));
351 /* multi-chip support not enabled, build one adapter information for
352 * DHD (either SDIO, USB or PCIe)
354 adapter = kzalloc(sizeof(wifi_adapter_info_t), GFP_KERNEL);
355 adapter->name = "DHD generic adapter";
356 adapter->bus_type = -1;
357 adapter->bus_num = -1;
358 adapter->slot_num = -1;
359 adapter->irq_num = -1;
361 wifi_plat_dev_probe_ret = 0;
362 dhd_wifi_platdata = kzalloc(sizeof(bcmdhd_wifi_platdata_t), GFP_KERNEL);
363 dhd_wifi_platdata->num_adapters = 1;
364 dhd_wifi_platdata->adapters = adapter;
367 err = platform_driver_register(&wifi_platform_dev_driver);
369 DHD_ERROR(("%s: failed to register wifi ctrl func driver\n",
375 err = platform_driver_register(&wifi_platform_dev_driver_legacy);
377 DHD_ERROR(("%s: failed to register wifi ctrl func legacy driver\n",
384 struct resource *resource;
385 adapter->wifi_plat_data = (void *)&dhd_wlan_control;
386 resource = &dhd_wlan_resources;
387 adapter->irq_num = resource->start;
388 adapter->intr_flags = resource->flags;
389 wifi_plat_dev_probe_ret = dhd_wifi_platform_load();
392 /* return probe function's return value if registeration succeeded */
393 return wifi_plat_dev_probe_ret;
396 void wifi_ctrlfunc_unregister_drv(void)
398 struct device *dev1, *dev2;
400 dev1 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME, wifi_platdev_match);
401 dev2 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME2, wifi_platdev_match);
403 if (dev1 == NULL && dev2 == NULL)
406 DHD_ERROR(("unregister wifi platform drivers\n"));
408 platform_driver_unregister(&wifi_platform_dev_driver);
410 platform_driver_unregister(&wifi_platform_dev_driver_legacy);
412 wifi_adapter_info_t *adapter;
413 adapter = &dhd_wifi_platdata->adapters[0];
415 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
416 wifi_platform_bus_enumerate(adapter, FALSE);
419 kfree(dhd_wifi_platdata->adapters);
420 dhd_wifi_platdata->adapters = NULL;
421 dhd_wifi_platdata->num_adapters = 0;
422 kfree(dhd_wifi_platdata);
423 dhd_wifi_platdata = NULL;
426 static int bcmdhd_wifi_plat_dev_drv_probe(struct platform_device *pdev)
428 dhd_wifi_platdata = (bcmdhd_wifi_platdata_t *)(pdev->dev.platform_data);
430 return dhd_wifi_platform_load();
433 static int bcmdhd_wifi_plat_dev_drv_remove(struct platform_device *pdev)
436 wifi_adapter_info_t *adapter;
437 ASSERT(dhd_wifi_platdata != NULL);
439 /* power down all adapters */
440 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
441 adapter = &dhd_wifi_platdata->adapters[i];
442 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
443 wifi_platform_bus_enumerate(adapter, FALSE);
448 static struct platform_driver dhd_wifi_platform_dev_driver = {
449 .probe = bcmdhd_wifi_plat_dev_drv_probe,
450 .remove = bcmdhd_wifi_plat_dev_drv_remove,
452 .name = WIFI_PLAT_EXT,
456 int dhd_wifi_platform_register_drv(void)
461 /* register Broadcom wifi platform data driver if multi-chip is enabled,
462 * otherwise use Android style wifi platform data (aka wifi control function)
465 * to support multi-chip DHD, Broadcom wifi platform data device must
466 * be added in kernel early boot (e.g. board config file).
469 dev = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_EXT, wifi_platdev_match);
471 DHD_ERROR(("bcmdhd wifi platform data device not found!!\n"));
474 err = platform_driver_register(&dhd_wifi_platform_dev_driver);
476 err = wifi_ctrlfunc_register_drv();
478 /* no wifi ctrl func either, load bus directly and ignore this error */
481 /* wifi ctrl function does not exist */
482 err = dhd_wifi_platform_load();
484 /* unregister driver due to initialization failure */
485 wifi_ctrlfunc_unregister_drv();
494 static int dhd_wifi_platform_load_pcie(void)
497 err = dhd_bus_register();
501 static int dhd_wifi_platform_load_pcie(void)
508 void dhd_wifi_platform_unregister_drv(void)
511 platform_driver_unregister(&dhd_wifi_platform_dev_driver);
513 wifi_ctrlfunc_unregister_drv();
516 extern int dhd_watchdog_prio;
517 extern int dhd_dpc_prio;
518 extern uint dhd_deferred_tx;
519 #if defined(BCMLXSDMMC)
520 extern struct semaphore dhd_registration_sem;
523 static int dhd_wifi_platform_load_sdio(void)
527 wifi_adapter_info_t *adapter;
530 BCM_REFERENCE(adapter);
531 /* Sanity check on the module parameters
532 * - Both watchdog and DPC as tasklets are ok
533 * - If both watchdog and DPC are threads, TX must be deferred
535 if (!(dhd_watchdog_prio < 0 && dhd_dpc_prio < 0) &&
536 !(dhd_watchdog_prio >= 0 && dhd_dpc_prio >= 0 && dhd_deferred_tx))
539 #if defined(BCMLXSDMMC)
540 if (dhd_wifi_platdata == NULL) {
541 DHD_ERROR(("DHD wifi platform data is required for Android build\n"));
545 sema_init(&dhd_registration_sem, 0);
546 /* power up all adapters */
547 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
548 bool chip_up = FALSE;
549 int retry = POWERUP_MAX_RETRY;
550 struct semaphore dhd_chipup_sem;
552 adapter = &dhd_wifi_platdata->adapters[i];
554 DHD_ERROR(("Power-up adapter '%s'\n", adapter->name));
555 DHD_INFO((" - irq %d [flags %d], firmware: %s, nvram: %s\n",
556 adapter->irq_num, adapter->intr_flags, adapter->fw_path, adapter->nv_path));
557 DHD_INFO((" - bus type %d, bus num %d, slot num %d\n\n",
558 adapter->bus_type, adapter->bus_num, adapter->slot_num));
561 sema_init(&dhd_chipup_sem, 0);
562 err = dhd_bus_reg_sdio_notify(&dhd_chipup_sem);
564 DHD_ERROR(("%s dhd_bus_reg_sdio_notify fail(%d)\n\n",
568 err = wifi_platform_set_power(adapter, TRUE, WIFI_TURNON_DELAY);
570 /* WL_REG_ON state unknown, Power off forcely */
571 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
574 wifi_platform_bus_enumerate(adapter, TRUE);
578 if (down_timeout(&dhd_chipup_sem, msecs_to_jiffies(POWERUP_WAIT_MS)) == 0) {
579 dhd_bus_unreg_sdio_notify();
584 DHD_ERROR(("failed to power up %s, %d retry left\n", adapter->name, retry));
585 dhd_bus_unreg_sdio_notify();
586 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
587 wifi_platform_bus_enumerate(adapter, FALSE);
591 DHD_ERROR(("failed to power up %s, max retry reached**\n", adapter->name));
597 err = dhd_bus_register();
600 DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__));
606 * Wait till MMC sdio_register_driver callback called and made driver attach.
607 * It's needed to make sync up exit from dhd insmod and
608 * Kernel MMC sdio device callback registration
610 err = down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT));
612 DHD_ERROR(("%s: sdio_register_driver timeout or error \n", __FUNCTION__));
613 dhd_bus_unregister();
620 /* power down all adapters */
621 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
622 adapter = &dhd_wifi_platdata->adapters[i];
623 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
624 wifi_platform_bus_enumerate(adapter, FALSE);
628 /* x86 bring-up PC needs no power-up operations */
629 err = dhd_bus_register();
636 static int dhd_wifi_platform_load_usb(void)
641 static int dhd_wifi_platform_load()
647 if ((err = dhd_wifi_platform_load_usb()))
649 else if ((err = dhd_wifi_platform_load_sdio()))
652 err = dhd_wifi_platform_load_pcie();
658 wl_android_post_init();