1 /*
2 * SDIO access interface for drivers - linux specific (pci only)
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 /**
27 * @file bcmsdh_linux.c
28 */
29
30 #define __UNDEF_NO_VERSION__
31
32 #include <typedefs.h>
33 #include <linuxver.h>
34 #include <linux/pci.h>
35 #include <linux/completion.h>
36
37 #include <osl.h>
38 #include <pcicfg.h>
39 #include <bcmdefs.h>
40 #include <bcmdevs.h>
41 #include <linux/irq.h>
42 extern void dhdsdio_isr(void * args);
43 #include <bcmutils.h>
44 #include <dngl_stats.h>
45 #include <dhd.h>
46 #if defined(CONFIG_ARCH_ODIN)
47 #include <linux/platform_data/gpio-odin.h>
48 #endif /* defined(CONFIG_ARCH_ODIN) */
49 #include <dhd_linux.h>
50
51 /* driver info, initialized when bcmsdh_register is called */
52 static bcmsdh_driver_t drvinfo = {NULL, NULL, NULL, NULL};
53
54 typedef enum {
55 DHD_INTR_INVALID = 0,
56 DHD_INTR_INBAND,
57 DHD_INTR_HWOOB,
58 DHD_INTR_SWOOB
59 } DHD_HOST_INTR_TYPE;
60
61 /* the BCMSDH module comprises the generic part (bcmsdh.c) and OS specific layer (e.g.
62 * bcmsdh_linux.c). Put all OS specific variables (e.g. irq number and flags) here rather
63 * than in the common structure bcmsdh_info. bcmsdh_info only keeps a handle (os_ctx) to this
64 * structure.
65 */
66 typedef struct bcmsdh_os_info {
67 DHD_HOST_INTR_TYPE intr_type;
68 int oob_irq_num; /* valid when hardware or software oob in use */
69 unsigned long oob_irq_flags; /* valid when hardware or software oob in use */
70 bool oob_irq_registered;
71 bool oob_irq_enabled;
72 bool oob_irq_wake_enabled;
73 spinlock_t oob_irq_spinlock;
74 bcmsdh_cb_fn_t oob_irq_handler;
75 void *oob_irq_handler_context;
76 void *context; /* context returned from upper layer */
77 void *sdioh; /* handle to lower layer (sdioh) */
78 void *dev; /* handle to the underlying device */
79 bool dev_wake_enabled;
80 } bcmsdh_os_info_t;
81
82 /* debugging macros */
83 #ifdef BCMDBG_ERR
84 #define SDLX_ERR(x) printf x
85 #define SDLX_MSG(x) printf x
86 #else
87 #define SDLX_ERR(x) printf x
88 #define SDLX_MSG(x) printf x
89 #endif /* BCMDBG_ERR */
90
91 /**
92 * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
93 */
94 bool
bcmsdh_chipmatch(uint16 vendor,uint16 device)95 bcmsdh_chipmatch(uint16 vendor, uint16 device)
96 {
97 /* Add other vendors and devices as required */
98 #ifdef BCMINTERNAL
99 #ifdef BCMSDIOH_BCM
100 if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
101 return (TRUE);
102 }
103 if (device == BCM_SDIOH_ID && vendor == VENDOR_BROADCOM) {
104 return (TRUE);
105 }
106 if (device == BCM4710_DEVICE_ID && vendor == VENDOR_BROADCOM) {
107 return (TRUE);
108 }
109 /* For now still accept the old devid */
110 if (device == 0x4380 && vendor == VENDOR_BROADCOM) {
111 return (TRUE);
112 }
113 #endif /* BCMSDIOH_BCM */
114 #endif /* BCMINTERNAL */
115
116 #ifdef BCMSDIOH_STD
117 /* Check for Arasan host controller */
118 if (vendor == VENDOR_SI_IMAGE) {
119 return (TRUE);
120 }
121 /* Check for BRCM 27XX Standard host controller */
122 if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) {
123 return (TRUE);
124 }
125 /* Check for BRCM Standard host controller */
126 if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
127 return (TRUE);
128 }
129 /* Check for TI PCIxx21 Standard host controller */
130 if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) {
131 return (TRUE);
132 }
133 if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) {
134 return (TRUE);
135 }
136 /* Ricoh R5C822 Standard SDIO Host */
137 if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) {
138 return (TRUE);
139 }
140 /* JMicron Standard SDIO Host */
141 if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) {
142 return (TRUE);
143 }
144
145 #ifdef BCMINTERNAL
146 /* Check for Jinvani (C-Guys) host controller */
147 if (device == JINVANI_SDIOH_ID && vendor == VENDOR_JINVANI) {
148 return (TRUE);
149 }
150 #endif /* BCMINTERNAL */
151 #endif /* BCMSDIOH_STD */
152 #ifdef BCMSDIOH_SPI
153 /* This is the PciSpiHost. */
154 if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
155 printf("Found PCI SPI Host Controller\n");
156 return (TRUE);
157 }
158
159 #ifdef BCMINTERNAL
160 /* This is the SPI Host for QT. */
161 if (device == BCM_SPIH_ID && vendor == VENDOR_BROADCOM) {
162 printf("Found SPI Host Controller\n");
163 return (TRUE);
164 }
165 #endif /* BCMINTERNAL */
166 #endif /* BCMSDIOH_SPI */
167
168 #ifdef BCMINTERNAL
169 /*
170 * XXX - This is a hack to get the GPL SdioLinux driver to load on Arasan/x86
171 * This is accomplished by installing a PciSpiHost into the system alongside the
172 * Arasan controller. The PciSpiHost is just used to get BCMSDH loaded.
173 */
174 #ifdef BCMSDH_FD
175 if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
176 printf("Found SdioLinux Host Controller\n");
177 return (TRUE);
178 }
179 #endif /* BCMSDH_FD */
180 #endif /* BCMINTERNAL */
181 return (FALSE);
182 }
183
bcmsdh_probe(osl_t * osh,void * dev,void * sdioh,void * adapter_info,uint bus_type,uint bus_num,uint slot_num)184 void* bcmsdh_probe(osl_t *osh, void *dev, void *sdioh, void *adapter_info, uint bus_type,
185 uint bus_num, uint slot_num)
186 {
187 ulong regs;
188 bcmsdh_info_t *bcmsdh;
189 uint32 vendevid;
190 bcmsdh_os_info_t *bcmsdh_osinfo = NULL;
191
192 bcmsdh = bcmsdh_attach(osh, sdioh, ®s);
193 if (bcmsdh == NULL) {
194 SDLX_ERR(("%s: bcmsdh_attach failed\n", __FUNCTION__));
195 goto err;
196 }
197 bcmsdh_osinfo = MALLOC(osh, sizeof(bcmsdh_os_info_t));
198 if (bcmsdh_osinfo == NULL) {
199 SDLX_ERR(("%s: failed to allocate bcmsdh_os_info_t\n", __FUNCTION__));
200 goto err;
201 }
202 bzero((char *)bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
203 bcmsdh->os_cxt = bcmsdh_osinfo;
204 bcmsdh_osinfo->sdioh = sdioh;
205 bcmsdh_osinfo->dev = dev;
206 osl_set_bus_handle(osh, bcmsdh);
207
208 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
209 if (dev && device_init_wakeup(dev, true) == 0)
210 bcmsdh_osinfo->dev_wake_enabled = TRUE;
211 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
212
213 #if defined(OOB_INTR_ONLY)
214 spin_lock_init(&bcmsdh_osinfo->oob_irq_spinlock);
215 /* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */
216 bcmsdh_osinfo->oob_irq_num = wifi_platform_get_irq_number(adapter_info,
217 &bcmsdh_osinfo->oob_irq_flags);
218 if (bcmsdh_osinfo->oob_irq_num < 0) {
219 SDLX_ERR(("%s: Host OOB irq is not defined\n", __FUNCTION__));
220 goto err;
221 }
222 #endif /* defined(BCMLXSDMMC) */
223
224 /* Read the vendor/device ID from the CIS */
225 vendevid = bcmsdh_query_device(bcmsdh);
226 /* try to attach to the target device */
227 bcmsdh_osinfo->context = drvinfo.probe((vendevid >> 16), (vendevid & 0xFFFF), bus_num,
228 slot_num, 0, bus_type, (void *)regs, osh, bcmsdh);
229 if (bcmsdh_osinfo->context == NULL) {
230 SDLX_ERR(("%s: device attach failed\n", __FUNCTION__));
231 goto err;
232 }
233
234 return bcmsdh;
235
236 /* error handling */
237 err:
238 if (bcmsdh != NULL)
239 bcmsdh_detach(osh, bcmsdh);
240 if (bcmsdh_osinfo != NULL)
241 MFREE(osh, bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
242 return NULL;
243 }
244
bcmsdh_remove(bcmsdh_info_t * bcmsdh)245 int bcmsdh_remove(bcmsdh_info_t *bcmsdh)
246 {
247 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
248
249 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
250 if (bcmsdh_osinfo->dev)
251 device_init_wakeup(bcmsdh_osinfo->dev, false);
252 bcmsdh_osinfo->dev_wake_enabled = FALSE;
253 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
254
255 drvinfo.remove(bcmsdh_osinfo->context);
256 MFREE(bcmsdh->osh, bcmsdh->os_cxt, sizeof(bcmsdh_os_info_t));
257 bcmsdh_detach(bcmsdh->osh, bcmsdh);
258
259 return 0;
260 }
261
262 #ifdef DHD_WAKE_STATUS
bcmsdh_get_total_wake(bcmsdh_info_t * bcmsdh)263 int bcmsdh_get_total_wake(bcmsdh_info_t *bcmsdh)
264 {
265 return bcmsdh->total_wake_count;
266 }
267
bcmsdh_set_get_wake(bcmsdh_info_t * bcmsdh,int flag)268 int bcmsdh_set_get_wake(bcmsdh_info_t *bcmsdh, int flag)
269 {
270 #if defined(OOB_INTR_ONLY)
271 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
272 unsigned long flags;
273 #endif
274 int ret = 0;
275
276 #if defined(OOB_INTR_ONLY)
277 spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
278
279 ret = bcmsdh->pkt_wake;
280 bcmsdh->total_wake_count += flag;
281 bcmsdh->pkt_wake = flag;
282
283 spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
284 #endif
285 return ret;
286 }
287 #endif /* DHD_WAKE_STATUS */
288
bcmsdh_suspend(bcmsdh_info_t * bcmsdh)289 int bcmsdh_suspend(bcmsdh_info_t *bcmsdh)
290 {
291 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
292
293 if (drvinfo.suspend && drvinfo.suspend(bcmsdh_osinfo->context))
294 return -EBUSY;
295 return 0;
296 }
297
bcmsdh_resume(bcmsdh_info_t * bcmsdh)298 int bcmsdh_resume(bcmsdh_info_t *bcmsdh)
299 {
300 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
301
302 if (drvinfo.resume)
303 return drvinfo.resume(bcmsdh_osinfo->context);
304 return 0;
305 }
306
307 extern int bcmsdh_register_client_driver(void);
308 extern void bcmsdh_unregister_client_driver(void);
309 extern int sdio_func_reg_notify(void* semaphore);
310 extern void sdio_func_unreg_notify(void);
311
312 #if defined(BCMLXSDMMC)
bcmsdh_reg_sdio_notify(void * semaphore)313 int bcmsdh_reg_sdio_notify(void* semaphore)
314 {
315 return sdio_func_reg_notify(semaphore);
316 }
317
bcmsdh_unreg_sdio_notify(void)318 void bcmsdh_unreg_sdio_notify(void)
319 {
320 sdio_func_unreg_notify();
321 }
322 #endif /* defined(BCMLXSDMMC) */
323
324 int
bcmsdh_register(bcmsdh_driver_t * driver)325 bcmsdh_register(bcmsdh_driver_t *driver)
326 {
327 int error = 0;
328
329 drvinfo = *driver;
330 SDLX_MSG(("%s: register client driver\n", __FUNCTION__));
331 error = bcmsdh_register_client_driver();
332 if (error)
333 SDLX_ERR(("%s: failed %d\n", __FUNCTION__, error));
334
335 return error;
336 }
337
338 void
bcmsdh_unregister(void)339 bcmsdh_unregister(void)
340 {
341 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
342 if (bcmsdh_pci_driver.node.next == NULL)
343 return;
344 #endif
345
346 bcmsdh_unregister_client_driver();
347 }
348
bcmsdh_get_dev(bcmsdh_info_t * sdh)349 void *bcmsdh_get_dev(bcmsdh_info_t *sdh)
350 {
351 bcmsdh_os_info_t *bcmsdh_osinfo = sdh->os_cxt;
352 return bcmsdh_osinfo->dev;
353 }
354
bcmsdh_dev_pm_stay_awake(bcmsdh_info_t * bcmsdh)355 void bcmsdh_dev_pm_stay_awake(bcmsdh_info_t *bcmsdh)
356 {
357 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
358 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
359 pm_stay_awake(bcmsdh_osinfo->dev);
360 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
361 }
362
bcmsdh_dev_relax(bcmsdh_info_t * bcmsdh)363 void bcmsdh_dev_relax(bcmsdh_info_t *bcmsdh)
364 {
365 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
366 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
367 pm_relax(bcmsdh_osinfo->dev);
368 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
369 }
370
bcmsdh_dev_pm_enabled(bcmsdh_info_t * bcmsdh)371 bool bcmsdh_dev_pm_enabled(bcmsdh_info_t *bcmsdh)
372 {
373 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
374
375 return bcmsdh_osinfo->dev_wake_enabled;
376 }
377
378 #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID)
bcmsdh_get_oob_intr_num(bcmsdh_info_t * bcmsdh)379 int bcmsdh_get_oob_intr_num(bcmsdh_info_t *bcmsdh)
380 {
381 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
382
383 return bcmsdh_osinfo->oob_irq_num;
384 }
385
bcmsdh_oob_intr_set(bcmsdh_info_t * bcmsdh,bool enable)386 void bcmsdh_oob_intr_set(bcmsdh_info_t *bcmsdh, bool enable)
387 {
388 unsigned long flags;
389 bcmsdh_os_info_t *bcmsdh_osinfo;
390
391 if (!bcmsdh)
392 return;
393
394 bcmsdh_osinfo = bcmsdh->os_cxt;
395 spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
396 if (bcmsdh_osinfo->oob_irq_enabled != enable) {
397 if (enable)
398 enable_irq(bcmsdh_osinfo->oob_irq_num);
399 else
400 disable_irq_nosync(bcmsdh_osinfo->oob_irq_num);
401 bcmsdh_osinfo->oob_irq_enabled = enable;
402 }
403 spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
404 }
405
406 #ifdef ENABLE_WAKEUP_PKT_DUMP
407 extern volatile bool dhd_mmc_suspend;
408 extern volatile bool dhd_mmc_wake;
409 #endif /* ENABLE_WAKEUP_PKT_DUMP */
410
wlan_oob_irq(int irq,void * dev_id)411 static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
412 {
413 bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)dev_id;
414 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
415
416 #ifndef BCMSPI_ANDROID
417 bcmsdh_oob_intr_set(bcmsdh, FALSE);
418 #endif /* !BCMSPI_ANDROID */
419 bcmsdh_osinfo->oob_irq_handler(bcmsdh_osinfo->oob_irq_handler_context);
420
421 #ifdef ENABLE_WAKEUP_PKT_DUMP
422 if (dhd_mmc_suspend) {
423 dhd_mmc_wake = TRUE;
424 }
425 #endif /* ENABLE_WAKEUP_PKT_DUMP */
426
427 return IRQ_HANDLED;
428 }
429
bcmsdh_oob_intr_register(bcmsdh_info_t * bcmsdh,bcmsdh_cb_fn_t oob_irq_handler,void * oob_irq_handler_context)430 int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handler,
431 void* oob_irq_handler_context)
432 {
433 int err = 0;
434 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
435
436 if (bcmsdh_osinfo->oob_irq_registered) {
437 SDLX_ERR(("%s: irq is already registered\n", __FUNCTION__));
438 return -EBUSY;
439 }
440 #ifdef HW_OOB
441 SDLX_MSG(("%s: HW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
442 (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
443 #else
444 SDLX_MSG(("%s: SW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
445 (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
446 #endif
447 bcmsdh_osinfo->oob_irq_handler = oob_irq_handler;
448 bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context;
449 bcmsdh_osinfo->oob_irq_enabled = TRUE;
450 bcmsdh_osinfo->oob_irq_registered = TRUE;
451 #if defined(CONFIG_ARCH_ODIN)
452 err = odin_gpio_sms_request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
453 bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
454 #else
455 err = request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
456 bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
457 #endif /* defined(CONFIG_ARCH_ODIN) */
458 if (err) {
459 SDLX_ERR(("%s: request_irq failed with %d\n", __FUNCTION__, err));
460 bcmsdh_osinfo->oob_irq_enabled = FALSE;
461 bcmsdh_osinfo->oob_irq_registered = FALSE;
462 return err;
463 }
464
465 #if defined(DISABLE_WOWLAN)
466 SDLX_MSG(("%s: disable_irq_wake\n", __FUNCTION__));
467 bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
468 #else
469 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
470 if (device_may_wakeup(bcmsdh_osinfo->dev)) {
471 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
472 err = enable_irq_wake(bcmsdh_osinfo->oob_irq_num);
473 if (err)
474 SDLX_ERR(("%s: enable_irq_wake failed with %d\n", __FUNCTION__, err));
475 else
476 bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;
477 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
478 }
479 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
480 #endif
481
482 return 0;
483 }
484
bcmsdh_oob_intr_unregister(bcmsdh_info_t * bcmsdh)485 void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh)
486 {
487 int err = 0;
488 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
489
490 SDLX_MSG(("%s: Enter\n", __FUNCTION__));
491 if (!bcmsdh_osinfo->oob_irq_registered) {
492 SDLX_MSG(("%s: irq is not registered\n", __FUNCTION__));
493 return;
494 }
495 if (bcmsdh_osinfo->oob_irq_wake_enabled) {
496 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
497 if (device_may_wakeup(bcmsdh_osinfo->dev)) {
498 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
499 err = disable_irq_wake(bcmsdh_osinfo->oob_irq_num);
500 if (!err)
501 bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
502 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
503 }
504 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
505 }
506 if (bcmsdh_osinfo->oob_irq_enabled) {
507 disable_irq(bcmsdh_osinfo->oob_irq_num);
508 bcmsdh_osinfo->oob_irq_enabled = FALSE;
509 }
510 free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh);
511 bcmsdh_osinfo->oob_irq_registered = FALSE;
512 }
513 #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */
514
515 /* Module parameters specific to each host-controller driver */
516 /* XXX Need to move these to where they really belong! */
517
518 extern uint sd_msglevel; /* Debug message level */
519 module_param(sd_msglevel, uint, 0);
520
521 extern uint sd_power; /* 0 = SD Power OFF, 1 = SD Power ON. */
522 module_param(sd_power, uint, 0);
523
524 extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
525 module_param(sd_clock, uint, 0);
526
527 extern uint sd_divisor; /* Divisor (-1 means external clock) */
528 module_param(sd_divisor, uint, 0);
529
530 extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
531 module_param(sd_sdmode, uint, 0);
532
533 extern uint sd_hiok; /* Ok to use hi-speed mode */
534 module_param(sd_hiok, uint, 0);
535
536 extern uint sd_f2_blocksize;
537 module_param(sd_f2_blocksize, int, 0);
538
539 extern uint sd_f1_blocksize;
540 module_param(sd_f1_blocksize, int, 0);
541
542 #ifdef BCMSDIOH_STD
543 extern int sd_uhsimode;
544 module_param(sd_uhsimode, int, 0);
545 extern uint sd_tuning_period;
546 module_param(sd_tuning_period, uint, 0);
547 extern int sd_delay_value;
548 module_param(sd_delay_value, uint, 0);
549
550 /* SDIO Drive Strength for UHSI mode specific to SDIO3.0 */
551 extern char dhd_sdiod_uhsi_ds_override[2];
552 module_param_string(dhd_sdiod_uhsi_ds_override, dhd_sdiod_uhsi_ds_override, 2, 0);
553
554 #endif
555
556 #ifdef BCMSDH_MODULE
557 EXPORT_SYMBOL(bcmsdh_attach);
558 EXPORT_SYMBOL(bcmsdh_detach);
559 EXPORT_SYMBOL(bcmsdh_intr_query);
560 EXPORT_SYMBOL(bcmsdh_intr_enable);
561 EXPORT_SYMBOL(bcmsdh_intr_disable);
562 EXPORT_SYMBOL(bcmsdh_intr_reg);
563 EXPORT_SYMBOL(bcmsdh_intr_dereg);
564
565 #if defined(DHD_DEBUG) || defined(BCMDBG)
566 EXPORT_SYMBOL(bcmsdh_intr_pending);
567 #endif
568
569 #if defined (BT_OVER_SDIO)
570 EXPORT_SYMBOL(bcmsdh_btsdio_interface_init);
571 #endif /* defined (BT_OVER_SDIO) */
572
573 EXPORT_SYMBOL(bcmsdh_devremove_reg);
574 EXPORT_SYMBOL(bcmsdh_cfg_read);
575 EXPORT_SYMBOL(bcmsdh_cfg_write);
576 EXPORT_SYMBOL(bcmsdh_cis_read);
577 EXPORT_SYMBOL(bcmsdh_reg_read);
578 EXPORT_SYMBOL(bcmsdh_reg_write);
579 EXPORT_SYMBOL(bcmsdh_regfail);
580 EXPORT_SYMBOL(bcmsdh_send_buf);
581 EXPORT_SYMBOL(bcmsdh_recv_buf);
582
583 EXPORT_SYMBOL(bcmsdh_rwdata);
584 EXPORT_SYMBOL(bcmsdh_abort);
585 EXPORT_SYMBOL(bcmsdh_query_device);
586 EXPORT_SYMBOL(bcmsdh_query_iofnum);
587 EXPORT_SYMBOL(bcmsdh_iovar_op);
588 EXPORT_SYMBOL(bcmsdh_register);
589 EXPORT_SYMBOL(bcmsdh_unregister);
590 EXPORT_SYMBOL(bcmsdh_chipmatch);
591 EXPORT_SYMBOL(bcmsdh_reset);
592 EXPORT_SYMBOL(bcmsdh_waitlockfree);
593
594 EXPORT_SYMBOL(bcmsdh_get_dstatus);
595 EXPORT_SYMBOL(bcmsdh_cfg_read_word);
596 EXPORT_SYMBOL(bcmsdh_cfg_write_word);
597 EXPORT_SYMBOL(bcmsdh_cur_sbwad);
598 EXPORT_SYMBOL(bcmsdh_chipinfo);
599
600 #endif /* BCMSDH_MODULE */
601