bcmsdh_linux.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /*
  2. * SDIO access interface for drivers - linux specific (pci only)
  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_linux.c 461443 2014-03-12 02:40:59Z $
  25. */
  26. /**
  27. * @file bcmsdh_linux.c
  28. */
  29. #define __UNDEF_NO_VERSION__
  30. #include <typedefs.h>
  31. #include <linuxver.h>
  32. #include <linux/pci.h>
  33. #include <linux/completion.h>
  34. #include <osl.h>
  35. #include <pcicfg.h>
  36. #include <bcmdefs.h>
  37. #include <bcmdevs.h>
  38. #include <linux/irq.h>
  39. extern void dhdsdio_isr(void * args);
  40. #include <bcmutils.h>
  41. #include <dngl_stats.h>
  42. #include <dhd.h>
  43. #include <dhd_linux.h>
  44. /* driver info, initialized when bcmsdh_register is called */
  45. static bcmsdh_driver_t drvinfo = {NULL, NULL, NULL, NULL};
  46. typedef enum {
  47. DHD_INTR_INVALID = 0,
  48. DHD_INTR_INBAND,
  49. DHD_INTR_HWOOB,
  50. DHD_INTR_SWOOB
  51. } DHD_HOST_INTR_TYPE;
  52. /* the BCMSDH module comprises the generic part (bcmsdh.c) and OS specific layer (e.g.
  53. * bcmsdh_linux.c). Put all OS specific variables (e.g. irq number and flags) here rather
  54. * than in the common structure bcmsdh_info. bcmsdh_info only keeps a handle (os_ctx) to this
  55. * structure.
  56. */
  57. typedef struct bcmsdh_os_info {
  58. DHD_HOST_INTR_TYPE intr_type;
  59. int oob_irq_num; /* valid when hardware or software oob in use */
  60. unsigned long oob_irq_flags; /* valid when hardware or software oob in use */
  61. bool oob_irq_registered;
  62. bool oob_irq_enabled;
  63. bool oob_irq_wake_enabled;
  64. spinlock_t oob_irq_spinlock;
  65. bcmsdh_cb_fn_t oob_irq_handler;
  66. void *oob_irq_handler_context;
  67. void *context; /* context returned from upper layer */
  68. void *sdioh; /* handle to lower layer (sdioh) */
  69. void *dev; /* handle to the underlying device */
  70. bool dev_wake_enabled;
  71. } bcmsdh_os_info_t;
  72. /* debugging macros */
  73. #define SDLX_MSG(x)
  74. /**
  75. * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
  76. */
  77. bool
  78. bcmsdh_chipmatch(uint16 vendor, uint16 device)
  79. {
  80. /* Add other vendors and devices as required */
  81. #ifdef BCMSDIOH_STD
  82. /* Check for Arasan host controller */
  83. if (vendor == VENDOR_SI_IMAGE) {
  84. return (TRUE);
  85. }
  86. /* Check for BRCM 27XX Standard host controller */
  87. if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) {
  88. return (TRUE);
  89. }
  90. /* Check for BRCM Standard host controller */
  91. if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
  92. return (TRUE);
  93. }
  94. /* Check for TI PCIxx21 Standard host controller */
  95. if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) {
  96. return (TRUE);
  97. }
  98. if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) {
  99. return (TRUE);
  100. }
  101. /* Ricoh R5C822 Standard SDIO Host */
  102. if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) {
  103. return (TRUE);
  104. }
  105. /* JMicron Standard SDIO Host */
  106. if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) {
  107. return (TRUE);
  108. }
  109. #endif /* BCMSDIOH_STD */
  110. #ifdef BCMSDIOH_SPI
  111. /* This is the PciSpiHost. */
  112. if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
  113. printf("Found PCI SPI Host Controller\n");
  114. return (TRUE);
  115. }
  116. #endif /* BCMSDIOH_SPI */
  117. return (FALSE);
  118. }
  119. void* bcmsdh_probe(osl_t *osh, void *dev, void *sdioh, void *adapter_info, uint bus_type,
  120. uint bus_num, uint slot_num)
  121. {
  122. ulong regs;
  123. bcmsdh_info_t *bcmsdh;
  124. uint32 vendevid;
  125. bcmsdh_os_info_t *bcmsdh_osinfo = NULL;
  126. bcmsdh = bcmsdh_attach(osh, sdioh, &regs);
  127. if (bcmsdh == NULL) {
  128. SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
  129. goto err;
  130. }
  131. bcmsdh_osinfo = MALLOC(osh, sizeof(bcmsdh_os_info_t));
  132. if (bcmsdh_osinfo == NULL) {
  133. SDLX_MSG(("%s: failed to allocate bcmsdh_os_info_t\n", __FUNCTION__));
  134. goto err;
  135. }
  136. bzero((char *)bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
  137. bcmsdh->os_cxt = bcmsdh_osinfo;
  138. bcmsdh_osinfo->sdioh = sdioh;
  139. bcmsdh_osinfo->dev = dev;
  140. osl_set_bus_handle(osh, bcmsdh);
  141. #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
  142. if (dev && device_init_wakeup(dev, true) == 0)
  143. bcmsdh_osinfo->dev_wake_enabled = TRUE;
  144. #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
  145. #if defined(OOB_INTR_ONLY)
  146. spin_lock_init(&bcmsdh_osinfo->oob_irq_spinlock);
  147. /* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */
  148. bcmsdh_osinfo->oob_irq_num = wifi_platform_get_irq_number(adapter_info,
  149. &bcmsdh_osinfo->oob_irq_flags);
  150. if (bcmsdh_osinfo->oob_irq_num < 0) {
  151. SDLX_MSG(("%s: Host OOB irq is not defined\n", __FUNCTION__));
  152. goto err;
  153. }
  154. #endif /* defined(BCMLXSDMMC) */
  155. /* Read the vendor/device ID from the CIS */
  156. vendevid = bcmsdh_query_device(bcmsdh);
  157. /* try to attach to the target device */
  158. bcmsdh_osinfo->context = drvinfo.probe((vendevid >> 16), (vendevid & 0xFFFF), bus_num,
  159. slot_num, 0, bus_type, (void *)regs, osh, bcmsdh);
  160. if (bcmsdh_osinfo->context == NULL) {
  161. SDLX_MSG(("%s: device attach failed\n", __FUNCTION__));
  162. goto err;
  163. }
  164. return bcmsdh;
  165. /* error handling */
  166. err:
  167. if (bcmsdh != NULL)
  168. bcmsdh_detach(osh, bcmsdh);
  169. if (bcmsdh_osinfo != NULL)
  170. MFREE(osh, bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
  171. return NULL;
  172. }
  173. int bcmsdh_remove(bcmsdh_info_t *bcmsdh)
  174. {
  175. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  176. #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
  177. if (bcmsdh_osinfo->dev)
  178. device_init_wakeup(bcmsdh_osinfo->dev, false);
  179. bcmsdh_osinfo->dev_wake_enabled = FALSE;
  180. #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
  181. drvinfo.remove(bcmsdh_osinfo->context);
  182. MFREE(bcmsdh->osh, bcmsdh->os_cxt, sizeof(bcmsdh_os_info_t));
  183. bcmsdh_detach(bcmsdh->osh, bcmsdh);
  184. return 0;
  185. }
  186. int bcmsdh_suspend(bcmsdh_info_t *bcmsdh)
  187. {
  188. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  189. if (drvinfo.suspend && drvinfo.suspend(bcmsdh_osinfo->context))
  190. return -EBUSY;
  191. return 0;
  192. }
  193. int bcmsdh_resume(bcmsdh_info_t *bcmsdh)
  194. {
  195. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  196. if (drvinfo.resume)
  197. return drvinfo.resume(bcmsdh_osinfo->context);
  198. return 0;
  199. }
  200. extern int bcmsdh_register_client_driver(void);
  201. extern void bcmsdh_unregister_client_driver(void);
  202. extern int sdio_func_reg_notify(void* semaphore);
  203. extern void sdio_func_unreg_notify(void);
  204. #if defined(BCMLXSDMMC)
  205. int bcmsdh_reg_sdio_notify(void* semaphore)
  206. {
  207. return sdio_func_reg_notify(semaphore);
  208. }
  209. void bcmsdh_unreg_sdio_notify(void)
  210. {
  211. sdio_func_unreg_notify();
  212. }
  213. #endif /* defined(BCMLXSDMMC) */
  214. int
  215. bcmsdh_register(bcmsdh_driver_t *driver)
  216. {
  217. int error = 0;
  218. drvinfo = *driver;
  219. SDLX_MSG(("%s: register client driver\n", __FUNCTION__));
  220. error = bcmsdh_register_client_driver();
  221. if (error)
  222. SDLX_MSG(("%s: failed %d\n", __FUNCTION__, error));
  223. return error;
  224. }
  225. void
  226. bcmsdh_unregister(void)
  227. {
  228. #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
  229. if (bcmsdh_pci_driver.node.next == NULL)
  230. return;
  231. #endif
  232. bcmsdh_unregister_client_driver();
  233. }
  234. void bcmsdh_dev_pm_stay_awake(bcmsdh_info_t *bcmsdh)
  235. {
  236. #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
  237. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  238. pm_stay_awake(bcmsdh_osinfo->dev);
  239. #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
  240. }
  241. void bcmsdh_dev_relax(bcmsdh_info_t *bcmsdh)
  242. {
  243. #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
  244. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  245. pm_relax(bcmsdh_osinfo->dev);
  246. #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
  247. }
  248. bool bcmsdh_dev_pm_enabled(bcmsdh_info_t *bcmsdh)
  249. {
  250. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  251. return bcmsdh_osinfo->dev_wake_enabled;
  252. }
  253. #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID)
  254. void bcmsdh_oob_intr_set(bcmsdh_info_t *bcmsdh, bool enable)
  255. {
  256. unsigned long flags;
  257. bcmsdh_os_info_t *bcmsdh_osinfo;
  258. if (!bcmsdh)
  259. return;
  260. bcmsdh_osinfo = bcmsdh->os_cxt;
  261. spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
  262. if (bcmsdh_osinfo->oob_irq_enabled != enable) {
  263. if (enable)
  264. enable_irq(bcmsdh_osinfo->oob_irq_num);
  265. else
  266. disable_irq_nosync(bcmsdh_osinfo->oob_irq_num);
  267. bcmsdh_osinfo->oob_irq_enabled = enable;
  268. }
  269. spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
  270. }
  271. static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
  272. {
  273. bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)dev_id;
  274. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  275. #ifndef BCMSPI_ANDROID
  276. bcmsdh_oob_intr_set(bcmsdh, FALSE);
  277. #endif /* !BCMSPI_ANDROID */
  278. bcmsdh_osinfo->oob_irq_handler(bcmsdh_osinfo->oob_irq_handler_context);
  279. return IRQ_HANDLED;
  280. }
  281. int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handler,
  282. void* oob_irq_handler_context)
  283. {
  284. int err = 0;
  285. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  286. SDLX_MSG(("%s: Enter\n", __FUNCTION__));
  287. if (bcmsdh_osinfo->oob_irq_registered) {
  288. SDLX_MSG(("%s: irq is already registered\n", __FUNCTION__));
  289. return -EBUSY;
  290. }
  291. SDLX_MSG(("%s OOB irq=%d flags=%X \n", __FUNCTION__,
  292. (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
  293. bcmsdh_osinfo->oob_irq_handler = oob_irq_handler;
  294. bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context;
  295. err = request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
  296. bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
  297. if (err) {
  298. SDLX_MSG(("%s: request_irq failed with %d\n", __FUNCTION__, err));
  299. return err;
  300. }
  301. #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
  302. if (device_may_wakeup(bcmsdh_osinfo->dev)) {
  303. #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
  304. err = enable_irq_wake(bcmsdh_osinfo->oob_irq_num);
  305. if (!err)
  306. bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;
  307. #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
  308. }
  309. #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
  310. bcmsdh_osinfo->oob_irq_enabled = TRUE;
  311. bcmsdh_osinfo->oob_irq_registered = TRUE;
  312. return err;
  313. }
  314. void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh)
  315. {
  316. int err = 0;
  317. bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
  318. SDLX_MSG(("%s: Enter\n", __FUNCTION__));
  319. if (!bcmsdh_osinfo->oob_irq_registered) {
  320. SDLX_MSG(("%s: irq is not registered\n", __FUNCTION__));
  321. return;
  322. }
  323. if (bcmsdh_osinfo->oob_irq_wake_enabled) {
  324. #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
  325. if (device_may_wakeup(bcmsdh_osinfo->dev)) {
  326. #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
  327. err = disable_irq_wake(bcmsdh_osinfo->oob_irq_num);
  328. if (!err)
  329. bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
  330. #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
  331. }
  332. #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
  333. }
  334. if (bcmsdh_osinfo->oob_irq_enabled) {
  335. disable_irq(bcmsdh_osinfo->oob_irq_num);
  336. bcmsdh_osinfo->oob_irq_enabled = FALSE;
  337. }
  338. free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh);
  339. bcmsdh_osinfo->oob_irq_registered = FALSE;
  340. }
  341. #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */
  342. /* Module parameters specific to each host-controller driver */
  343. extern uint sd_msglevel; /* Debug message level */
  344. module_param(sd_msglevel, uint, 0);
  345. extern uint sd_power; /* 0 = SD Power OFF, 1 = SD Power ON. */
  346. module_param(sd_power, uint, 0);
  347. extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
  348. module_param(sd_clock, uint, 0);
  349. extern uint sd_divisor; /* Divisor (-1 means external clock) */
  350. module_param(sd_divisor, uint, 0);
  351. extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
  352. module_param(sd_sdmode, uint, 0);
  353. extern uint sd_hiok; /* Ok to use hi-speed mode */
  354. module_param(sd_hiok, uint, 0);
  355. extern uint sd_f2_blocksize;
  356. module_param(sd_f2_blocksize, int, 0);
  357. #ifdef BCMSDIOH_STD
  358. extern int sd_uhsimode;
  359. module_param(sd_uhsimode, int, 0);
  360. extern uint sd_tuning_period;
  361. module_param(sd_tuning_period, uint, 0);
  362. extern int sd_delay_value;
  363. module_param(sd_delay_value, uint, 0);
  364. /* SDIO Drive Strength for UHSI mode specific to SDIO3.0 */
  365. extern char dhd_sdiod_uhsi_ds_override[2];
  366. module_param_string(dhd_sdiod_uhsi_ds_override, dhd_sdiod_uhsi_ds_override, 2, 0);
  367. #endif
  368. #ifdef BCMSDH_MODULE
  369. EXPORT_SYMBOL(bcmsdh_attach);
  370. EXPORT_SYMBOL(bcmsdh_detach);
  371. EXPORT_SYMBOL(bcmsdh_intr_query);
  372. EXPORT_SYMBOL(bcmsdh_intr_enable);
  373. EXPORT_SYMBOL(bcmsdh_intr_disable);
  374. EXPORT_SYMBOL(bcmsdh_intr_reg);
  375. EXPORT_SYMBOL(bcmsdh_intr_dereg);
  376. #if defined(DHD_DEBUG)
  377. EXPORT_SYMBOL(bcmsdh_intr_pending);
  378. #endif
  379. EXPORT_SYMBOL(bcmsdh_devremove_reg);
  380. EXPORT_SYMBOL(bcmsdh_cfg_read);
  381. EXPORT_SYMBOL(bcmsdh_cfg_write);
  382. EXPORT_SYMBOL(bcmsdh_cis_read);
  383. EXPORT_SYMBOL(bcmsdh_reg_read);
  384. EXPORT_SYMBOL(bcmsdh_reg_write);
  385. EXPORT_SYMBOL(bcmsdh_regfail);
  386. EXPORT_SYMBOL(bcmsdh_send_buf);
  387. EXPORT_SYMBOL(bcmsdh_recv_buf);
  388. EXPORT_SYMBOL(bcmsdh_rwdata);
  389. EXPORT_SYMBOL(bcmsdh_abort);
  390. EXPORT_SYMBOL(bcmsdh_query_device);
  391. EXPORT_SYMBOL(bcmsdh_query_iofnum);
  392. EXPORT_SYMBOL(bcmsdh_iovar_op);
  393. EXPORT_SYMBOL(bcmsdh_register);
  394. EXPORT_SYMBOL(bcmsdh_unregister);
  395. EXPORT_SYMBOL(bcmsdh_chipmatch);
  396. EXPORT_SYMBOL(bcmsdh_reset);
  397. EXPORT_SYMBOL(bcmsdh_waitlockfree);
  398. EXPORT_SYMBOL(bcmsdh_get_dstatus);
  399. EXPORT_SYMBOL(bcmsdh_cfg_read_word);
  400. EXPORT_SYMBOL(bcmsdh_cfg_write_word);
  401. EXPORT_SYMBOL(bcmsdh_cur_sbwad);
  402. EXPORT_SYMBOL(bcmsdh_chipinfo);
  403. #endif /* BCMSDH_MODULE */