blob: 562781e40abc339dc153bf9f0759e30b12432c96 [file] [log] [blame]
Victor Hsue0cd0e72021-06-08 11:05:03 +08001/*
2 * Linux platform device for DHD WLAN adapter
3 *
4 * Copyright (C) 2021, Broadcom.
5 *
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:
11 *
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.
19 *
20 *
21 * <<Broadcom-WL-IPTag/Open:>>
22 *
23 * $Id$
24 */
25#include <typedefs.h>
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/platform_device.h>
30#include <bcmutils.h>
31#include <linux_osl.h>
32#include <dhd_dbg.h>
33#include <dngl_stats.h>
34#include <dhd.h>
35#include <dhd_bus.h>
36#include <dhd_linux.h>
37#if defined(OEM_ANDROID)
38#include <wl_android.h>
Stephen Chu6869e162022-09-26 16:24:04 +080039#include <dhd_plat.h>
Victor Hsue0cd0e72021-06-08 11:05:03 +080040#endif
41#if defined(CONFIG_WIFI_CONTROL_FUNC) || defined(CUSTOMER_HW4)
42#include <linux/wlan_plat.h>
Victor Hsue0cd0e72021-06-08 11:05:03 +080043#endif /* CONFIG_WIFI_CONTROL_FUNC */
44#ifdef BCMDBUS
45#include <dbus.h>
46#endif
47#ifdef CONFIG_DHD_DTS
48#include<linux/regulator/consumer.h>
49#include<linux/of_gpio.h>
50#endif /* CONFIG_DHD_DTS */
51#define WIFI_PLAT_NAME "bcmdhd_wlan"
52#define WIFI_PLAT_NAME2 "bcm4329_wlan"
53#define WIFI_PLAT_EXT "bcmdhd_wifi_platform"
54
55#ifdef DHD_WIFI_SHUTDOWN
56extern void wifi_plat_dev_drv_shutdown(struct platform_device *pdev);
57#endif
58
59#ifdef CONFIG_DHD_DTS
60struct regulator *wifi_regulator = NULL;
61#endif /* CONFIG_DHD_DTS */
62
63bool cfg_multichip = FALSE;
64bcmdhd_wifi_platdata_t *dhd_wifi_platdata = NULL;
65static int wifi_plat_dev_probe_ret = 0;
66static bool is_power_on = FALSE;
67/* XXX Some Qualcomm based CUSTOMER_HW4 platforms are using platform
68 * device structure even if the Kernel uses device tree structure.
69 * Therefore, the CONFIG_ARCH_MSM condition is temporarly remained
70 * to support in this case.
71 */
72#if !defined(CONFIG_DHD_DTS)
Stephen Chuf4cfcc52021-06-18 23:51:05 +080073#if defined(BOARD_MODULAR_INIT)
Victor Hsue0cd0e72021-06-08 11:05:03 +080074static bool dts_enabled = TRUE;
75extern struct resource dhd_wlan_resources;
76extern struct wifi_platform_data dhd_wlan_control;
Stephen Chuf4cfcc52021-06-18 23:51:05 +080077#else // !defined(BOARD_MODULAR_INIT)
Victor Hsue0cd0e72021-06-08 11:05:03 +080078static bool dts_enabled = FALSE;
79#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
80#pragma GCC diagnostic push
81#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
82#endif
83struct resource dhd_wlan_resources = {0};
84struct wifi_platform_data dhd_wlan_control = {0};
85#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
86#pragma GCC diagnostic pop
87#endif
Stephen Chuf4cfcc52021-06-18 23:51:05 +080088#endif /* !defined(BOARD_MODULAR_INIT) */
Victor Hsue0cd0e72021-06-08 11:05:03 +080089#endif /* !defind(CONFIG_DHD_DTS) */
90
91static int dhd_wifi_platform_load(void);
92
93extern void* wl_cfg80211_get_dhdp(struct net_device *dev);
94
95// modify for compatibility
96#if defined(BCMDHD_MODULAR) && defined(BOARD_MODULAR_INIT)
97extern int dhd_wlan_init(void);
98extern int dhd_wlan_deinit(void);
99#ifdef WBRC
100extern int wbrc_init(void);
101extern void wbrc_exit(void);
102#endif /* WBRC */
103#endif /* defined(BCMDHD_MODULAR) && defined(BOARD_MODULAR_INIT) */
104
105#ifdef ENABLE_4335BT_WAR
106extern int bcm_bt_lock(int cookie);
107extern void bcm_bt_unlock(int cookie);
108static int lock_cookie_wifi = 'W' | 'i'<<8 | 'F'<<16 | 'i'<<24; /* cookie is "WiFi" */
109#endif /* ENABLE_4335BT_WAR */
110
111#ifdef BCM4335_XTAL_WAR
112extern bool check_bcm4335_rev(void);
113#endif /* BCM4335_XTAL_WAR */
114
Stephen Chu6869e162022-09-26 16:24:04 +0800115#if defined(CONFIG_X86)
116#define PCIE_RC_VENDOR_ID 0x8086
117#define PCIE_RC_DEVICE_ID 0x9c1a
118#elif defined(CONFIG_ARCH_TEGRA)
119#define PCIE_RC_VENDOR_ID 0x14e4
120#define PCIE_RC_DEVICE_ID 0x4347
121#else /* CONFIG_ARCH_TEGRA */
122/* Dummy defn */
123#define PCIE_RC_VENDOR_ID 0xffff
124#define PCIE_RC_DEVICE_ID 0xffff
125#endif /* CONFIG_X86 */
126
Victor Hsue0cd0e72021-06-08 11:05:03 +0800127wifi_adapter_info_t* dhd_wifi_platform_get_adapter(uint32 bus_type, uint32 bus_num, uint32 slot_num)
128{
129 int i;
130
131 if (dhd_wifi_platdata == NULL)
132 return NULL;
133
134 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
135 wifi_adapter_info_t *adapter = &dhd_wifi_platdata->adapters[i];
136 if ((adapter->bus_type == -1 || adapter->bus_type == bus_type) &&
137 (adapter->bus_num == -1 || adapter->bus_num == bus_num) &&
138 (adapter->slot_num == -1 || adapter->slot_num == slot_num)) {
139 DHD_TRACE(("found adapter info '%s'\n", adapter->name));
140 return adapter;
141 }
142 }
143 return NULL;
144}
145
146void* wifi_platform_prealloc(wifi_adapter_info_t *adapter, int section, unsigned long size)
147{
148 void *alloc_ptr = NULL;
149 struct wifi_platform_data *plat_data;
150
151 if (!adapter || !adapter->wifi_plat_data)
152 return NULL;
153 plat_data = adapter->wifi_plat_data;
154 if (plat_data->mem_prealloc) {
155 alloc_ptr = plat_data->mem_prealloc(section, size);
156 if (alloc_ptr) {
157 DHD_INFO(("success alloc section %d\n", section));
158 if (size != 0L)
159 bzero(alloc_ptr, size);
160 return alloc_ptr;
161 }
162 }
163
164 DHD_ERROR(("%s: failed to alloc static mem section %d\n", __FUNCTION__, section));
165 return NULL;
166}
167
168void* wifi_platform_get_prealloc_func_ptr(wifi_adapter_info_t *adapter)
169{
170 struct wifi_platform_data *plat_data;
171
172 if (!adapter || !adapter->wifi_plat_data)
173 return NULL;
174 plat_data = adapter->wifi_plat_data;
175 return plat_data->mem_prealloc;
176}
177
178int wifi_platform_get_irq_number(wifi_adapter_info_t *adapter, unsigned long *irq_flags_ptr)
179{
180 if (adapter == NULL)
181 return -1;
182 if (irq_flags_ptr)
183 *irq_flags_ptr = adapter->intr_flags;
184 return adapter->irq_num;
185}
186
187int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long msec)
188{
189 int err = 0;
190#ifdef CONFIG_DHD_DTS
191 if (on) {
192 err = regulator_enable(wifi_regulator);
193 is_power_on = TRUE;
194 }
195 else {
196 err = regulator_disable(wifi_regulator);
197 is_power_on = FALSE;
198 }
199 if (err < 0)
200 DHD_ERROR(("%s: regulator enable/disable failed", __FUNCTION__));
201#else
202 struct wifi_platform_data *plat_data;
203
204 if (!adapter || !adapter->wifi_plat_data)
205 return -EINVAL;
206 plat_data = adapter->wifi_plat_data;
207
208 DHD_ERROR(("%s = %d, delay: %lu msec\n", __FUNCTION__, on, msec));
209 if (plat_data->set_power) {
210#ifdef ENABLE_4335BT_WAR
211 if (on) {
212 printk("WiFi: trying to acquire BT lock\n");
213 if (bcm_bt_lock(lock_cookie_wifi) != 0)
214 printk("** WiFi: timeout in acquiring bt lock**\n");
215 printk("%s: btlock acquired\n", __FUNCTION__);
216 }
217 else {
218 /* For a exceptional case, release btlock */
219 bcm_bt_unlock(lock_cookie_wifi);
220 }
221#endif /* ENABLE_4335BT_WAR */
222
223#ifdef BCM4335_XTAL_WAR
224 err = plat_data->set_power(on, check_bcm4335_rev());
225#else /* BCM4335_XTAL_WAR */
226 err = plat_data->set_power(on);
227#endif /* BCM4335_XTAL_WAR */
228 }
229
230 if (msec && !err)
231 OSL_SLEEP(msec);
232
233 if (on && !err)
234 is_power_on = TRUE;
235 else
236 is_power_on = FALSE;
237
238#endif /* CONFIG_DHD_DTS */
239
240 return err;
241}
242
243int wifi_platform_bus_enumerate(wifi_adapter_info_t *adapter, bool device_present)
244{
245 int err = 0;
246 struct wifi_platform_data *plat_data;
247
248 if (!adapter || !adapter->wifi_plat_data)
249 return -EINVAL;
250 plat_data = adapter->wifi_plat_data;
251
252 DHD_ERROR(("%s device present %d\n", __FUNCTION__, device_present));
253 if (plat_data->set_carddetect) {
254 err = plat_data->set_carddetect(device_present);
255 }
256 return err;
257
258}
259
260int wifi_platform_get_mac_addr(wifi_adapter_info_t *adapter, unsigned char *buf)
261{
262 struct wifi_platform_data *plat_data;
263
264 DHD_ERROR(("%s\n", __FUNCTION__));
265 if (!buf || !adapter || !adapter->wifi_plat_data)
266 return -EINVAL;
267 plat_data = adapter->wifi_plat_data;
268 if (plat_data->get_mac_addr) {
269 return plat_data->get_mac_addr(buf);
270 }
271 return -EOPNOTSUPP;
272}
273
274#ifdef DHD_COREDUMP
275int wifi_platform_set_coredump(wifi_adapter_info_t *adapter, const char *buf,
276 int buf_len, const char *info)
277{
278 struct wifi_platform_data *plat_data;
279
280 DHD_ERROR(("%s\n", __FUNCTION__));
281 if (!buf || !adapter || !adapter->wifi_plat_data)
282 return -EINVAL;
283 plat_data = adapter->wifi_plat_data;
284 if (plat_data->set_coredump) {
285 return plat_data->set_coredump(buf, buf_len, info);
286 }
287 return -EOPNOTSUPP;
288}
289#endif /* DHD_COREDUMP */
290
291#ifdef CUSTOM_COUNTRY_CODE
292void *wifi_platform_get_country_code(wifi_adapter_info_t *adapter, char *ccode, u32 flags)
293#else
294void *wifi_platform_get_country_code(wifi_adapter_info_t *adapter, char *ccode)
295#endif /* CUSTOM_COUNTRY_CODE */
296{
297 /* get_country_code was added after 2.6.39 */
298#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
299 struct wifi_platform_data *plat_data;
300
301 if (!ccode || !adapter || !adapter->wifi_plat_data)
302 return NULL;
303 plat_data = adapter->wifi_plat_data;
304
305 DHD_TRACE(("%s\n", __FUNCTION__));
306 if (plat_data->get_country_code) {
307#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 58))
308 return plat_data->get_country_code(ccode, WLAN_PLAT_NODFS_FLAG);
309#else
310#ifdef CUSTOM_COUNTRY_CODE
311 return plat_data->get_country_code(ccode, flags);
312#else
313 return plat_data->get_country_code(ccode);
314#endif /* CUSTOM_COUNTRY_CODE */
315#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 58)) */
316 }
317#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */
318
319 return NULL;
320}
321
322static int wifi_plat_dev_drv_probe(struct platform_device *pdev)
323{
324 struct resource *resource;
325 wifi_adapter_info_t *adapter;
326#ifdef CONFIG_DHD_DTS
327 int irq, gpio;
328#endif /* CONFIG_DHD_DTS */
329
330 /* Android style wifi platform data device ("bcmdhd_wlan" or "bcm4329_wlan")
331 * is kept for backward compatibility and supports only 1 adapter
332 */
333 ASSERT(dhd_wifi_platdata != NULL);
334 ASSERT(dhd_wifi_platdata->num_adapters == 1);
335 adapter = &dhd_wifi_platdata->adapters[0];
336 adapter->wifi_plat_data = (struct wifi_platform_data *)(pdev->dev.platform_data);
337
338 resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq");
339 if (resource == NULL)
340 resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcm4329_wlan_irq");
341 if (resource) {
342 adapter->irq_num = resource->start;
343 adapter->intr_flags = resource->flags & IRQF_TRIGGER_MASK;
344#ifdef DHD_ISR_NO_SUSPEND
345 adapter->intr_flags |= IRQF_NO_SUSPEND;
346#endif
347 }
348
349#ifdef CONFIG_DHD_DTS
350 wifi_regulator = regulator_get(&pdev->dev, "wlreg_on");
351 if (wifi_regulator == NULL) {
352 DHD_ERROR(("%s regulator is null\n", __FUNCTION__));
353 return -1;
354 }
355
356 /* This is to get the irq for the OOB */
357 gpio = of_get_gpio(pdev->dev.of_node, 0);
358
359 if (gpio < 0) {
360 DHD_ERROR(("%s gpio information is incorrect\n", __FUNCTION__));
361 return -1;
362 }
363 irq = gpio_to_irq(gpio);
364 if (irq < 0) {
365 DHD_ERROR(("%s irq information is incorrect\n", __FUNCTION__));
366 return -1;
367 }
368 adapter->irq_num = irq;
369
370 /* need to change the flags according to our requirement */
371 adapter->intr_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL |
372 IORESOURCE_IRQ_SHAREABLE;
373#endif /* CONFIG_DHD_DTS */
374
375 wifi_plat_dev_probe_ret = dhd_wifi_platform_load();
376 return wifi_plat_dev_probe_ret;
377}
378
379static int wifi_plat_dev_drv_remove(struct platform_device *pdev)
380{
381 wifi_adapter_info_t *adapter;
382
383 /* Android style wifi platform data device ("bcmdhd_wlan" or "bcm4329_wlan")
384 * is kept for backward compatibility and supports only 1 adapter
385 */
386 ASSERT(dhd_wifi_platdata != NULL);
387 ASSERT(dhd_wifi_platdata->num_adapters == 1);
388 adapter = &dhd_wifi_platdata->adapters[0];
389 if (is_power_on) {
390#ifdef BCMPCIE
391 wifi_platform_bus_enumerate(adapter, FALSE);
392 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
393#else
394 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
395 wifi_platform_bus_enumerate(adapter, FALSE);
396#endif /* BCMPCIE */
397 }
398
399#ifdef CONFIG_DHD_DTS
400 regulator_put(wifi_regulator);
401#endif /* CONFIG_DHD_DTS */
402 return 0;
403}
404
405static int wifi_plat_dev_drv_suspend(struct platform_device *pdev, pm_message_t state)
406{
407 DHD_TRACE(("##> %s\n", __FUNCTION__));
408#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && defined(BCMSDIO)
409 bcmsdh_oob_intr_set(0);
410#endif /* (OOB_INTR_ONLY) */
411 return 0;
412}
413
414static int wifi_plat_dev_drv_resume(struct platform_device *pdev)
415{
416 DHD_TRACE(("##> %s\n", __FUNCTION__));
417#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && defined(BCMSDIO)
418 if (dhd_os_check_if_up(wl_cfg80211_get_dhdp()))
419 bcmsdh_oob_intr_set(1);
420#endif /* (OOB_INTR_ONLY) */
421 return 0;
422}
423
424static const struct of_device_id wifi_device_dt_match[] = {
425 { .name = "" },
426 { .type = "" },
427 { .compatible = "android,bcmdhd_wlan" },
428 { .data = NULL }
429};
430static struct platform_driver wifi_platform_dev_driver = {
431 .probe = wifi_plat_dev_drv_probe,
432 .remove = wifi_plat_dev_drv_remove,
433 .suspend = wifi_plat_dev_drv_suspend,
434 .resume = wifi_plat_dev_drv_resume,
435#ifdef DHD_WIFI_SHUTDOWN
436 .shutdown = wifi_plat_dev_drv_shutdown,
437#endif /* DHD_WIFI_SHUTDOWN */
438 .driver = {
439 .name = WIFI_PLAT_NAME,
440 .of_match_table = wifi_device_dt_match,
441 }
442};
443
444static struct platform_driver wifi_platform_dev_driver_legacy = {
445 .probe = wifi_plat_dev_drv_probe,
446 .remove = wifi_plat_dev_drv_remove,
447 .suspend = wifi_plat_dev_drv_suspend,
448 .resume = wifi_plat_dev_drv_resume,
449#ifdef DHD_WIFI_SHUTDOWN
450 .shutdown = wifi_plat_dev_drv_shutdown,
451#endif /* DHD_WIFI_SHUTDOWN */
452 .driver = {
453 .name = WIFI_PLAT_NAME2,
454 }
455};
456
457#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
458static int wifi_platdev_match(struct device *dev, const void *data)
459#else
460static int wifi_platdev_match(struct device *dev, void *data)
461#endif /* LINUX_VER >= 5.3.0 */
462{
463 char *name = NULL;
464 const struct platform_device *pdev;
465 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
466 name = (char*)data;
467 pdev = to_platform_device(dev);
468 GCC_DIAGNOSTIC_POP();
469
470 if (strcmp(pdev->name, name) == 0) {
471 DHD_ERROR(("found wifi platform device %s\n", name));
472 return TRUE;
473 }
474
475 return FALSE;
476}
477
478static int wifi_ctrlfunc_register_drv(void)
479{
480 int err = 0;
481 struct device *dev1, *dev2;
482 wifi_adapter_info_t *adapter;
483
484 dev1 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME, wifi_platdev_match);
485 dev2 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME2, wifi_platdev_match);
486
487// modify for compaibility
488#if defined(BCMDHD_MODULAR) && defined(BOARD_MODULAR_INIT)
Stephen Chu84526a02021-11-21 21:14:12 +0800489 if ((err = dhd_wlan_init())) {
490 DHD_ERROR(("%s: dhd_wlan_init() failed(%d)\n", __FUNCTION__, err));
491 return err;
492 }
Victor Hsue0cd0e72021-06-08 11:05:03 +0800493#ifdef WBRC
494 wbrc_init();
495#endif /* WBRC */
496#endif /* defined(BCMDHD_MODULAR) && defined(BOARD_MODULAR_INIT) */
497
498#if !defined(CONFIG_DHD_DTS)
499 if (!dts_enabled) {
500 if (dev1 == NULL && dev2 == NULL) {
501 DHD_ERROR(("no wifi platform data, skip\n"));
502 return -ENXIO;
503 }
504 }
505#endif /* !defined(CONFIG_DHD_DTS) */
506
507 /* multi-chip support not enabled, build one adapter information for
508 * DHD (either SDIO, USB or PCIe)
509 */
510 adapter = kzalloc(sizeof(wifi_adapter_info_t), GFP_KERNEL);
511 if (adapter == NULL) {
512 DHD_ERROR(("%s:adapter alloc failed", __FUNCTION__));
Stephen Chu84526a02021-11-21 21:14:12 +0800513 return -ENOMEM;
Victor Hsue0cd0e72021-06-08 11:05:03 +0800514 }
515 adapter->name = "DHD generic adapter";
516 adapter->bus_type = -1;
517 adapter->bus_num = -1;
518 adapter->slot_num = -1;
519 adapter->irq_num = -1;
520 is_power_on = FALSE;
521 wifi_plat_dev_probe_ret = 0;
522 dhd_wifi_platdata = kzalloc(sizeof(bcmdhd_wifi_platdata_t), GFP_KERNEL);
Stephen Chu84526a02021-11-21 21:14:12 +0800523 if (dhd_wifi_platdata == NULL) {
524 DHD_ERROR(("%s:dhd_wifi_platdata alloc failed", __FUNCTION__));
525 kfree(adapter);
526 return -ENOMEM;
527 }
Victor Hsue0cd0e72021-06-08 11:05:03 +0800528 dhd_wifi_platdata->num_adapters = 1;
529 dhd_wifi_platdata->adapters = adapter;
530
531 if (dev1) {
532 err = platform_driver_register(&wifi_platform_dev_driver);
533 if (err) {
534 DHD_ERROR(("%s: failed to register wifi ctrl func driver\n",
535 __FUNCTION__));
536 return err;
537 }
538 }
539 if (dev2) {
540 err = platform_driver_register(&wifi_platform_dev_driver_legacy);
541 if (err) {
542 DHD_ERROR(("%s: failed to register wifi ctrl func legacy driver\n",
543 __FUNCTION__));
544 return err;
545 }
546 }
547#if !defined(CONFIG_DHD_DTS)
548 if (dts_enabled) {
549 struct resource *resource;
550 adapter->wifi_plat_data = (void *)&dhd_wlan_control;
551 resource = &dhd_wlan_resources;
552 adapter->irq_num = resource->start;
553 adapter->intr_flags = resource->flags & IRQF_TRIGGER_MASK;
554#ifdef DHD_ISR_NO_SUSPEND
555 adapter->intr_flags |= IRQF_NO_SUSPEND;
556#endif
557 wifi_plat_dev_probe_ret = dhd_wifi_platform_load();
558 }
559#endif /* !defined(CONFIG_DHD_DTS) */
560
561#ifdef CONFIG_DHD_DTS
562 wifi_plat_dev_probe_ret = platform_driver_register(&wifi_platform_dev_driver);
563#endif /* CONFIG_DHD_DTS */
564
565 /* return probe function's return value if registeration succeeded */
566 return wifi_plat_dev_probe_ret;
567}
568
569void wifi_ctrlfunc_unregister_drv(void)
570{
571
572#ifdef CONFIG_DHD_DTS
573 DHD_ERROR(("unregister wifi platform drivers\n"));
574 platform_driver_unregister(&wifi_platform_dev_driver);
575#else
576 struct device *dev1, *dev2;
577 dev1 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME, wifi_platdev_match);
578 dev2 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME2, wifi_platdev_match);
579 if (!dts_enabled)
580 if (dev1 == NULL && dev2 == NULL)
581 return;
582
583 DHD_ERROR(("unregister wifi platform drivers\n"));
584
585 if (dev1)
586 platform_driver_unregister(&wifi_platform_dev_driver);
587 if (dev2)
588 platform_driver_unregister(&wifi_platform_dev_driver_legacy);
Stephen Chu84526a02021-11-21 21:14:12 +0800589
590 if (!dhd_wifi_platdata) {
591 goto done;
592 }
593
Victor Hsue0cd0e72021-06-08 11:05:03 +0800594 if (dts_enabled) {
595 wifi_adapter_info_t *adapter;
596 adapter = &dhd_wifi_platdata->adapters[0];
597 if (is_power_on) {
598 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
599 wifi_platform_bus_enumerate(adapter, FALSE);
600 } else {
601 // here for removalbe module, use this unify action to
602 // enumerate/detect function to check and unlock the SDIO
603 // to be compatible with some platforms
604 wifi_platform_bus_enumerate(adapter, FALSE);
605 }
606 }
607// modify for compaibility
608#if defined(BCMDHD_MODULAR) && defined(BOARD_MODULAR_INIT)
609 dhd_wlan_deinit();
610#ifdef WBRC
611 wbrc_exit();
612#endif /* WBRC */
613#endif /* defined(BCMDHD_MODULAR) && defined(BOARD_MODULAR_INIT) */
614#endif /* !defined(CONFIG_DHD_DTS) */
615
Stephen Chu84526a02021-11-21 21:14:12 +0800616done:
617 if (dhd_wifi_platdata) {
618 if (dhd_wifi_platdata->adapters) {
619 kfree(dhd_wifi_platdata->adapters);
620 }
621 dhd_wifi_platdata->adapters = NULL;
622 dhd_wifi_platdata->num_adapters = 0;
623 kfree(dhd_wifi_platdata);
624 dhd_wifi_platdata = NULL;
625 }
Victor Hsue0cd0e72021-06-08 11:05:03 +0800626}
627
628static int bcmdhd_wifi_plat_dev_drv_probe(struct platform_device *pdev)
629{
630 dhd_wifi_platdata = (bcmdhd_wifi_platdata_t *)(pdev->dev.platform_data);
631
632 return dhd_wifi_platform_load();
633}
634
635static int bcmdhd_wifi_plat_dev_drv_remove(struct platform_device *pdev)
636{
637 int i;
638 wifi_adapter_info_t *adapter;
639 ASSERT(dhd_wifi_platdata != NULL);
640
641 /* power down all adapters */
642 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
643 adapter = &dhd_wifi_platdata->adapters[i];
644 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
645 wifi_platform_bus_enumerate(adapter, FALSE);
646 }
647 return 0;
648}
649
650static struct platform_driver dhd_wifi_platform_dev_driver = {
651 .probe = bcmdhd_wifi_plat_dev_drv_probe,
652 .remove = bcmdhd_wifi_plat_dev_drv_remove,
653 .driver = {
654 .name = WIFI_PLAT_EXT,
655 }
656};
657
658int dhd_wifi_platform_register_drv(void)
659{
660 int err = 0;
661 struct device *dev;
662
663 /* register Broadcom wifi platform data driver if multi-chip is enabled,
664 * otherwise use Android style wifi platform data (aka wifi control function)
665 * if it exists
666 *
667 * to support multi-chip DHD, Broadcom wifi platform data device must
668 * be added in kernel early boot (e.g. board config file).
669 */
670 if (cfg_multichip) {
671 dev = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_EXT, wifi_platdev_match);
672 if (dev == NULL) {
673 DHD_ERROR(("bcmdhd wifi platform data device not found!!\n"));
674 return -ENXIO;
675 }
676 err = platform_driver_register(&dhd_wifi_platform_dev_driver);
677 } else {
678 err = wifi_ctrlfunc_register_drv();
679
680 /* no wifi ctrl func either, load bus directly and ignore this error */
681 if (err) {
682 if (err == -ENXIO) {
683 /* wifi ctrl function does not exist */
684 err = dhd_wifi_platform_load();
685 } else {
686 /* unregister driver due to initialization failure */
687 wifi_ctrlfunc_unregister_drv();
688 }
689 }
690 }
691
692 return err;
693}
694
695#ifdef BCMPCIE
696static int dhd_wifi_platform_load_pcie(void)
697{
698 int err = 0;
699 int i;
700 wifi_adapter_info_t *adapter;
701
702 BCM_REFERENCE(i);
703 BCM_REFERENCE(adapter);
704
705 if (dhd_wifi_platdata == NULL) {
706 /* XXX For x86 Bringup PC or BRIX */
707 err = dhd_bus_register();
708 } else {
709#ifdef DHD_SUPPORT_HDM
710 if (dhd_download_fw_on_driverload || hdm_trigger_init) {
711#else
712 if (dhd_download_fw_on_driverload) {
713#endif /* DHD_SUPPORT_HDM */
714 /* power up all adapters */
715 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
716 int retry = POWERUP_MAX_RETRY;
717 adapter = &dhd_wifi_platdata->adapters[i];
718
719 DHD_ERROR(("Power-up adapter '%s'\n", adapter->name));
720 DHD_INFO((" - irq %d [flags %d], firmware: %s, nvram: %s\n",
721 adapter->irq_num, adapter->intr_flags, adapter->fw_path,
722 adapter->nv_path));
723 DHD_INFO((" - bus type %d, bus num %d, slot num %d\n\n",
724 adapter->bus_type, adapter->bus_num, adapter->slot_num));
725
726 do {
727 err = wifi_platform_set_power(adapter,
728 TRUE, WIFI_TURNON_DELAY);
729 if (err) {
730 DHD_ERROR(("failed to power up %s,"
731 " %d retry left\n",
732 adapter->name, retry));
733 /* WL_REG_ON state unknown, Power off forcely */
734 wifi_platform_set_power(adapter,
735 FALSE, WIFI_TURNOFF_DELAY);
736 continue;
737 } else {
738 err = wifi_platform_bus_enumerate(adapter, TRUE);
739 if (err) {
740 DHD_ERROR(("failed to enumerate bus %s, "
741 "%d retry left\n",
742 adapter->name, retry));
743 wifi_platform_set_power(adapter, FALSE,
744 WIFI_TURNOFF_DELAY);
745 } else {
746 break;
747 }
748 }
749 } while (retry--);
750
751 if (retry < 0) {
752 DHD_ERROR(("failed to power up %s, max retry reached**\n",
753 adapter->name));
754 return -ENODEV;
755 }
756 }
757 }
758
759 err = dhd_bus_register();
760
761 if (err) {
762 DHD_ERROR(("%s: pcie_register_driver failed\n", __FUNCTION__));
763 if (dhd_download_fw_on_driverload) {
764 /* power down all adapters */
765 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
766 adapter = &dhd_wifi_platdata->adapters[i];
767 wifi_platform_bus_enumerate(adapter, FALSE);
768 wifi_platform_set_power(adapter,
769 FALSE, WIFI_TURNOFF_DELAY);
770 }
771 }
772 }
773 }
774
775 return err;
776}
777#else
778static int dhd_wifi_platform_load_pcie(void)
779{
780 return 0;
781}
782#endif /* BCMPCIE */
783
784void dhd_wifi_platform_unregister_drv(void)
785{
786 if (cfg_multichip)
787 platform_driver_unregister(&dhd_wifi_platform_dev_driver);
788 else
789 wifi_ctrlfunc_unregister_drv();
790}
791
792extern int dhd_watchdog_prio;
793extern int dhd_dpc_prio;
794extern uint dhd_deferred_tx;
795#if defined(OEM_ANDROID) && defined(BCMLXSDMMC)
796extern struct semaphore dhd_registration_sem;
797#endif /* defined(OEM_ANDROID) && defined(BCMLXSDMMC) */
798
799#ifdef BCMSDIO
800static int dhd_wifi_platform_load_sdio(void)
801{
802 int i;
803 int err = 0;
804 wifi_adapter_info_t *adapter;
805
806 BCM_REFERENCE(i);
807 BCM_REFERENCE(adapter);
808 /* Sanity check on the module parameters
809 * - Both watchdog and DPC as tasklets are ok
810 * - If both watchdog and DPC are threads, TX must be deferred
811 */
812 if (!(dhd_watchdog_prio < 0 && dhd_dpc_prio < 0) &&
813 !(dhd_watchdog_prio >= 0 && dhd_dpc_prio >= 0 && dhd_deferred_tx))
814 return -EINVAL;
815
816#if defined(OEM_ANDROID) && defined(BCMLXSDMMC)
817 sema_init(&dhd_registration_sem, 0);
818#endif
819
820 if (dhd_wifi_platdata == NULL) {
821 DHD_ERROR(("DHD wifi platform data is required for Android build\n"));
822 DHD_ERROR(("DHD registering bus directly\n"));
823 /* x86 bring-up PC needs no power-up operations */
824 err = dhd_bus_register();
825 return err;
826 }
827
828#if defined(OEM_ANDROID) && defined(BCMLXSDMMC)
829 /* power up all adapters */
830 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
831 bool chip_up = FALSE;
832 int retry = POWERUP_MAX_RETRY;
833 struct semaphore dhd_chipup_sem;
834
835 adapter = &dhd_wifi_platdata->adapters[i];
836
837 DHD_ERROR(("Power-up adapter '%s', retry=%d\n", adapter->name, retry));
838 DHD_INFO((" - irq %d [flags %d], firmware: %s, nvram: %s\n",
839 adapter->irq_num, adapter->intr_flags, adapter->fw_path, adapter->nv_path));
840 DHD_INFO((" - bus type %d, bus num %d, slot num %d\n\n",
841 adapter->bus_type, adapter->bus_num, adapter->slot_num));
842
843 do {
844 sema_init(&dhd_chipup_sem, 0);
845 err = dhd_bus_reg_sdio_notify(&dhd_chipup_sem);
846 if (err) {
847 DHD_ERROR(("%s dhd_bus_reg_sdio_notify fail(%d)\n\n",
848 __FUNCTION__, err));
849 return err;
850 }
851 err = wifi_platform_set_power(adapter, TRUE, WIFI_TURNON_DELAY);
852 if (err) {
853 DHD_ERROR(("%s: wifi pwr on error ! \n", __FUNCTION__));
854 dhd_bus_unreg_sdio_notify();
855 /* WL_REG_ON state unknown, Power off forcely */
856 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
857 continue;
858 } else {
859 wifi_platform_bus_enumerate(adapter, TRUE);
860 }
861
862 if (down_timeout(&dhd_chipup_sem, msecs_to_jiffies(POWERUP_WAIT_MS)) == 0) {
863 dhd_bus_unreg_sdio_notify();
864 chip_up = TRUE;
865 // use this unify action to enumerate/detect function to check
866 // and lock the SDIO to be compatible with some platforms
867 wifi_platform_bus_enumerate(adapter, TRUE);
868 break;
869 }
870
871 DHD_ERROR(("failed to power up %s, %d retry left\n", adapter->name, retry));
872 dhd_bus_unreg_sdio_notify();
873 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
874 wifi_platform_bus_enumerate(adapter, FALSE);
875 } while (retry--);
876
877 if (!chip_up) {
878 DHD_ERROR(("failed to power up %s, max retry reached**\n", adapter->name));
879 return -ENODEV;
880 }
881
882 }
883
884 err = dhd_bus_register();
885
886 if (err) {
887 DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__));
888 goto fail;
889 }
890
891 /*
892 * Wait till MMC sdio_register_driver callback called and made driver attach.
893 * It's needed to make sync up exit from dhd insmod and
894 * Kernel MMC sdio device callback registration
895 */
896 err = down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT));
897 if (err) {
898 DHD_ERROR(("%s: sdio_register_driver timeout or error \n", __FUNCTION__));
899 dhd_bus_unregister();
900 goto fail;
901 }
902
903 return err;
904
905fail:
906 /* power down all adapters */
907 for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
908 adapter = &dhd_wifi_platdata->adapters[i];
909 wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
910 wifi_platform_bus_enumerate(adapter, FALSE);
911 }
912#endif /* defined(OEM_ANDROID) && defined(BCMLXSDMMC) */
913
914 return err;
915}
916#else /* BCMSDIO */
917static int dhd_wifi_platform_load_sdio(void)
918{
919 return 0;
920}
921#endif /* BCMSDIO */
922
923#ifdef BCMDBUS
924/* User-specified vid/pid */
925int dhd_vid = 0xa5c;
926int dhd_pid = 0x48f;
927module_param(dhd_vid, int, 0);
928module_param(dhd_pid, int, 0);
929void *dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype, uint32 hdrlen);
930void dhd_dbus_disconnect_cb(void *arg);
931
932static int dhd_wifi_platform_load_usb(void)
933{
934 int err = 0;
935
936 if (dhd_vid < 0 || dhd_vid > 0xffff) {
937 DHD_ERROR(("%s: invalid dhd_vid 0x%x\n", __FUNCTION__, dhd_vid));
938 return -EINVAL;
939 }
940 if (dhd_pid < 0 || dhd_pid > 0xffff) {
941 DHD_ERROR(("%s: invalid dhd_pid 0x%x\n", __FUNCTION__, dhd_pid));
942 return -EINVAL;
943 }
944
945 err = dbus_register(dhd_vid, dhd_pid, dhd_dbus_probe_cb, dhd_dbus_disconnect_cb,
946 NULL, NULL, NULL);
947
948 /* Device not detected */
949 if (err == DBUS_ERR_NODEVICE)
950 err = DBUS_OK;
951
952 return err;
953}
954#else /* BCMDBUS */
955static int dhd_wifi_platform_load_usb(void)
956{
957 return 0;
958}
959#endif /* BCMDBUS */
960
961static int dhd_wifi_platform_load()
962{
963 int err = 0;
964
965#if defined(OEM_ANDROID)
966 wl_android_init();
967#endif /* OEM_ANDROID */
968
969 if ((err = dhd_wifi_platform_load_usb()))
970 goto end;
971 else if ((err = dhd_wifi_platform_load_sdio()))
972 goto end;
973 else
974 err = dhd_wifi_platform_load_pcie();
975
976end:
977#if defined(OEM_ANDROID)
978 if (err)
979 wl_android_exit();
980 else
981 wl_android_post_init();
982#endif /* OEM_ANDROID */
983
984 return err;
985}
Stephen Chu52962b92021-11-24 11:58:36 +0800986
Stephen Chu6869e162022-09-26 16:24:04 +0800987int
988__attribute__ ((weak)) dhd_get_platform_naming_for_nvram_clmblob_file(char *file_name)
989{
990 return BCME_ERROR;
991}
992
993/* Weak functions that can be overridden in Platform specific implementation */
994uint32 __attribute__ ((weak)) dhd_plat_get_info_size(void)
995{
996 return 0;
997}
998
999int __attribute__ ((weak)) dhd_plat_pcie_register_event(void *plat_info,
1000 struct pci_dev *pdev, dhd_pcie_event_cb_t pfn)
1001{
1002 return 0;
1003}
1004
1005void __attribute__ ((weak)) dhd_plat_pcie_deregister_event(void *plat_info)
1006{
1007 return;
1008}
1009
1010void __attribute__ ((weak)) dhd_plat_l1ss_ctrl(bool ctrl)
1011{
1012 return;
1013}
1014
Stephen Chu52962b92021-11-24 11:58:36 +08001015void __attribute__ ((weak)) dhd_plat_l1_exit_io(void)
1016{
1017 return;
1018}
normanwhl4f684212022-08-24 11:42:31 +00001019
Stephen Chu6869e162022-09-26 16:24:04 +08001020void __attribute__ ((weak)) dhd_plat_l1_exit(void)
normanwhl4f684212022-08-24 11:42:31 +00001021{
Stephen Chu6869e162022-09-26 16:24:04 +08001022 return;
1023}
1024
1025void __attribute__ ((weak)) dhd_plat_report_bh_sched(void *plat_info, int resched)
1026{
1027 return;
1028}
1029
1030uint32 __attribute__ ((weak)) dhd_plat_get_rc_device_id(void)
1031{
1032 return PCIE_RC_DEVICE_ID;
normanwhl4f684212022-08-24 11:42:31 +00001033}