bcmsdh_sdmmc_linux.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
  3. *
  4. * Portions of this code are copyright (c) 2020 Cypress Semiconductor Corporation
  5. *
  6. * Copyright (C) 1999-2020, Broadcom Corporation
  7. *
  8. * Unless you and Broadcom execute a separate written software license
  9. * agreement governing use of this software, this software is licensed to you
  10. * under the terms of the GNU General Public License version 2 (the "GPL"),
  11. * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  12. * following added to such license:
  13. *
  14. * As a special exception, the copyright holders of this software give you
  15. * permission to link this software with independent modules, and to copy and
  16. * distribute the resulting executable under terms of your choice, provided that
  17. * you also meet, for each linked independent module, the terms and conditions of
  18. * the license of that module. An independent module is a module which is not
  19. * derived from this software. The special exception does not apply to any
  20. * modifications of the software.
  21. *
  22. * Notwithstanding the above, under no circumstances may you combine this
  23. * software in any way with any other Broadcom software provided under a license
  24. * other than the GPL, without Broadcom's express prior written consent.
  25. *
  26. *
  27. * <<Broadcom-WL-IPTag/Proprietary,Open:>>
  28. *
  29. * $Id: bcmsdh_sdmmc_linux.c 689795 2017-03-13 14:57:21Z $
  30. */
  31. #include <typedefs.h>
  32. #include <bcmutils.h>
  33. #include <sdio.h> /* SDIO Device and Protocol Specs */
  34. #include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
  35. #include <sdiovar.h> /* to get msglevel bit values */
  36. #include <linux/sched.h> /* request_irq() */
  37. #include <linux/mmc/core.h>
  38. #include <linux/mmc/card.h>
  39. #include <linux/mmc/host.h>
  40. #include <linux/mmc/sdio_func.h>
  41. #include <linux/mmc/sdio_ids.h>
  42. #include <dhd_linux.h>
  43. #include <bcmsdh_sdmmc.h>
  44. #include <dhd_dbg.h>
  45. #include <bcmdevs.h>
  46. #if !defined(SDIO_VENDOR_ID_BROADCOM)
  47. #define SDIO_VENDOR_ID_BROADCOM 0x02d0
  48. #endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
  49. #define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000
  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. int sdio_function_init(void);
  57. void sdio_function_cleanup(void);
  58. #define DESCRIPTION "bcmsdh_sdmmc Driver"
  59. #define AUTHOR "Broadcom Corporation"
  60. /* module param defaults */
  61. static int clockoverride = 0;
  62. module_param(clockoverride, int, 0644);
  63. MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
  64. /* Maximum number of bcmsdh_sdmmc devices supported by driver */
  65. #define BCMSDH_SDMMC_MAX_DEVICES 1
  66. extern volatile bool dhd_mmc_suspend;
  67. static int sdioh_probe(struct sdio_func *func)
  68. {
  69. int host_idx = func->card->host->index;
  70. uint32 rca = func->card->rca;
  71. wifi_adapter_info_t *adapter;
  72. osl_t *osh = NULL;
  73. sdioh_info_t *sdioh = NULL;
  74. sd_err(("bus num (host idx)=%d, slot num (rca)=%d\n", host_idx, rca));
  75. adapter = dhd_wifi_platform_get_adapter(SDIO_BUS, host_idx, rca);
  76. if (adapter != NULL)
  77. sd_err(("found adapter info '%s'\n", adapter->name));
  78. else
  79. sd_err(("can't find adapter info for this chip\n"));
  80. #ifdef WL_CFG80211
  81. wl_cfg80211_set_parent_dev(&func->dev);
  82. #endif // endif
  83. /* allocate SDIO Host Controller state info */
  84. osh = osl_attach(&func->dev, SDIO_BUS, TRUE);
  85. if (osh == NULL) {
  86. sd_err(("%s: osl_attach failed\n", __FUNCTION__));
  87. goto fail;
  88. }
  89. osl_static_mem_init(osh, adapter);
  90. sdioh = sdioh_attach(osh, func);
  91. if (sdioh == NULL) {
  92. sd_err(("%s: sdioh_attach failed\n", __FUNCTION__));
  93. goto fail;
  94. }
  95. sdioh->bcmsdh = bcmsdh_probe(osh, &func->dev, sdioh, adapter, SDIO_BUS, host_idx, rca);
  96. if (sdioh->bcmsdh == NULL) {
  97. sd_err(("%s: bcmsdh_probe failed\n", __FUNCTION__));
  98. goto fail;
  99. }
  100. sdio_set_drvdata(func, sdioh);
  101. return 0;
  102. fail:
  103. if (sdioh != NULL)
  104. sdioh_detach(osh, sdioh);
  105. if (osh != NULL)
  106. osl_detach(osh);
  107. return -ENOMEM;
  108. }
  109. static void sdioh_remove(struct sdio_func *func)
  110. {
  111. sdioh_info_t *sdioh;
  112. osl_t *osh;
  113. sdioh = sdio_get_drvdata(func);
  114. if (sdioh == NULL) {
  115. sd_err(("%s: error, no sdioh handler found\n", __FUNCTION__));
  116. return;
  117. }
  118. osh = sdioh->osh;
  119. bcmsdh_remove(sdioh->bcmsdh);
  120. sdioh_detach(osh, sdioh);
  121. osl_detach(osh);
  122. }
  123. static int bcmsdh_sdmmc_probe(struct sdio_func *func,
  124. const struct sdio_device_id *id)
  125. {
  126. int ret = 0;
  127. if (func == NULL)
  128. return -EINVAL;
  129. sd_err(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
  130. sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
  131. sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
  132. sd_info(("sdio_device: 0x%04x\n", func->device));
  133. sd_info(("Function#: 0x%04x\n", func->num));
  134. /* 4318 doesn't have function 2 */
  135. if ((func->num == 2) || (func->num == 1 && func->device == 0x4))
  136. ret = sdioh_probe(func);
  137. return ret;
  138. }
  139. static void bcmsdh_sdmmc_remove(struct sdio_func *func)
  140. {
  141. if (func == NULL) {
  142. sd_err(("%s is called with NULL SDIO function pointer\n", __FUNCTION__));
  143. return;
  144. }
  145. sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
  146. sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
  147. sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
  148. sd_info(("sdio_device: 0x%04x\n", func->device));
  149. sd_info(("Function#: 0x%04x\n", func->num));
  150. if ((func->num == 2) || (func->num == 1 && func->device == 0x4))
  151. sdioh_remove(func);
  152. }
  153. /* devices we support, null terminated */
  154. static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
  155. { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
  156. { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM4362_CHIP_ID) },
  157. { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43751_CHIP_ID) },
  158. { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43012_CHIP_ID) },
  159. { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43014_CHIP_ID) },
  160. { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43014_D11N_ID) },
  161. { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43014_D11N2G_ID) },
  162. { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, BCM43014_D11N5G_ID) },
  163. { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) }, /* dummy_sdmmc_driver driver can't be probe without this and cause bring up failed */
  164. /* { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_ANY_ID) }, */
  165. /* end: all zeroes */
  166. { 0, 0, 0, 0},
  167. };
  168. MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
  169. #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
  170. static int bcmsdh_sdmmc_suspend(struct device *pdev)
  171. {
  172. int err;
  173. sdioh_info_t *sdioh;
  174. struct sdio_func *func = dev_to_sdio_func(pdev);
  175. mmc_pm_flag_t sdio_flags;
  176. sd_err(("%s Enter\n", __FUNCTION__));
  177. if (func->num != 2)
  178. return 0;
  179. dhd_mmc_suspend = TRUE;
  180. sdioh = sdio_get_drvdata(func);
  181. err = bcmsdh_suspend(sdioh->bcmsdh);
  182. if (err) {
  183. dhd_mmc_suspend = FALSE;
  184. return err;
  185. }
  186. sdio_flags = sdio_get_host_pm_caps(func);
  187. if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
  188. sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__));
  189. dhd_mmc_suspend = FALSE;
  190. return -EINVAL;
  191. }
  192. /* keep power while host suspended */
  193. err = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
  194. if (err) {
  195. sd_err(("%s: error while trying to keep power\n", __FUNCTION__));
  196. dhd_mmc_suspend = FALSE;
  197. return err;
  198. }
  199. smp_mb();
  200. return 0;
  201. }
  202. static int bcmsdh_sdmmc_resume(struct device *pdev)
  203. {
  204. sdioh_info_t *sdioh;
  205. struct sdio_func *func = dev_to_sdio_func(pdev);
  206. sd_err(("%s Enter\n", __FUNCTION__));
  207. if (func->num != 2)
  208. return 0;
  209. sdioh = sdio_get_drvdata(func);
  210. dhd_mmc_suspend = FALSE;
  211. bcmsdh_resume(sdioh->bcmsdh);
  212. smp_mb();
  213. return 0;
  214. }
  215. static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = {
  216. .suspend = bcmsdh_sdmmc_suspend,
  217. .resume = bcmsdh_sdmmc_resume,
  218. };
  219. #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
  220. #if defined(BCMLXSDMMC)
  221. static struct semaphore *notify_semaphore = NULL;
  222. static int dummy_probe(struct sdio_func *func,
  223. const struct sdio_device_id *id)
  224. {
  225. sd_err(("%s: enter\n", __FUNCTION__));
  226. if (func)
  227. sd_err(("%s: func->num=0x%x; \n", __FUNCTION__, func->num));
  228. if (id) {
  229. sd_err(("%s: class=0x%x; vendor=0x%x; device=0x%x\n", __FUNCTION__,
  230. id->class, id->vendor, id->device));
  231. if (id->vendor != SDIO_VENDOR_ID_BROADCOM)
  232. return -ENODEV;
  233. }
  234. if (func && (func->num != 2)) {
  235. return 0;
  236. }
  237. if (notify_semaphore)
  238. up(notify_semaphore);
  239. return 0;
  240. }
  241. static void dummy_remove(struct sdio_func *func)
  242. {
  243. }
  244. static struct sdio_driver dummy_sdmmc_driver = {
  245. .probe = dummy_probe,
  246. .remove = dummy_remove,
  247. .name = "dummy_sdmmc",
  248. .id_table = bcmsdh_sdmmc_ids,
  249. };
  250. int sdio_func_reg_notify(void* semaphore)
  251. {
  252. notify_semaphore = semaphore;
  253. return sdio_register_driver(&dummy_sdmmc_driver);
  254. }
  255. void sdio_func_unreg_notify(void)
  256. {
  257. OSL_SLEEP(15);
  258. sdio_unregister_driver(&dummy_sdmmc_driver);
  259. }
  260. #endif /* defined(BCMLXSDMMC) */
  261. static struct sdio_driver bcmsdh_sdmmc_driver = {
  262. .probe = bcmsdh_sdmmc_probe,
  263. .remove = bcmsdh_sdmmc_remove,
  264. .name = "bcmsdh_sdmmc",
  265. .id_table = bcmsdh_sdmmc_ids,
  266. #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
  267. .drv = {
  268. .pm = &bcmsdh_sdmmc_pm_ops,
  269. },
  270. #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
  271. };
  272. struct sdos_info {
  273. sdioh_info_t *sd;
  274. spinlock_t lock;
  275. };
  276. /* Interrupt enable/disable */
  277. SDIOH_API_RC
  278. sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
  279. {
  280. if (!sd)
  281. return BCME_BADARG;
  282. sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
  283. return SDIOH_API_RC_SUCCESS;
  284. }
  285. #ifdef BCMSDH_MODULE
  286. static int __init
  287. bcmsdh_module_init(void)
  288. {
  289. int error = 0;
  290. error = sdio_function_init();
  291. return error;
  292. }
  293. static void __exit
  294. bcmsdh_module_cleanup(void)
  295. {
  296. sdio_function_cleanup();
  297. }
  298. module_init(bcmsdh_module_init);
  299. module_exit(bcmsdh_module_cleanup);
  300. MODULE_LICENSE("GPL v2");
  301. MODULE_DESCRIPTION(DESCRIPTION);
  302. MODULE_AUTHOR(AUTHOR);
  303. #endif /* BCMSDH_MODULE */
  304. /*
  305. * module init
  306. */
  307. int bcmsdh_register_client_driver(void)
  308. {
  309. return sdio_register_driver(&bcmsdh_sdmmc_driver);
  310. }
  311. /*
  312. * module cleanup
  313. */
  314. void bcmsdh_unregister_client_driver(void)
  315. {
  316. sdio_unregister_driver(&bcmsdh_sdmmc_driver);
  317. }