2 * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
4 * Copyright (C) 1999-2019, Broadcom.
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.
25 * <<Broadcom-WL-IPTag/Proprietary,Open:>>
27 * $Id: bcmsdh_sdmmc_linux.c 796833 2018-12-27 05:52:37Z $
32 #include <sdio.h> /* SDIO Device and Protocol Specs */
33 #include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
34 #include <sdiovar.h> /* to get msglevel bit values */
36 #include <linux/sched.h> /* request_irq() */
38 #include <linux/mmc/core.h>
39 #include <linux/mmc/card.h>
40 #include <linux/mmc/host.h>
41 #include <linux/mmc/sdio_func.h>
42 #include <linux/mmc/sdio_ids.h>
43 #include <dhd_linux.h>
44 #include <bcmsdh_sdmmc.h>
47 #if !defined(SDIO_VENDOR_ID_BROADCOM)
48 #define SDIO_VENDOR_ID_BROADCOM 0x02d0
49 #endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
51 #define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000
53 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
54 #define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */
55 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
56 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
57 #define SDIO_DEVICE_ID_BROADCOM_4325 0x0493
58 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
59 #if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
60 #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
61 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
62 #if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
63 #define SDIO_DEVICE_ID_BROADCOM_4319 0x4319
64 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */
65 #if !defined(SDIO_DEVICE_ID_BROADCOM_4330)
66 #define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
67 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */
68 #if !defined(SDIO_DEVICE_ID_BROADCOM_4334)
69 #define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
70 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4334) */
71 #if !defined(SDIO_DEVICE_ID_BROADCOM_4324)
72 #define SDIO_DEVICE_ID_BROADCOM_4324 0x4324
73 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4324) */
74 #if !defined(SDIO_DEVICE_ID_BROADCOM_43239)
75 #define SDIO_DEVICE_ID_BROADCOM_43239 43239
76 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_43239) */
77 #if !defined(SDIO_DEVICE_ID_BROADCOM_4345)
78 #define SDIO_DEVICE_ID_BROADCOM_4345 0x4345
79 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4345) */
81 #if !defined(SDIO_DEVICE_ID_BROADCOM_4362)
82 #define SDIO_DEVICE_ID_BROADCOM_4362 0x4362
85 extern void wl_cfg80211_set_parent_dev(void *dev
);
86 extern void sdioh_sdmmc_devintr_off(sdioh_info_t
*sd
);
87 extern void sdioh_sdmmc_devintr_on(sdioh_info_t
*sd
);
88 extern void* bcmsdh_probe(osl_t
*osh
, void *dev
, void *sdioh
, void *adapter_info
, uint bus_type
,
89 uint bus_num
, uint slot_num
);
90 extern int bcmsdh_remove(bcmsdh_info_t
*bcmsdh
);
92 int sdio_function_init(void);
93 void sdio_function_cleanup(void);
95 #define DESCRIPTION "bcmsdh_sdmmc Driver"
96 #define AUTHOR "Broadcom Corporation"
98 /* module param defaults */
99 static int clockoverride
= 0;
101 module_param(clockoverride
, int, 0644);
102 MODULE_PARM_DESC(clockoverride
, "SDIO card clock override");
104 /* Maximum number of bcmsdh_sdmmc devices supported by driver */
105 #define BCMSDH_SDMMC_MAX_DEVICES 1
107 extern volatile bool dhd_mmc_suspend
;
109 static int sdioh_probe(struct sdio_func
*func
)
111 int host_idx
= func
->card
->host
->index
;
112 uint32 rca
= func
->card
->rca
;
113 wifi_adapter_info_t
*adapter
;
115 sdioh_info_t
*sdioh
= NULL
;
117 sd_err(("bus num (host idx)=%d, slot num (rca)=%d\n", host_idx
, rca
));
118 adapter
= dhd_wifi_platform_get_adapter(SDIO_BUS
, host_idx
, rca
);
120 sd_err(("found adapter info '%s'\n", adapter
->name
));
122 sd_err(("can't find adapter info for this chip\n"));
125 wl_cfg80211_set_parent_dev(&func
->dev
);
128 /* allocate SDIO Host Controller state info */
129 osh
= osl_attach(&func
->dev
, SDIO_BUS
, TRUE
);
131 sd_err(("%s: osl_attach failed\n", __FUNCTION__
));
134 osl_static_mem_init(osh
, adapter
);
135 sdioh
= sdioh_attach(osh
, func
);
137 sd_err(("%s: sdioh_attach failed\n", __FUNCTION__
));
140 sdioh
->bcmsdh
= bcmsdh_probe(osh
, &func
->dev
, sdioh
, adapter
, SDIO_BUS
, host_idx
, rca
);
141 if (sdioh
->bcmsdh
== NULL
) {
142 sd_err(("%s: bcmsdh_probe failed\n", __FUNCTION__
));
146 sdio_set_drvdata(func
, sdioh
);
151 sdioh_detach(osh
, sdioh
);
157 static void sdioh_remove(struct sdio_func
*func
)
162 sdioh
= sdio_get_drvdata(func
);
164 sd_err(("%s: error, no sdioh handler found\n", __FUNCTION__
));
169 bcmsdh_remove(sdioh
->bcmsdh
);
170 sdioh_detach(osh
, sdioh
);
174 static int bcmsdh_sdmmc_probe(struct sdio_func
*func
,
175 const struct sdio_device_id
*id
)
182 sd_err(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__
));
183 sd_info(("sdio_bcmsdh: func->class=%x\n", func
->class));
184 sd_info(("sdio_vendor: 0x%04x\n", func
->vendor
));
185 sd_info(("sdio_device: 0x%04x\n", func
->device
));
186 sd_info(("Function#: 0x%04x\n", func
->num
));
188 /* 4318 doesn't have function 2 */
189 if ((func
->num
== 2) || (func
->num
== 1 && func
->device
== 0x4))
190 ret
= sdioh_probe(func
);
195 static void bcmsdh_sdmmc_remove(struct sdio_func
*func
)
198 sd_err(("%s is called with NULL SDIO function pointer\n", __FUNCTION__
));
202 sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__
));
203 sd_info(("sdio_bcmsdh: func->class=%x\n", func
->class));
204 sd_info(("sdio_vendor: 0x%04x\n", func
->vendor
));
205 sd_info(("sdio_device: 0x%04x\n", func
->device
));
206 sd_info(("Function#: 0x%04x\n", func
->num
));
208 if ((func
->num
== 2) || (func
->num
== 1 && func
->device
== 0x4))
212 /* devices we support, null terminated */
213 static const struct sdio_device_id bcmsdh_sdmmc_ids
[] = {
214 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_DEFAULT
) },
215 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_DEFAULT
) },
216 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB
) },
217 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4325
) },
218 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4329
) },
219 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4319
) },
220 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4330
) },
221 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4334
) },
222 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4324
) },
223 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_43239
) },
224 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4345
) },
225 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4362
) },
226 { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE
) },
227 { 0, 0, 0, 0 /* end: all zeroes */
231 MODULE_DEVICE_TABLE(sdio
, bcmsdh_sdmmc_ids
);
233 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
234 static int bcmsdh_sdmmc_suspend(struct device
*pdev
)
238 struct sdio_func
*func
= dev_to_sdio_func(pdev
);
239 mmc_pm_flag_t sdio_flags
;
241 sd_err(("%s Enter\n", __FUNCTION__
));
245 dhd_mmc_suspend
= TRUE
;
246 sdioh
= sdio_get_drvdata(func
);
247 err
= bcmsdh_suspend(sdioh
->bcmsdh
);
249 dhd_mmc_suspend
= FALSE
;
253 sdio_flags
= sdio_get_host_pm_caps(func
);
254 if (!(sdio_flags
& MMC_PM_KEEP_POWER
)) {
255 sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__
));
256 dhd_mmc_suspend
= FALSE
;
260 /* keep power while host suspended */
261 err
= sdio_set_host_pm_flags(func
, MMC_PM_KEEP_POWER
);
263 sd_err(("%s: error while trying to keep power\n", __FUNCTION__
));
264 dhd_mmc_suspend
= FALSE
;
272 static int bcmsdh_sdmmc_resume(struct device
*pdev
)
275 struct sdio_func
*func
= dev_to_sdio_func(pdev
);
277 sd_err(("%s Enter\n", __FUNCTION__
));
281 sdioh
= sdio_get_drvdata(func
);
282 dhd_mmc_suspend
= FALSE
;
283 bcmsdh_resume(sdioh
->bcmsdh
);
289 static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops
= {
290 .suspend
= bcmsdh_sdmmc_suspend
,
291 .resume
= bcmsdh_sdmmc_resume
,
293 #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
295 #if defined(BCMLXSDMMC)
296 static struct semaphore
*notify_semaphore
= NULL
;
298 static int dummy_probe(struct sdio_func
*func
,
299 const struct sdio_device_id
*id
)
302 sd_info(("%s: func->num=0x%x; \n", __FUNCTION__
, func
->num
));
304 sd_info(("%s: class=0x%x; vendor=0x%x; device=0x%x\n", __FUNCTION__
,
305 id
->class, id
->vendor
, id
->device
));
306 if (id
->vendor
!= SDIO_VENDOR_ID_BROADCOM
)
309 if (func
&& (func
->num
!= 2)) {
313 if (notify_semaphore
)
314 up(notify_semaphore
);
318 static void dummy_remove(struct sdio_func
*func
)
322 static struct sdio_driver dummy_sdmmc_driver
= {
323 .probe
= dummy_probe
,
324 .remove
= dummy_remove
,
325 .name
= "dummy_sdmmc",
326 .id_table
= bcmsdh_sdmmc_ids
,
329 int sdio_func_reg_notify(void* semaphore
)
331 notify_semaphore
= semaphore
;
332 return sdio_register_driver(&dummy_sdmmc_driver
);
335 void sdio_func_unreg_notify(void)
338 sdio_unregister_driver(&dummy_sdmmc_driver
);
341 #endif /* defined(BCMLXSDMMC) */
343 static struct sdio_driver bcmsdh_sdmmc_driver
= {
344 .probe
= bcmsdh_sdmmc_probe
,
345 .remove
= bcmsdh_sdmmc_remove
,
346 .name
= "bcmsdh_sdmmc",
347 .id_table
= bcmsdh_sdmmc_ids
,
348 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
350 .pm
= &bcmsdh_sdmmc_pm_ops
,
352 #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
360 /* Interrupt enable/disable */
362 sdioh_interrupt_set(sdioh_info_t
*sd
, bool enable
)
367 sd_trace(("%s: %s\n", __FUNCTION__
, enable
? "Enabling" : "Disabling"));
368 return SDIOH_API_RC_SUCCESS
;
373 bcmsdh_module_init(void)
376 error
= sdio_function_init();
381 bcmsdh_module_cleanup(void)
383 sdio_function_cleanup();
386 module_init(bcmsdh_module_init
);
387 module_exit(bcmsdh_module_cleanup
);
389 MODULE_LICENSE("GPL v2");
390 MODULE_DESCRIPTION(DESCRIPTION
);
391 MODULE_AUTHOR(AUTHOR
);
393 #endif /* BCMSDH_MODULE */
397 int bcmsdh_register_client_driver(void)
399 return sdio_register_driver(&bcmsdh_sdmmc_driver
);
405 void bcmsdh_unregister_client_driver(void)
407 sdio_unregister_driver(&bcmsdh_sdmmc_driver
);