2 * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
4 * Copyright (C) 1999-2018, 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 753315 2018-03-21 04:10:12Z $
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_4362)
54 #define SDIO_DEVICE_ID_BROADCOM_4362 0x4362
57 extern void wl_cfg80211_set_parent_dev(void *dev
);
58 extern void sdioh_sdmmc_devintr_off(sdioh_info_t
*sd
);
59 extern void sdioh_sdmmc_devintr_on(sdioh_info_t
*sd
);
60 extern void* bcmsdh_probe(osl_t
*osh
, void *dev
, void *sdioh
, void *adapter_info
, uint bus_type
,
61 uint bus_num
, uint slot_num
);
62 extern int bcmsdh_remove(bcmsdh_info_t
*bcmsdh
);
64 int sdio_function_init(void);
65 void sdio_function_cleanup(void);
67 #define DESCRIPTION "bcmsdh_sdmmc Driver"
68 #define AUTHOR "Broadcom Corporation"
70 /* module param defaults */
71 static int clockoverride
= 0;
73 module_param(clockoverride
, int, 0644);
74 MODULE_PARM_DESC(clockoverride
, "SDIO card clock override");
76 #ifdef GLOBAL_SDMMC_INSTANCE
77 PBCMSDH_SDMMC_INSTANCE gInstance
;
80 /* Maximum number of bcmsdh_sdmmc devices supported by driver */
81 #define BCMSDH_SDMMC_MAX_DEVICES 1
83 extern volatile bool dhd_mmc_suspend
;
85 static int sdioh_probe(struct sdio_func
*func
)
87 int host_idx
= func
->card
->host
->index
;
88 uint32 rca
= func
->card
->rca
;
89 wifi_adapter_info_t
*adapter
;
91 sdioh_info_t
*sdioh
= NULL
;
93 sd_err(("bus num (host idx)=%d, slot num (rca)=%d\n", host_idx
, rca
));
94 adapter
= dhd_wifi_platform_get_adapter(SDIO_BUS
, host_idx
, rca
);
95 if (adapter
!= NULL
) {
96 sd_err(("found adapter info '%s'\n", adapter
->name
));
97 #ifdef BUS_POWER_RESTORE
98 adapter
->sdio_func
= func
;
101 sd_err(("can't find adapter info for this chip\n"));
104 wl_cfg80211_set_parent_dev(&func
->dev
);
107 /* allocate SDIO Host Controller state info */
108 osh
= osl_attach(&func
->dev
, SDIO_BUS
, TRUE
);
110 sd_err(("%s: osl_attach failed\n", __FUNCTION__
));
113 osl_static_mem_init(osh
, adapter
);
114 sdioh
= sdioh_attach(osh
, func
);
116 sd_err(("%s: sdioh_attach failed\n", __FUNCTION__
));
119 sdioh
->bcmsdh
= bcmsdh_probe(osh
, &func
->dev
, sdioh
, adapter
, SDIO_BUS
, host_idx
, rca
);
120 if (sdioh
->bcmsdh
== NULL
) {
121 sd_err(("%s: bcmsdh_probe failed\n", __FUNCTION__
));
125 sdio_set_drvdata(func
, sdioh
);
130 sdioh_detach(osh
, sdioh
);
136 static void sdioh_remove(struct sdio_func
*func
)
141 sdioh
= sdio_get_drvdata(func
);
143 sd_err(("%s: error, no sdioh handler found\n", __FUNCTION__
));
146 sd_err(("%s: Enter\n", __FUNCTION__
));
149 bcmsdh_remove(sdioh
->bcmsdh
);
150 sdioh_detach(osh
, sdioh
);
154 static int bcmsdh_sdmmc_probe(struct sdio_func
*func
,
155 const struct sdio_device_id
*id
)
162 sd_err(("%s: Enter num=%d\n", __FUNCTION__
, func
->num
));
163 sd_info(("sdio_bcmsdh: func->class=%x\n", func
->class));
164 sd_info(("sdio_vendor: 0x%04x\n", func
->vendor
));
165 sd_info(("sdio_device: 0x%04x\n", func
->device
));
166 sd_info(("Function#: 0x%04x\n", func
->num
));
168 #ifdef GLOBAL_SDMMC_INSTANCE
169 gInstance
->func
[func
->num
] = func
;
172 /* 4318 doesn't have function 2 */
173 if ((func
->num
== 2) || (func
->num
== 1 && func
->device
== 0x4))
174 ret
= sdioh_probe(func
);
179 static void bcmsdh_sdmmc_remove(struct sdio_func
*func
)
182 sd_err(("%s is called with NULL SDIO function pointer\n", __FUNCTION__
));
186 sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__
));
187 sd_info(("sdio_bcmsdh: func->class=%x\n", func
->class));
188 sd_info(("sdio_vendor: 0x%04x\n", func
->vendor
));
189 sd_info(("sdio_device: 0x%04x\n", func
->device
));
190 sd_info(("Function#: 0x%04x\n", func
->num
));
192 if ((func
->num
== 2) || (func
->num
== 1 && func
->device
== 0x4))
196 /* devices we support, null terminated */
197 static const struct sdio_device_id bcmsdh_sdmmc_ids
[] = {
198 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_DEFAULT
) },
199 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM
, SDIO_DEVICE_ID_BROADCOM_4362
) },
200 { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE
) },
201 { 0, 0, 0, 0 /* end: all zeroes */
205 MODULE_DEVICE_TABLE(sdio
, bcmsdh_sdmmc_ids
);
207 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
208 static int bcmsdh_sdmmc_suspend(struct device
*pdev
)
212 struct sdio_func
*func
= dev_to_sdio_func(pdev
);
213 mmc_pm_flag_t sdio_flags
;
215 printf("%s Enter func->num=%d\n", __FUNCTION__
, func
->num
);
219 dhd_mmc_suspend
= TRUE
;
220 sdioh
= sdio_get_drvdata(func
);
221 err
= bcmsdh_suspend(sdioh
->bcmsdh
);
223 printf("%s bcmsdh_suspend err=%d\n", __FUNCTION__
, err
);
224 dhd_mmc_suspend
= FALSE
;
228 sdio_flags
= sdio_get_host_pm_caps(func
);
229 if (!(sdio_flags
& MMC_PM_KEEP_POWER
)) {
230 sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__
));
231 dhd_mmc_suspend
= FALSE
;
235 /* keep power while host suspended */
236 err
= sdio_set_host_pm_flags(func
, MMC_PM_KEEP_POWER
);
238 sd_err(("%s: error while trying to keep power\n", __FUNCTION__
));
239 dhd_mmc_suspend
= FALSE
;
244 printf("%s Exit\n", __FUNCTION__
);
248 static int bcmsdh_sdmmc_resume(struct device
*pdev
)
251 struct sdio_func
*func
= dev_to_sdio_func(pdev
);
253 printf("%s Enter func->num=%d\n", __FUNCTION__
, func
->num
);
257 dhd_mmc_suspend
= FALSE
;
258 sdioh
= sdio_get_drvdata(func
);
259 bcmsdh_resume(sdioh
->bcmsdh
);
262 printf("%s Exit\n", __FUNCTION__
);
266 static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops
= {
267 .suspend
= bcmsdh_sdmmc_suspend
,
268 .resume
= bcmsdh_sdmmc_resume
,
270 #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
272 #if defined(BCMLXSDMMC)
273 static struct semaphore
*notify_semaphore
= NULL
;
275 static int dummy_probe(struct sdio_func
*func
,
276 const struct sdio_device_id
*id
)
278 if (func
&& (func
->num
!= 2)) {
282 if (notify_semaphore
)
283 up(notify_semaphore
);
287 static void dummy_remove(struct sdio_func
*func
)
291 static struct sdio_driver dummy_sdmmc_driver
= {
292 .probe
= dummy_probe
,
293 .remove
= dummy_remove
,
294 .name
= "dummy_sdmmc",
295 .id_table
= bcmsdh_sdmmc_ids
,
298 int sdio_func_reg_notify(void* semaphore
)
300 notify_semaphore
= semaphore
;
301 return sdio_register_driver(&dummy_sdmmc_driver
);
304 void sdio_func_unreg_notify(void)
307 sdio_unregister_driver(&dummy_sdmmc_driver
);
310 #endif /* defined(BCMLXSDMMC) */
312 static struct sdio_driver bcmsdh_sdmmc_driver
= {
313 .probe
= bcmsdh_sdmmc_probe
,
314 .remove
= bcmsdh_sdmmc_remove
,
315 .name
= "bcmsdh_sdmmc",
316 .id_table
= bcmsdh_sdmmc_ids
,
317 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
319 .pm
= &bcmsdh_sdmmc_pm_ops
,
321 #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
329 /* Interrupt enable/disable */
331 sdioh_interrupt_set(sdioh_info_t
*sd
, bool enable
)
336 sd_trace(("%s: %s\n", __FUNCTION__
, enable
? "Enabling" : "Disabling"));
337 return SDIOH_API_RC_SUCCESS
;
342 bcmsdh_module_init(void)
345 error
= sdio_function_init();
350 bcmsdh_module_cleanup(void)
352 sdio_function_cleanup();
355 module_init(bcmsdh_module_init
);
356 module_exit(bcmsdh_module_cleanup
);
358 MODULE_LICENSE("GPL v2");
359 MODULE_DESCRIPTION(DESCRIPTION
);
360 MODULE_AUTHOR(AUTHOR
);
362 #endif /* BCMSDH_MODULE */
366 int bcmsdh_register_client_driver(void)
368 return sdio_register_driver(&bcmsdh_sdmmc_driver
);
374 void bcmsdh_unregister_client_driver(void)
376 sdio_unregister_driver(&bcmsdh_sdmmc_driver
);