rkwifi: optimize wifi open time
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / rockchip_wlan / rkwifi / bcmdhd / bcmsdh_sdmmc_linux.c
1 /*
2  * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
3  *
4  * Copyright (C) 1999-2013, Broadcom Corporation
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  *      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.
23  *
24  * $Id: bcmsdh_sdmmc_linux.c 404103 2013-05-23 20:07:27Z $
25  */
26
27 #include <typedefs.h>
28 #include <bcmutils.h>
29 #include <sdio.h>       /* SDIO Device and Protocol Specs */
30 #include <bcmsdbus.h>   /* bcmsdh to/from specific controller APIs */
31 #include <sdiovar.h>    /* to get msglevel bit values */
32
33 #include <linux/sched.h>        /* request_irq() */
34
35 #include <linux/mmc/core.h>
36 #include <linux/mmc/card.h>
37 #include <linux/mmc/sdio_func.h>
38 #include <linux/mmc/sdio_ids.h>
39 #include <dhd_config.h>
40
41 #if !defined(SDIO_VENDOR_ID_BROADCOM)
42 #define SDIO_VENDOR_ID_BROADCOM         0x02d0
43 #endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
44
45 #define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000
46
47 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
48 #define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB      0x0492  /* BCM94325SDGWB */
49 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
50 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
51 #define SDIO_DEVICE_ID_BROADCOM_4325    0x0493
52 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
53 #if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
54 #define SDIO_DEVICE_ID_BROADCOM_4329    0x4329
55 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
56 #if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
57 #define SDIO_DEVICE_ID_BROADCOM_4319    0x4319
58 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */
59 #if !defined(SDIO_DEVICE_ID_BROADCOM_4330)
60 #define SDIO_DEVICE_ID_BROADCOM_4330    0x4330
61 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */
62 #if !defined(SDIO_DEVICE_ID_BROADCOM_4334)
63 #define SDIO_DEVICE_ID_BROADCOM_4334    0x4334
64 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4334) */
65 #if !defined(SDIO_DEVICE_ID_BROADCOM_4324)
66 #define SDIO_DEVICE_ID_BROADCOM_4324    0x4324
67 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4324) */
68 #if !defined(SDIO_DEVICE_ID_BROADCOM_43239)
69 #define SDIO_DEVICE_ID_BROADCOM_43239    43239
70 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_43239) */
71
72
73 #include <bcmsdh_sdmmc.h>
74
75 #include <dhd_dbg.h>
76
77 #ifdef WL_CFG80211
78 extern void wl_cfg80211_set_parent_dev(void *dev);
79 #endif
80
81 extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
82 extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
83 extern int dhd_os_check_wakelock(void *dhdp);
84 extern int dhd_os_check_if_up(void *dhdp);
85 extern void *bcmsdh_get_drvdata(void);
86
87 int sdio_function_init(void);
88 void sdio_function_cleanup(void);
89
90 #define DESCRIPTION "bcmsdh_sdmmc Driver"
91 #define AUTHOR "Broadcom Corporation"
92
93 /* module param defaults */
94 static int clockoverride = 0;
95
96 module_param(clockoverride, int, 0644);
97 MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
98
99 PBCMSDH_SDMMC_INSTANCE gInstance;
100
101 /* Maximum number of bcmsdh_sdmmc devices supported by driver */
102 #define BCMSDH_SDMMC_MAX_DEVICES 1
103
104 extern int bcmsdh_probe(struct device *dev);
105 extern int bcmsdh_remove(struct device *dev);
106 extern volatile bool dhd_mmc_suspend;
107
108 static int bcmsdh_sdmmc_probe(struct sdio_func *func,
109                               const struct sdio_device_id *id)
110 {
111         int ret = 0;
112         static struct sdio_func sdio_func_0;
113
114         if (!gInstance)
115                 return -EINVAL;
116
117         if (func) {
118                 sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
119                 sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class));
120                 sd_trace(("sdio_vendor: 0x%04x\n", func->vendor));
121                 sd_trace(("sdio_device: 0x%04x\n", func->device));
122                 sd_trace(("Function#: 0x%04x\n", func->num));
123
124                 if (func->num == 1) {
125                         sdio_func_0.num = 0;
126                         sdio_func_0.card = func->card;
127                         gInstance->func[0] = &sdio_func_0;
128                         if(func->device == 0x4) { /* 4318 */
129                                 gInstance->func[2] = NULL;
130                                 sd_trace(("NIC found, calling bcmsdh_probe...\n"));
131                                 ret = bcmsdh_probe(&func->dev);
132                         }
133                 }
134
135                 gInstance->func[func->num] = func;
136
137                 if (func->num == 2) {
138         #ifdef WL_CFG80211
139                         wl_cfg80211_set_parent_dev(&func->dev);
140         #endif
141                         sd_trace(("F2 found, calling bcmsdh_probe...\n"));
142                         ret = bcmsdh_probe(&func->dev);
143                         if (ret < 0)
144                                 gInstance->func[2] = NULL;
145                 }
146         } else {
147                 ret = -ENODEV;
148         }
149 #ifdef POWER_OFF_IN_SUSPEND
150         dhd_conf_register_wifi_suspend(func);
151 #endif
152
153         return ret;
154 }
155
156 static void bcmsdh_sdmmc_remove(struct sdio_func *func)
157 {
158         if (func) {
159                 sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
160                 sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
161                 sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
162                 sd_info(("sdio_device: 0x%04x\n", func->device));
163                 sd_info(("Function#: 0x%04x\n", func->num));
164
165 #ifdef POWER_OFF_IN_SUSPEND
166                 dhd_conf_unregister_wifi_suspend(func);
167 #endif
168                 if (gInstance->func[2]) {
169                         sd_trace(("F2 found, calling bcmsdh_remove...\n"));
170                         bcmsdh_remove(&func->dev);
171                         gInstance->func[2] = NULL;
172                 }
173                 if (func->num == 1) {
174                         sdio_claim_host(func);
175                         sdio_disable_func(func);
176                         sdio_release_host(func);
177                         gInstance->func[1] = NULL;
178                 }
179         }
180 }
181
182 /* devices we support, null terminated */
183 static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
184         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
185         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) },
186         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
187         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) },
188         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) },
189         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) },
190         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) },
191         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) },
192         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) },
193         { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE)            },
194         { /* end: all zeroes */                         },
195 };
196
197 MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
198
199 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
200 static int bcmsdh_sdmmc_suspend(struct device *pdev)
201 {
202         struct sdio_func *func = dev_to_sdio_func(pdev);
203 #ifndef POWER_OFF_IN_SUSPEND
204         mmc_pm_flag_t sdio_flags;
205         int ret;
206 #endif
207
208         if (func->num != 2)
209                 return 0;
210
211         sd_trace(("%s Enter\n", __FUNCTION__));
212         if (dhd_os_check_wakelock(bcmsdh_get_drvdata()))
213                 return -EBUSY;
214
215 #ifdef POWER_OFF_IN_SUSPEND
216         dhd_conf_wifi_suspend(func);
217 #else
218         sdio_flags = sdio_get_host_pm_caps(func);
219
220         if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
221                 sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__));
222                 return  -EINVAL;
223         }
224
225         /* keep power while host suspended */
226         ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
227         if (ret) {
228                 sd_err(("%s: error while trying to keep power\n", __FUNCTION__));
229                 return ret;
230         }
231 #if defined(OOB_INTR_ONLY)
232         bcmsdh_oob_intr_set(0);
233 #endif
234 #endif
235         dhd_mmc_suspend = TRUE;
236         smp_mb();
237
238         return 0;
239 }
240
241 static int bcmsdh_sdmmc_resume(struct device *pdev)
242 {
243 #if defined(OOB_INTR_ONLY) || defined(POWER_OFF_IN_SUSPEND)
244         struct sdio_func *func = dev_to_sdio_func(pdev);
245 #endif
246         sd_trace(("%s Enter\n", __FUNCTION__));
247         dhd_mmc_suspend = FALSE;
248         
249 #ifdef POWER_OFF_IN_SUSPEND
250         gInstance->func[func->num] = func;
251 #else
252 #if defined(OOB_INTR_ONLY)
253         if ((func->num == 2) && dhd_os_check_if_up(bcmsdh_get_drvdata()))
254                 bcmsdh_oob_intr_set(1);
255 #endif 
256 #endif
257         smp_mb();
258         return 0;
259 }
260
261 static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = {
262         .suspend        = bcmsdh_sdmmc_suspend,
263         .resume         = bcmsdh_sdmmc_resume,
264 };
265 #endif  /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
266
267 #if defined(BCMLXSDMMC)
268 static struct semaphore *notify_semaphore = NULL;
269
270 static int dummy_probe(struct sdio_func *func,
271                               const struct sdio_device_id *id)
272 {
273         if (notify_semaphore)
274                 up(notify_semaphore);
275         return 0;
276 }
277
278 static void dummy_remove(struct sdio_func *func)
279 {
280 }
281
282 static struct sdio_driver dummy_sdmmc_driver = {
283         .probe          = dummy_probe,
284         .remove         = dummy_remove,
285         .name           = "dummy_sdmmc",
286         .id_table       = bcmsdh_sdmmc_ids,
287         };
288
289 int sdio_func_reg_notify(void* semaphore)
290 {
291         notify_semaphore = semaphore;
292         if (notify_semaphore)
293                 up(notify_semaphore);
294         return 0;       
295         //return sdio_register_driver(&dummy_sdmmc_driver);
296 }
297
298 void sdio_func_unreg_notify(void)
299 {
300         //sdio_unregister_driver(&dummy_sdmmc_driver);
301 }
302
303 #endif /* defined(BCMLXSDMMC) */
304
305 static struct sdio_driver bcmsdh_sdmmc_driver = {
306         .probe          = bcmsdh_sdmmc_probe,
307         .remove         = bcmsdh_sdmmc_remove,
308         .name           = "bcmsdh_sdmmc",
309         .id_table       = bcmsdh_sdmmc_ids,
310 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
311         .drv = {
312         .pm     = &bcmsdh_sdmmc_pm_ops,
313         },
314 #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
315         };
316
317 struct sdos_info {
318         sdioh_info_t *sd;
319         spinlock_t lock;
320 };
321
322
323 int
324 sdioh_sdmmc_osinit(sdioh_info_t *sd)
325 {
326         struct sdos_info *sdos;
327
328         if (!sd)
329                 return BCME_BADARG;
330
331         sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
332         sd->sdos_info = (void*)sdos;
333         if (sdos == NULL)
334                 return BCME_NOMEM;
335
336         sdos->sd = sd;
337         spin_lock_init(&sdos->lock);
338         return BCME_OK;
339 }
340
341 void
342 sdioh_sdmmc_osfree(sdioh_info_t *sd)
343 {
344         struct sdos_info *sdos;
345         ASSERT(sd && sd->sdos_info);
346
347         sdos = (struct sdos_info *)sd->sdos_info;
348         MFREE(sd->osh, sdos, sizeof(struct sdos_info));
349 }
350
351 /* Interrupt enable/disable */
352 SDIOH_API_RC
353 sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
354 {
355         ulong flags;
356         struct sdos_info *sdos;
357
358         if (!sd)
359                 return BCME_BADARG;
360
361         sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
362
363         sdos = (struct sdos_info *)sd->sdos_info;
364         ASSERT(sdos);
365
366 #if !defined(OOB_INTR_ONLY)
367         if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
368                 sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
369                 return SDIOH_API_RC_FAIL;
370         }
371 #endif /* !defined(OOB_INTR_ONLY) */
372
373         /* Ensure atomicity for enable/disable calls */
374         spin_lock_irqsave(&sdos->lock, flags);
375
376         sd->client_intr_enabled = enable;
377         if (enable) {
378                 sdioh_sdmmc_devintr_on(sd);
379         } else {
380                 sdioh_sdmmc_devintr_off(sd);
381         }
382
383         spin_unlock_irqrestore(&sdos->lock, flags);
384
385         return SDIOH_API_RC_SUCCESS;
386 }
387
388
389 #ifdef BCMSDH_MODULE
390 static int __init
391 bcmsdh_module_init(void)
392 {
393         int error = 0;
394         error = sdio_function_init();
395         return error;
396 }
397
398 static void __exit
399 bcmsdh_module_cleanup(void)
400 {
401         sdio_function_cleanup();
402 }
403
404 module_init(bcmsdh_module_init);
405 module_exit(bcmsdh_module_cleanup);
406
407 MODULE_LICENSE("GPL v2");
408 MODULE_DESCRIPTION(DESCRIPTION);
409 MODULE_AUTHOR(AUTHOR);
410
411 #endif /* BCMSDH_MODULE */
412 /*
413  * module init
414 */
415 int sdio_function_init(void)
416 {
417         int error = 0;
418         sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
419
420         gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
421         if (!gInstance)
422                 return -ENOMEM;
423
424         error = sdio_register_driver(&bcmsdh_sdmmc_driver);
425         if (error) {
426                 kfree(gInstance);
427                 gInstance = NULL;
428         }
429
430         return error;
431 }
432
433 /*
434  * module cleanup
435 */
436 extern int bcmsdh_remove(struct device *dev);
437 void sdio_function_cleanup(void)
438 {
439         sd_trace(("%s Enter\n", __FUNCTION__));
440
441
442         sdio_unregister_driver(&bcmsdh_sdmmc_driver);
443
444         if (gInstance) {
445                 kfree(gInstance);
446                 gInstance = NULL;
447         }
448 }