83f40c36cc460abaec0c69539b9946a17861256d
[linux-3.10.git] / drivers / net / wireless / 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-2015, 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 434777 2013-11-07 09:30:27Z $
25  */
26
27 #include <typedefs.h>
28 #include "dynamic.h"
29 #include <bcmutils.h>
30 #include <sdio.h>       /* SDIO Device and Protocol Specs */
31 #include <bcmsdbus.h>   /* bcmsdh to/from specific controller APIs */
32 #include <sdiovar.h>    /* to get msglevel bit values */
33
34 #include <linux/sched.h>        /* request_irq() */
35
36 #include <linux/mmc/core.h>
37 #include <linux/mmc/card.h>
38 #include <linux/mmc/host.h>
39 #include <linux/mmc/sdio_func.h>
40 #include <linux/mmc/sdio_ids.h>
41 #include <dhd_linux.h>
42 #include <bcmsdh_sdmmc.h>
43 #include <dhd_dbg.h>
44 #if defined(CONFIG_WIFI_CONTROL_FUNC)
45 #include <linux/wlan_plat.h>
46 #endif
47
48 #ifdef  CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
49 #include "dhd_custom_sysfs_tegra.h"
50 #endif
51
52 #if !defined(SDIO_VENDOR_ID_BROADCOM)
53 #define SDIO_VENDOR_ID_BROADCOM         0x02d0
54 #endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
55
56 #define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000
57
58 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
59 #define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB      0x0492  /* BCM94325SDGWB */
60 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
61 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
62 #define SDIO_DEVICE_ID_BROADCOM_4325    0x0493
63 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
64 #if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
65 #define SDIO_DEVICE_ID_BROADCOM_4329    0x4329
66 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
67 #if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
68 #define SDIO_DEVICE_ID_BROADCOM_4319    0x4319
69 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */
70 #if !defined(SDIO_DEVICE_ID_BROADCOM_4330)
71 #define SDIO_DEVICE_ID_BROADCOM_4330    0x4330
72 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */
73 #if !defined(SDIO_DEVICE_ID_BROADCOM_4334)
74 #define SDIO_DEVICE_ID_BROADCOM_4334    0x4334
75 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4334) */
76 #if !defined(SDIO_DEVICE_ID_BROADCOM_4324)
77 #define SDIO_DEVICE_ID_BROADCOM_4324    0x4324
78 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4324) */
79 #if !defined(SDIO_DEVICE_ID_BROADCOM_43239)
80 #define SDIO_DEVICE_ID_BROADCOM_43239    43239
81 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_43239) */
82 #if !defined(SDIO_DEVICE_ID_BROADCOM_4339)
83 #define SDIO_DEVICE_ID_BROADCOM_4339    4339
84 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4339) */
85
86 extern void wl_cfg80211_set_parent_dev(void *dev);
87 extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
88 extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
89 extern void* bcmsdh_probe(osl_t *osh, void *dev, void *sdioh, void *adapter_info, uint bus_type,
90         uint bus_num, uint slot_num);
91 extern int bcmsdh_remove(bcmsdh_info_t *bcmsdh);
92
93 int sdio_function_init(void);
94 void sdio_function_cleanup(void);
95 int card_removed;
96
97 #define DESCRIPTION "bcmsdh_sdmmc Driver"
98 #define AUTHOR "Broadcom Corporation"
99
100 /* module param defaults */
101 static int clockoverride = 0;
102
103 module_param(clockoverride, int, 0644);
104 MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
105
106 /* Maximum number of bcmsdh_sdmmc devices supported by driver */
107 #define BCMSDH_SDMMC_MAX_DEVICES 1
108
109 extern volatile bool dhd_mmc_suspend;
110
111 static int sdioh_probe(struct sdio_func *func)
112 {
113         int host_idx = func->card->host->index;
114         uint32 rca = func->card->rca;
115         wifi_adapter_info_t *adapter;
116         osl_t *osh = NULL;
117         sdioh_info_t *sdioh = NULL;
118 #if defined(CONFIG_WIFI_CONTROL_FUNC)
119         struct wifi_platform_data *plat_data;
120 #endif
121
122         sd_err(("bus num (host idx)=%d, slot num (rca)=%d\n", host_idx, rca));
123         adapter = dhd_wifi_platform_get_adapter(SDIO_BUS, host_idx, rca);
124         if (adapter  != NULL) {
125                 sd_err(("found adapter info '%s'\n", adapter->name));
126 #if defined(CONFIG_WIFI_CONTROL_FUNC)
127                 if (adapter->wifi_plat_data) {
128                         plat_data = adapter->wifi_plat_data;
129                         /* sdio card detection is completed,
130                          * so stop card detection here */
131                         if (plat_data->set_carddetect) {
132                                 sd_debug(("stopping card detection\n"));
133                                 plat_data->set_carddetect(0);
134                         }
135                         else
136                                 sd_err(("set_carddetect is not registered\n"));
137                 }
138                 else
139                         sd_err(("platform data is NULL\n"));
140 #endif
141         } else
142                 sd_err(("can't find adapter info for this chip\n"));
143
144 #ifdef WL_CFG80211
145         wl_cfg80211_set_parent_dev(&func->dev);
146 #endif
147
148          /* allocate SDIO Host Controller state info */
149          osh = osl_attach(&func->dev, SDIO_BUS, TRUE);
150          if (osh == NULL) {
151                  sd_err(("%s: osl_attach failed\n", __FUNCTION__));
152                  goto fail;
153          }
154          osl_static_mem_init(osh, adapter);
155          sdioh = sdioh_attach(osh, func);
156          if (sdioh == NULL) {
157                  sd_err(("%s: sdioh_attach failed\n", __FUNCTION__));
158                  goto fail;
159          }
160          sdioh->bcmsdh = bcmsdh_probe(osh, &func->dev, sdioh, adapter, SDIO_BUS, host_idx, rca);
161          if (sdioh->bcmsdh == NULL) {
162                  sd_err(("%s: bcmsdh_probe failed\n", __FUNCTION__));
163                  goto fail;
164          }
165
166         sdio_set_drvdata(func, sdioh);
167         return 0;
168
169 fail:
170         if (sdioh != NULL)
171                 sdioh_detach(osh, sdioh);
172         if (osh != NULL)
173                 osl_detach(osh);
174         return -ENOMEM;
175 }
176
177 static void sdioh_remove(struct sdio_func *func)
178 {
179         sdioh_info_t *sdioh;
180         osl_t *osh;
181
182         sdioh = sdio_get_drvdata(func);
183         if (sdioh == NULL) {
184                 sd_err(("%s: error, no sdioh handler found\n", __FUNCTION__));
185                 return;
186         }
187
188         osh = sdioh->osh;
189         bcmsdh_remove(sdioh->bcmsdh);
190         sdioh_detach(osh, sdioh);
191         osl_detach(osh);
192 }
193
194 #ifdef CONFIG_BCMDYNAMIC
195
196 int bcmdhd_chipid;
197 int bcmdhd_custom_ampdu_ba_wsize;
198 extern int dhd_dpc_prio;
199 extern int dhd_rxf_prio;
200 int bcmdhd_custom_glom_setting;
201 int bcmdhd_custom_rxchain;
202 extern int firstread;
203 int bcmdhd_custom_ampdu_mpdu;
204 int bcmdhd_custom_pspretend_thr;
205 int bcmdhd_wifi_turnon_delay;
206 int bcmdhd_wifi_turnoff_delay;
207
208 bool bcmdhd_use_custom_ampdu_mpdu;
209 bool bcmdhd_use_custom_pspretend_thr;
210
211 bool bcmdhd_custom_rxcb;
212 bool bcmdhd_prop_txstatus_vsdb;
213 bool bcmdhd_vsdb_bw_allocate_enable;
214 bool bcmdhd_bcmsdioh_txglom;
215 bool bcmdhd_bcmsdioh_txglow_highspeed;
216 bool bcmdhd_use_wl_txbf;
217 bool bcmdhd_use_wl_frameburst;
218 bool bcmdhd_disable_roam_event;
219 bool bcmdhd_support_p2p_go_ps;
220 bool bcmdhd_wl11u;
221 bool bcmdhd_dhd_enable_lpc;
222
223 static void bcmdhd_dynamic_configure(int chipid)
224 {
225         bcmdhd_chipid = chipid;
226         if (chipid == 0x4324) {
227                 /* from the old cflags */
228                 bcmdhd_custom_ampdu_ba_wsize = 32;
229                 dhd_dpc_prio = dhd_rxf_prio = MAX_USER_RT_PRIO/2;
230                 /* set on/off delay */
231                 bcmdhd_wifi_turnon_delay = 400;
232                 bcmdhd_wifi_turnoff_delay = 400;
233
234                 bcmdhd_prop_txstatus_vsdb = true;
235                 bcmdhd_vsdb_bw_allocate_enable = true;
236
237                 /* defaults */
238                 bcmdhd_custom_glom_setting = DEFAULT_GLOM_VALUE;
239                 bcmdhd_custom_ampdu_mpdu = -1;
240         } else if(chipid == 0x4354) {
241                 bcmdhd_custom_glom_setting = 8;
242                 bcmdhd_custom_rxchain = 1;
243                 bcmdhd_custom_rxcb=true;
244                 bcmdhd_custom_ampdu_ba_wsize = 64;
245                 firstread = 128;
246                 bcmdhd_use_custom_ampdu_mpdu = true;
247                 bcmdhd_custom_ampdu_mpdu = 16;
248                 bcmdhd_use_custom_pspretend_thr = true;
249                 bcmdhd_custom_pspretend_thr = 30;
250                 dhd_dpc_prio = dhd_rxf_prio = 99;
251                 /* set on/off delay */
252                 bcmdhd_wifi_turnon_delay = 200;
253                 bcmdhd_wifi_turnoff_delay = 200;
254
255                 bcmdhd_bcmsdioh_txglom = true;
256                 bcmdhd_use_wl_txbf = true;
257                 bcmdhd_use_wl_frameburst = true;
258                 bcmdhd_disable_roam_event = true;
259                 bcmdhd_support_p2p_go_ps = true;
260                 bcmdhd_wl11u = true;
261                 bcmdhd_dhd_enable_lpc = true;
262         } else {
263                 BUG();
264         }
265 }
266
267 #else
268 static void bcmdhd_dynamic_configure(int chipid) {}
269 #endif
270
271 static int bcmsdh_sdmmc_probe(struct sdio_func *func,
272                               const struct sdio_device_id *id)
273 {
274         int ret = 0;
275
276         if (func == NULL)
277                 return -EINVAL;
278
279         sd_err(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
280         sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
281         sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
282         sd_info(("sdio_device: 0x%04x\n", func->device));
283         sd_info(("Function#: 0x%04x\n", func->num));
284
285         bcmdhd_dynamic_configure(func->device);
286
287         /* 4318 doesn't have function 2 */
288         if ((func->num == 2) || (func->num == 1 && func->device == 0x4)) {
289                 ret = sdioh_probe(func);
290                 if (mmc_power_save_host(func->card->host))
291                         sd_err(("%s: card power save fail", __FUNCTION__));
292         }
293
294         return ret;
295 }
296
297 static void bcmsdh_sdmmc_remove(struct sdio_func *func)
298 {
299         if (func == NULL) {
300                 sd_err(("%s is called with NULL SDIO function pointer\n", __FUNCTION__));
301                 return;
302         }
303         card_removed = 1;
304         sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
305         sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
306         sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
307         sd_info(("sdio_device: 0x%04x\n", func->device));
308         sd_info(("Function#: 0x%04x\n", func->num));
309
310         if (func->card->sdio_func[1])
311                 mmc_power_restore_host(func->card->host);
312
313         if ((func->num == 2) || (func->num == 1 && func->device == 0x4))
314                 sdioh_remove(func);
315
316         if (mmc_power_save_host(func->card->host))
317                 sd_err(("%s: card power save fail", __FUNCTION__));
318         card_removed = 0;
319 }
320
321 /* devices we support, null terminated */
322 static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
323 #if 0
324         {       .class  = SDIO_CLASS_NONE,
325                 .vendor = SDIO_VENDOR_ID_BROADCOM,
326                 .device = SDIO_ANY_ID
327         },
328 #elif 1
329         /* BCM4354 */
330         {       .class  = SDIO_CLASS_NONE,
331                 .vendor = SDIO_VENDOR_ID_BROADCOM,
332                 .device = 0x4354
333         },
334         /* BCM43241 */
335         {       .class  = SDIO_CLASS_NONE,
336                 .vendor = SDIO_VENDOR_ID_BROADCOM,
337                 .device = 0x4324
338         },
339         /* BCM4339 */
340         {       .class  = SDIO_CLASS_NONE,
341                 .vendor = SDIO_VENDOR_ID_BROADCOM,
342                 .device = 0x4339
343         },
344
345 #else
346         /* BCM43341 */
347         {       .class  = SDIO_CLASS_NONE,
348                 .vendor = SDIO_VENDOR_ID_BROADCOM,
349                 .device = 0xa94d
350         },
351 #endif
352         { /* end: all zeroes */                         },
353 };
354
355 MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
356
357 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
358 static int bcmsdh_sdmmc_suspend(struct device *pdev)
359 {
360         int err;
361         sdioh_info_t *sdioh;
362         struct sdio_func *func = dev_to_sdio_func(pdev);
363         mmc_pm_flag_t sdio_flags;
364
365         sd_err(("%s Enter\n", __FUNCTION__));
366         if (func->num != 2)
367                 return 0;
368
369         sdioh = sdio_get_drvdata(func);
370         err = bcmsdh_suspend(sdioh->bcmsdh);
371         if (err)
372                 return err;
373
374         sdio_flags = sdio_get_host_pm_caps(func);
375         if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
376                 sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__));
377                 return  -EINVAL;
378         }
379
380 #ifdef  CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
381         tegra_sysfs_suspend();
382 #endif
383
384         /* keep power while host suspended */
385         err = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
386         if (err) {
387                 sd_err(("%s: error while trying to keep power\n", __FUNCTION__));
388                 return err;
389         }
390 #if defined(OOB_INTR_ONLY)
391         bcmsdh_oob_intr_set(sdioh->bcmsdh, FALSE);
392 #endif 
393         dhd_mmc_suspend = TRUE;
394         smp_mb();
395
396         return 0;
397 }
398
399 static int bcmsdh_sdmmc_resume(struct device *pdev)
400 {
401         sdioh_info_t *sdioh;
402         struct sdio_func *func = dev_to_sdio_func(pdev);
403
404         sd_err(("%s Enter\n", __FUNCTION__));
405         if (func->num != 2)
406                 return 0;
407
408         sdioh = sdio_get_drvdata(func);
409         dhd_mmc_suspend = FALSE;
410 #if defined(OOB_INTR_ONLY)
411         bcmsdh_resume(sdioh->bcmsdh);
412 #endif 
413 #ifdef  CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
414         tegra_sysfs_resume();
415 #endif
416
417         smp_mb();
418         return 0;
419 }
420
421 #ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
422 static int bcmsdh_sdmmc_resume_noirq(struct device *pdev)
423 {
424         tegra_sysfs_resume_capture();
425         return 0;
426 }
427 #endif
428
429 static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = {
430         .suspend        = bcmsdh_sdmmc_suspend,
431         .resume         = bcmsdh_sdmmc_resume,
432 #ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
433         .resume_noirq   = bcmsdh_sdmmc_resume_noirq,
434 #endif
435 };
436 #endif  /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
437
438 #if defined(BCMLXSDMMC)
439 static struct semaphore *notify_semaphore = NULL;
440
441 static int dummy_probe(struct sdio_func *func,
442                               const struct sdio_device_id *id)
443 {
444         if (func && (func->num != 2)) {
445                 return 0;
446         }
447
448         if (notify_semaphore)
449                 up(notify_semaphore);
450         return 0;
451 }
452
453 static void dummy_remove(struct sdio_func *func)
454 {
455 }
456
457 static struct sdio_driver dummy_sdmmc_driver = {
458         .probe          = dummy_probe,
459         .remove         = dummy_remove,
460         .name           = "dummy_sdmmc",
461         .id_table       = bcmsdh_sdmmc_ids,
462         };
463
464 int sdio_func_reg_notify(void* semaphore)
465 {
466         notify_semaphore = semaphore;
467         return sdio_register_driver(&dummy_sdmmc_driver);
468 }
469
470 void sdio_func_unreg_notify(void)
471 {
472         OSL_SLEEP(15);
473         sdio_unregister_driver(&dummy_sdmmc_driver);
474 }
475
476 #endif /* defined(BCMLXSDMMC) */
477
478 static struct sdio_driver bcmsdh_sdmmc_driver = {
479         .probe          = bcmsdh_sdmmc_probe,
480         .remove         = bcmsdh_sdmmc_remove,
481         .name           = "bcmsdh_sdmmc",
482         .id_table       = bcmsdh_sdmmc_ids,
483 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
484         .drv = {
485         .pm     = &bcmsdh_sdmmc_pm_ops,
486         },
487 #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
488         };
489
490 struct sdos_info {
491         sdioh_info_t *sd;
492         spinlock_t lock;
493 };
494
495 /* Interrupt enable/disable */
496 SDIOH_API_RC
497 sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
498 {
499         if (!sd)
500                 return BCME_BADARG;
501
502         sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
503         return SDIOH_API_RC_SUCCESS;
504 }
505
506 #ifdef BCMSDH_MODULE
507 static int __init
508 bcmsdh_module_init(void)
509 {
510         int error = 0;
511         error = sdio_function_init();
512         return error;
513 }
514
515 static void __exit
516 bcmsdh_module_cleanup(void)
517 {
518         sdio_function_cleanup();
519 }
520
521 module_init(bcmsdh_module_init);
522 module_exit(bcmsdh_module_cleanup);
523
524 MODULE_LICENSE("GPL v2");
525 MODULE_DESCRIPTION(DESCRIPTION);
526 MODULE_AUTHOR(AUTHOR);
527
528 #endif /* BCMSDH_MODULE */
529 /*
530  * module init
531 */
532 int bcmsdh_register_client_driver(void)
533 {
534         return sdio_register_driver(&bcmsdh_sdmmc_driver);
535 }
536
537 /*
538  * module cleanup
539 */
540 void bcmsdh_unregister_client_driver(void)
541 {
542         sdio_unregister_driver(&bcmsdh_sdmmc_driver);
543 }