xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/bcmsdh_sdmmc_linux.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
3  *
4  * Copyright (C) 2020, 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 
26 #include <typedefs.h>
27 #include <bcmutils.h>
28 #include <sdio.h>	/* SDIO Device and Protocol Specs */
29 #include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
30 #include <sdiovar.h>	/* to get msglevel bit values */
31 
32 #include <linux/sched.h>	/* request_irq() */
33 
34 #include <linux/mmc/core.h>
35 #include <linux/mmc/card.h>
36 #include <linux/mmc/host.h>
37 #include <linux/mmc/sdio_func.h>
38 #include <linux/mmc/sdio_ids.h>
39 #include <dhd_linux.h>
40 #include <bcmsdh_sdmmc.h>
41 #include <dhd_dbg.h>
42 #include <bcmdevs.h>
43 
44 #if !defined(SDIO_VENDOR_ID_BROADCOM)
45 #define SDIO_VENDOR_ID_BROADCOM		0x02d0
46 #endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
47 
48 #define SDIO_DEVICE_ID_BROADCOM_DEFAULT	0x0000
49 
50 extern void wl_cfg80211_set_parent_dev(void *dev);
51 extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
52 extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
53 extern void* bcmsdh_probe(osl_t *osh, void *dev, void *sdioh, void *adapter_info, uint bus_type,
54 	uint bus_num, uint slot_num);
55 extern int bcmsdh_remove(bcmsdh_info_t *bcmsdh);
56 
57 int sdio_function_init(void);
58 void sdio_function_cleanup(void);
59 
60 #define DESCRIPTION "bcmsdh_sdmmc Driver"
61 #define AUTHOR "Broadcom Corporation"
62 
63 /* module param defaults */
64 static int clockoverride = 0;
65 
66 module_param(clockoverride, int, 0644);
67 MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
68 
69 #ifdef GLOBAL_SDMMC_INSTANCE
70 PBCMSDH_SDMMC_INSTANCE gInstance;
71 #endif
72 
73 /* Maximum number of bcmsdh_sdmmc devices supported by driver */
74 #define BCMSDH_SDMMC_MAX_DEVICES 1
75 
76 extern volatile bool dhd_mmc_suspend;
77 
sdioh_probe(struct sdio_func * func)78 static int sdioh_probe(struct sdio_func *func)
79 {
80 	int host_idx = func->card->host->index;
81 	uint32 rca = func->card->rca;
82 	wifi_adapter_info_t *adapter;
83 	osl_t *osh = NULL;
84 	sdioh_info_t *sdioh = NULL;
85 
86 	sd_err(("bus num (host idx)=%d, slot num (rca)=%d\n", host_idx, rca));
87 	adapter = dhd_wifi_platform_get_adapter(SDIO_BUS, host_idx, rca);
88 	if (adapter != NULL) {
89 		sd_err(("found adapter info '%s'\n", adapter->name));
90 		adapter->bus_type = SDIO_BUS;
91 		adapter->bus_num = host_idx;
92 		adapter->slot_num = rca;
93 		adapter->sdio_func = func;
94 	} else {
95 		sd_err(("can't find adapter info for this chip\n"));
96 #ifdef ADAPTER_IDX
97 		goto fail;
98 #endif
99 	}
100 
101 #ifdef WL_CFG80211
102 	wl_cfg80211_set_parent_dev(&func->dev);
103 #endif
104 
105 	 /* allocate SDIO Host Controller state info */
106 	 osh = osl_attach(&func->dev, SDIO_BUS, TRUE);
107 	 if (osh == NULL) {
108 		 sd_err(("%s: osl_attach failed\n", __FUNCTION__));
109 		 goto fail;
110 	 }
111 	 osl_static_mem_init(osh, adapter);
112 	 sdioh = sdioh_attach(osh, func);
113 	 if (sdioh == NULL) {
114 		 sd_err(("%s: sdioh_attach failed\n", __FUNCTION__));
115 		 goto fail;
116 	 }
117 	 sdioh->bcmsdh = bcmsdh_probe(osh, &func->dev, sdioh, adapter, SDIO_BUS, host_idx, rca);
118 	 if (sdioh->bcmsdh == NULL) {
119 		 sd_err(("%s: bcmsdh_probe failed\n", __FUNCTION__));
120 		 goto fail;
121 	 }
122 
123 	sdio_set_drvdata(func, sdioh);
124 	return 0;
125 
126 fail:
127 	if (sdioh != NULL)
128 		sdioh_detach(osh, sdioh);
129 	if (osh != NULL)
130 		osl_detach(osh);
131 	return -ENOMEM;
132 }
133 
sdioh_remove(struct sdio_func * func)134 static void sdioh_remove(struct sdio_func *func)
135 {
136 	sdioh_info_t *sdioh;
137 	osl_t *osh;
138 
139 	sdioh = sdio_get_drvdata(func);
140 	if (sdioh == NULL) {
141 		sd_err(("%s: error, no sdioh handler found\n", __FUNCTION__));
142 		return;
143 	}
144 	sd_err(("%s: Enter\n", __FUNCTION__));
145 
146 	osh = sdioh->osh;
147 	bcmsdh_remove(sdioh->bcmsdh);
148 	sdioh_detach(osh, sdioh);
149 	osl_detach(osh);
150 }
151 
bcmsdh_sdmmc_probe(struct sdio_func * func,const struct sdio_device_id * id)152 static int bcmsdh_sdmmc_probe(struct sdio_func *func,
153                               const struct sdio_device_id *id)
154 {
155 	int ret = 0;
156 
157 	if (func == NULL)
158 		return -EINVAL;
159 
160 	sd_err(("%s: Enter num=%d\n", __FUNCTION__, func->num));
161 	sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
162 	sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
163 	sd_info(("sdio_device: 0x%04x\n", func->device));
164 	sd_info(("Function#: 0x%04x\n", func->num));
165 
166 #ifdef GLOBAL_SDMMC_INSTANCE
167 	gInstance->func[func->num] = func;
168 #endif
169 
170 	/* 4318 doesn't have function 2 */
171 	if ((func->num == 2) || (func->num == 1 && func->device == 0x4))
172 		ret = sdioh_probe(func);
173 
174 	return ret;
175 }
176 
bcmsdh_sdmmc_remove(struct sdio_func * func)177 static void bcmsdh_sdmmc_remove(struct sdio_func *func)
178 {
179 	if (func == NULL) {
180 		sd_err(("%s is called with NULL SDIO function pointer\n", __FUNCTION__));
181 		return;
182 	}
183 
184 	sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
185 	sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
186 	sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
187 	sd_info(("sdio_device: 0x%04x\n", func->device));
188 	sd_info(("Function#: 0x%04x\n", func->num));
189 
190 	if ((func->num == 2) || (func->num == 1 && func->device == 0x4))
191 		sdioh_remove(func);
192 }
193 
194 /* devices we support, null terminated */
195 static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
196 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
197 	/* XXX This should not be in the external release, as it will attach to any SDIO
198 	 * device, even non-WLAN devices.
199 	 * Need to add IDs for the FALCON-based chips and put this under BCMINTERNAL
200 	 { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) },
201 	 */
202 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM4362_CHIP_ID) },
203 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43751_CHIP_ID) },
204 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43752_CHIP_ID) },
205 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43012_CHIP_ID) },
206 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43014_CHIP_ID) },
207 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43014_D11N_ID) },
208 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43014_D11N2G_ID) },
209 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43014_D11N5G_ID) },
210 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43013_CHIP_ID) },
211 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43013_D11N_ID) },
212 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43013_D11N2G_ID) },
213 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43013_D11N5G_ID) },
214 	{ SDIO_DEVICE_CLASS(SDIO_CLASS_NONE)		},
215 	{ 0, 0, 0, 0 /* end: all zeroes */
216 	},
217 };
218 
219 MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
220 
221 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
bcmsdh_sdmmc_suspend(struct device * pdev)222 static int bcmsdh_sdmmc_suspend(struct device *pdev)
223 {
224 	int err;
225 	sdioh_info_t *sdioh;
226 	struct sdio_func *func = dev_to_sdio_func(pdev);
227 	mmc_pm_flag_t sdio_flags;
228 
229 	printf("%s Enter func->num=%d\n", __FUNCTION__, func->num);
230 	if (func->num != 2)
231 		return 0;
232 
233 	dhd_mmc_suspend = TRUE;
234 	sdioh = sdio_get_drvdata(func);
235 	err = bcmsdh_suspend(sdioh->bcmsdh);
236 	if (err) {
237 		printf("%s bcmsdh_suspend err=%d\n", __FUNCTION__, err);
238 		dhd_mmc_suspend = FALSE;
239 		return err;
240 	}
241 
242 	sdio_flags = sdio_get_host_pm_caps(func);
243 	if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
244 		sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__));
245 		dhd_mmc_suspend = FALSE;
246 		return  -EINVAL;
247 	}
248 
249 	/* keep power while host suspended */
250 	err = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
251 	if (err) {
252 		sd_err(("%s: error while trying to keep power\n", __FUNCTION__));
253 		dhd_mmc_suspend = FALSE;
254 		return err;
255 	}
256 	smp_mb();
257 
258 	printf("%s Exit\n", __FUNCTION__);
259 	return 0;
260 }
261 
bcmsdh_sdmmc_resume(struct device * pdev)262 static int bcmsdh_sdmmc_resume(struct device *pdev)
263 {
264 	sdioh_info_t *sdioh;
265 	struct sdio_func *func = dev_to_sdio_func(pdev);
266 
267 	printf("%s Enter func->num=%d\n", __FUNCTION__, func->num);
268 	if (func->num != 2)
269 		return 0;
270 
271 	dhd_mmc_suspend = FALSE;
272 	sdioh = sdio_get_drvdata(func);
273 	bcmsdh_resume(sdioh->bcmsdh);
274 
275 	smp_mb();
276 	printf("%s Exit\n", __FUNCTION__);
277 	return 0;
278 }
279 
280 static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = {
281 	.suspend	= bcmsdh_sdmmc_suspend,
282 	.resume		= bcmsdh_sdmmc_resume,
283 };
284 #endif  /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
285 
286 #if defined(BCMLXSDMMC)
287 static struct semaphore *notify_semaphore = NULL;
288 
dummy_probe(struct sdio_func * func,const struct sdio_device_id * id)289 static int dummy_probe(struct sdio_func *func,
290                               const struct sdio_device_id *id)
291 {
292 	sd_err(("%s: enter\n", __FUNCTION__));
293 	if (func && (func->num != 2)) {
294 		return 0;
295 	}
296 
297 	if (notify_semaphore)
298 		up(notify_semaphore);
299 	return 0;
300 }
301 
dummy_remove(struct sdio_func * func)302 static void dummy_remove(struct sdio_func *func)
303 {
304 }
305 
306 static struct sdio_driver dummy_sdmmc_driver = {
307 	.probe		= dummy_probe,
308 	.remove		= dummy_remove,
309 	.name		= "dummy_sdmmc",
310 	.id_table	= bcmsdh_sdmmc_ids,
311 	};
312 
sdio_func_reg_notify(void * semaphore)313 int sdio_func_reg_notify(void* semaphore)
314 {
315 	notify_semaphore = semaphore;
316 	return sdio_register_driver(&dummy_sdmmc_driver);
317 }
318 
sdio_func_unreg_notify(void)319 void sdio_func_unreg_notify(void)
320 {
321 	OSL_SLEEP(15);
322 	sdio_unregister_driver(&dummy_sdmmc_driver);
323 }
324 
325 #endif /* defined(BCMLXSDMMC) */
326 
327 static struct sdio_driver bcmsdh_sdmmc_driver = {
328 	.probe		= bcmsdh_sdmmc_probe,
329 	.remove		= bcmsdh_sdmmc_remove,
330 	.name		= "bcmsdh_sdmmc",
331 	.id_table	= bcmsdh_sdmmc_ids,
332 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
333 	.drv = {
334 	.pm	= &bcmsdh_sdmmc_pm_ops,
335 	},
336 #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
337 	};
338 
339 struct sdos_info {
340 	sdioh_info_t *sd;
341 	spinlock_t lock;
342 };
343 
344 /* Interrupt enable/disable */
345 SDIOH_API_RC
sdioh_interrupt_set(sdioh_info_t * sd,bool enable)346 sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
347 {
348 	if (!sd)
349 		return BCME_BADARG;
350 
351 	sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
352 	return SDIOH_API_RC_SUCCESS;
353 }
354 
355 #ifdef BCMSDH_MODULE
356 static int __init
bcmsdh_module_init(void)357 bcmsdh_module_init(void)
358 {
359 	int error = 0;
360 	error = sdio_function_init();
361 	return error;
362 }
363 
364 static void __exit
bcmsdh_module_cleanup(void)365 bcmsdh_module_cleanup(void)
366 {
367 	sdio_function_cleanup();
368 }
369 
370 module_init(bcmsdh_module_init);
371 module_exit(bcmsdh_module_cleanup);
372 
373 MODULE_LICENSE("GPL v2");
374 MODULE_DESCRIPTION(DESCRIPTION);
375 MODULE_AUTHOR(AUTHOR);
376 
377 #endif /* BCMSDH_MODULE */
378 /*
379  * module init
380 */
bcmsdh_register_client_driver(void)381 int bcmsdh_register_client_driver(void)
382 {
383 	return sdio_register_driver(&bcmsdh_sdmmc_driver);
384 }
385 
386 /*
387  * module cleanup
388 */
bcmsdh_unregister_client_driver(void)389 void bcmsdh_unregister_client_driver(void)
390 {
391 	sdio_unregister_driver(&bcmsdh_sdmmc_driver);
392 }
393