1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * SDIO access interface for drivers - linux specific (pci only)
4 *
5 * Copyright (C) 1999-2019, Broadcom.
6 *
7 * Unless you and Broadcom execute a separate written software license
8 * agreement governing use of this software, this software is licensed to you
9 * under the terms of the GNU General Public License version 2 (the "GPL"),
10 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
11 * following added to such license:
12 *
13 * As a special exception, the copyright holders of this software give you
14 * permission to link this software with independent modules, and to copy and
15 * distribute the resulting executable under terms of your choice, provided that
16 * you also meet, for each linked independent module, the terms and conditions of
17 * the license of that module. An independent module is a module which is not
18 * derived from this software. The special exception does not apply to any
19 * modifications of the software.
20 *
21 * Notwithstanding the above, under no circumstances may you combine this
22 * software in any way with any other Broadcom software provided under a license
23 * other than the GPL, without Broadcom's express prior written consent.
24 *
25 *
26 * <<Broadcom-WL-IPTag/Open:>>
27 *
28 * $Id: bcmsdh_linux.c 689948 2017-03-14 05:21:03Z $
29 */
30
31 /**
32 * @file bcmsdh_linux.c
33 */
34
35 #define __UNDEF_NO_VERSION__
36
37 #include <typedefs.h>
38 #include <linuxver.h>
39 #include <linux/pci.h>
40 #include <linux/completion.h>
41
42 #include <osl.h>
43 #include <pcicfg.h>
44 #include <bcmdefs.h>
45 #include <bcmdevs.h>
46 #include <linux/irq.h>
47 extern void dhdsdio_isr(void * args);
48 #include <bcmutils.h>
49 #include <dngl_stats.h>
50 #include <dhd.h>
51 #include <dhd_linux.h>
52
53 /* driver info, initialized when bcmsdh_register is called */
54 static bcmsdh_driver_t drvinfo = {NULL, NULL, NULL, NULL};
55
56 typedef enum {
57 DHD_INTR_INVALID = 0,
58 DHD_INTR_INBAND,
59 DHD_INTR_HWOOB,
60 DHD_INTR_SWOOB
61 } DHD_HOST_INTR_TYPE;
62
63 /* the BCMSDH module comprises the generic part (bcmsdh.c) and OS specific layer (e.g.
64 * bcmsdh_linux.c). Put all OS specific variables (e.g. irq number and flags) here rather
65 * than in the common structure bcmsdh_info. bcmsdh_info only keeps a handle (os_ctx) to this
66 * structure.
67 */
68 typedef struct bcmsdh_os_info {
69 DHD_HOST_INTR_TYPE intr_type;
70 int oob_irq_num; /* valid when hardware or software oob in use */
71 unsigned long oob_irq_flags; /* valid when hardware or software oob in use */
72 bool oob_irq_registered;
73 bool oob_irq_enabled;
74 bool oob_irq_wake_enabled;
75 spinlock_t oob_irq_spinlock;
76 bcmsdh_cb_fn_t oob_irq_handler;
77 void *oob_irq_handler_context;
78 void *context; /* context returned from upper layer */
79 void *sdioh; /* handle to lower layer (sdioh) */
80 void *dev; /* handle to the underlying device */
81 bool dev_wake_enabled;
82 } bcmsdh_os_info_t;
83
84 /* debugging macros */
85 #define SDLX_ERR(x) printf x
86 #define SDLX_MSG(x) printf x
87
88 /**
89 * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
90 */
91 bool
bcmsdh_chipmatch(uint16 vendor,uint16 device)92 bcmsdh_chipmatch(uint16 vendor, uint16 device)
93 {
94 /* Add other vendors and devices as required */
95
96 #ifdef BCMSDIOH_STD
97 /* Check for Arasan host controller */
98 if (vendor == VENDOR_SI_IMAGE) {
99 return (TRUE);
100 }
101 /* Check for BRCM 27XX Standard host controller */
102 if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) {
103 return (TRUE);
104 }
105 /* Check for BRCM Standard host controller */
106 if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
107 return (TRUE);
108 }
109 /* Check for TI PCIxx21 Standard host controller */
110 if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) {
111 return (TRUE);
112 }
113 if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) {
114 return (TRUE);
115 }
116 /* Ricoh R5C822 Standard SDIO Host */
117 if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) {
118 return (TRUE);
119 }
120 /* JMicron Standard SDIO Host */
121 if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) {
122 return (TRUE);
123 }
124
125 #endif /* BCMSDIOH_STD */
126 #ifdef BCMSDIOH_SPI
127 /* This is the PciSpiHost. */
128 if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
129 printf("Found PCI SPI Host Controller\n");
130 return (TRUE);
131 }
132
133 #endif /* BCMSDIOH_SPI */
134
135 return (FALSE);
136 }
137
bcmsdh_probe(osl_t * osh,void * dev,void * sdioh,void * adapter_info,uint bus_type,uint bus_num,uint slot_num)138 void* bcmsdh_probe(osl_t *osh, void *dev, void *sdioh, void *adapter_info, uint bus_type,
139 uint bus_num, uint slot_num)
140 {
141 ulong regs;
142 bcmsdh_info_t *bcmsdh;
143 uint32 vendevid;
144 bcmsdh_os_info_t *bcmsdh_osinfo = NULL;
145
146 bcmsdh = bcmsdh_attach(osh, sdioh, ®s);
147 if (bcmsdh == NULL) {
148 SDLX_ERR(("%s: bcmsdh_attach failed\n", __FUNCTION__));
149 goto err;
150 }
151 bcmsdh_osinfo = MALLOC(osh, sizeof(bcmsdh_os_info_t));
152 if (bcmsdh_osinfo == NULL) {
153 SDLX_ERR(("%s: failed to allocate bcmsdh_os_info_t\n", __FUNCTION__));
154 goto err;
155 }
156 bzero((char *)bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
157 bcmsdh->os_cxt = bcmsdh_osinfo;
158 bcmsdh_osinfo->sdioh = sdioh;
159 bcmsdh_osinfo->dev = dev;
160 osl_set_bus_handle(osh, bcmsdh);
161
162 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
163 if (dev && device_init_wakeup(dev, true) == 0)
164 bcmsdh_osinfo->dev_wake_enabled = TRUE;
165 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
166
167 #if defined(OOB_INTR_ONLY)
168 spin_lock_init(&bcmsdh_osinfo->oob_irq_spinlock);
169 /* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */
170 bcmsdh_osinfo->oob_irq_num = wifi_platform_get_irq_number(adapter_info,
171 &bcmsdh_osinfo->oob_irq_flags);
172 if (bcmsdh_osinfo->oob_irq_num < 0) {
173 SDLX_ERR(("%s: Host OOB irq is not defined\n", __FUNCTION__));
174 goto err;
175 }
176 #endif /* defined(BCMLXSDMMC) */
177
178 /* Read the vendor/device ID from the CIS */
179 vendevid = bcmsdh_query_device(bcmsdh);
180 /* try to attach to the target device */
181 bcmsdh_osinfo->context = drvinfo.probe((vendevid >> 16), (vendevid & 0xFFFF), bus_num,
182 slot_num, 0, bus_type, (void *)regs, osh, bcmsdh);
183 if (bcmsdh_osinfo->context == NULL) {
184 SDLX_ERR(("%s: device attach failed\n", __FUNCTION__));
185 goto err;
186 }
187
188 return bcmsdh;
189
190 /* error handling */
191 err:
192 if (bcmsdh != NULL)
193 bcmsdh_detach(osh, bcmsdh);
194 if (bcmsdh_osinfo != NULL)
195 MFREE(osh, bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
196 return NULL;
197 }
198
bcmsdh_remove(bcmsdh_info_t * bcmsdh)199 int bcmsdh_remove(bcmsdh_info_t *bcmsdh)
200 {
201 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
202
203 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
204 if (bcmsdh_osinfo->dev)
205 device_init_wakeup(bcmsdh_osinfo->dev, false);
206 bcmsdh_osinfo->dev_wake_enabled = FALSE;
207 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
208
209 drvinfo.remove(bcmsdh_osinfo->context);
210 MFREE(bcmsdh->osh, bcmsdh->os_cxt, sizeof(bcmsdh_os_info_t));
211 bcmsdh_detach(bcmsdh->osh, bcmsdh);
212
213 return 0;
214 }
215
216 #ifdef DHD_WAKE_STATUS
bcmsdh_get_total_wake(bcmsdh_info_t * bcmsdh)217 int bcmsdh_get_total_wake(bcmsdh_info_t *bcmsdh)
218 {
219 return bcmsdh->total_wake_count;
220 }
221
bcmsdh_set_get_wake(bcmsdh_info_t * bcmsdh,int flag)222 int bcmsdh_set_get_wake(bcmsdh_info_t *bcmsdh, int flag)
223 {
224 #if defined(OOB_INTR_ONLY)
225 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
226 unsigned long flags;
227 #endif
228 int ret = 0;
229
230 #if defined(OOB_INTR_ONLY)
231 spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
232
233 ret = bcmsdh->pkt_wake;
234 bcmsdh->total_wake_count += flag;
235 bcmsdh->pkt_wake = flag;
236
237 spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
238 #endif
239 return ret;
240 }
241 #endif /* DHD_WAKE_STATUS */
242
bcmsdh_suspend(bcmsdh_info_t * bcmsdh)243 int bcmsdh_suspend(bcmsdh_info_t *bcmsdh)
244 {
245 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
246
247 if (drvinfo.suspend && drvinfo.suspend(bcmsdh_osinfo->context))
248 return -EBUSY;
249 return 0;
250 }
251
bcmsdh_resume(bcmsdh_info_t * bcmsdh)252 int bcmsdh_resume(bcmsdh_info_t *bcmsdh)
253 {
254 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
255
256 if (drvinfo.resume)
257 return drvinfo.resume(bcmsdh_osinfo->context);
258 return 0;
259 }
260
261 extern int bcmsdh_register_client_driver(void);
262 extern void bcmsdh_unregister_client_driver(void);
263 extern int sdio_func_reg_notify(void* semaphore);
264 extern void sdio_func_unreg_notify(void);
265
266 #if defined(BCMLXSDMMC)
bcmsdh_reg_sdio_notify(void * semaphore)267 int bcmsdh_reg_sdio_notify(void* semaphore)
268 {
269 return sdio_func_reg_notify(semaphore);
270 }
271
bcmsdh_unreg_sdio_notify(void)272 void bcmsdh_unreg_sdio_notify(void)
273 {
274 sdio_func_unreg_notify();
275 }
276 #endif /* defined(BCMLXSDMMC) */
277
278 int
bcmsdh_register(bcmsdh_driver_t * driver)279 bcmsdh_register(bcmsdh_driver_t *driver)
280 {
281 int error = 0;
282
283 drvinfo = *driver;
284 SDLX_MSG(("%s: register client driver\n", __FUNCTION__));
285 error = bcmsdh_register_client_driver();
286 if (error)
287 SDLX_ERR(("%s: failed %d\n", __FUNCTION__, error));
288
289 return error;
290 }
291
292 void
bcmsdh_unregister(void)293 bcmsdh_unregister(void)
294 {
295 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
296 if (bcmsdh_pci_driver.node.next == NULL)
297 return;
298 #endif // endif
299
300 bcmsdh_unregister_client_driver();
301 }
302
bcmsdh_dev_pm_stay_awake(bcmsdh_info_t * bcmsdh)303 void bcmsdh_dev_pm_stay_awake(bcmsdh_info_t *bcmsdh)
304 {
305 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
306 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
307 pm_stay_awake(bcmsdh_osinfo->dev);
308 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
309 }
310
bcmsdh_dev_relax(bcmsdh_info_t * bcmsdh)311 void bcmsdh_dev_relax(bcmsdh_info_t *bcmsdh)
312 {
313 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
314 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
315 pm_relax(bcmsdh_osinfo->dev);
316 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
317 }
318
bcmsdh_dev_pm_enabled(bcmsdh_info_t * bcmsdh)319 bool bcmsdh_dev_pm_enabled(bcmsdh_info_t *bcmsdh)
320 {
321 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
322
323 return bcmsdh_osinfo->dev_wake_enabled;
324 }
325
326 #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID)
bcmsdh_oob_intr_set(bcmsdh_info_t * bcmsdh,bool enable)327 void bcmsdh_oob_intr_set(bcmsdh_info_t *bcmsdh, bool enable)
328 {
329 unsigned long flags;
330 bcmsdh_os_info_t *bcmsdh_osinfo;
331
332 if (!bcmsdh)
333 return;
334
335 bcmsdh_osinfo = bcmsdh->os_cxt;
336 spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
337 if (bcmsdh_osinfo->oob_irq_enabled != enable) {
338 if (enable)
339 enable_irq(bcmsdh_osinfo->oob_irq_num);
340 else
341 disable_irq_nosync(bcmsdh_osinfo->oob_irq_num);
342 bcmsdh_osinfo->oob_irq_enabled = enable;
343 }
344 spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
345 }
346
wlan_oob_irq(int irq,void * dev_id)347 static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
348 {
349 bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)dev_id;
350 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
351
352 #ifndef BCMSPI_ANDROID
353 bcmsdh_oob_intr_set(bcmsdh, FALSE);
354 #endif /* !BCMSPI_ANDROID */
355 bcmsdh_osinfo->oob_irq_handler(bcmsdh_osinfo->oob_irq_handler_context);
356
357 return IRQ_HANDLED;
358 }
359
bcmsdh_oob_intr_register(bcmsdh_info_t * bcmsdh,bcmsdh_cb_fn_t oob_irq_handler,void * oob_irq_handler_context)360 int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handler,
361 void* oob_irq_handler_context)
362 {
363 int err = 0;
364 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
365
366 if (bcmsdh_osinfo->oob_irq_registered) {
367 SDLX_ERR(("%s: irq is already registered\n", __FUNCTION__));
368 return -EBUSY;
369 }
370 #ifdef HW_OOB
371 SDLX_MSG(("%s: HW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
372 (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
373 #else
374 SDLX_MSG(("%s: SW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
375 (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
376 #endif
377 bcmsdh_osinfo->oob_irq_handler = oob_irq_handler;
378 bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context;
379 bcmsdh_osinfo->oob_irq_enabled = TRUE;
380 bcmsdh_osinfo->oob_irq_registered = TRUE;
381 err = request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
382 bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
383 if (err) {
384 SDLX_ERR(("%s: request_irq failed with %d\n", __FUNCTION__, err));
385 bcmsdh_osinfo->oob_irq_enabled = FALSE;
386 bcmsdh_osinfo->oob_irq_registered = FALSE;
387 return err;
388 }
389
390 #if defined(DISABLE_WOWLAN)
391 SDLX_MSG(("%s: disable_irq_wake\n", __FUNCTION__));
392 bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
393 #else
394 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
395 if (device_may_wakeup(bcmsdh_osinfo->dev)) {
396 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
397 err = enable_irq_wake(bcmsdh_osinfo->oob_irq_num);
398 if (err)
399 SDLX_ERR(("%s: enable_irq_wake failed with %d\n", __FUNCTION__, err));
400 else
401 bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;
402 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
403 }
404 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
405 #endif
406
407 return 0;
408 }
409
bcmsdh_oob_intr_unregister(bcmsdh_info_t * bcmsdh)410 void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh)
411 {
412 int err = 0;
413 bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
414
415 SDLX_MSG(("%s: Enter\n", __FUNCTION__));
416 if (!bcmsdh_osinfo->oob_irq_registered) {
417 SDLX_MSG(("%s: irq is not registered\n", __FUNCTION__));
418 return;
419 }
420 if (bcmsdh_osinfo->oob_irq_wake_enabled) {
421 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
422 if (device_may_wakeup(bcmsdh_osinfo->dev)) {
423 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
424 err = disable_irq_wake(bcmsdh_osinfo->oob_irq_num);
425 if (!err)
426 bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
427 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
428 }
429 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
430 }
431 if (bcmsdh_osinfo->oob_irq_enabled) {
432 disable_irq(bcmsdh_osinfo->oob_irq_num);
433 bcmsdh_osinfo->oob_irq_enabled = FALSE;
434 }
435 free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh);
436 bcmsdh_osinfo->oob_irq_registered = FALSE;
437 }
438 #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */
439
440 /* Module parameters specific to each host-controller driver */
441
442 extern uint sd_msglevel; /* Debug message level */
443 module_param(sd_msglevel, uint, 0);
444
445 extern uint sd_power; /* 0 = SD Power OFF, 1 = SD Power ON. */
446 module_param(sd_power, uint, 0);
447
448 extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
449 module_param(sd_clock, uint, 0);
450
451 extern uint sd_divisor; /* Divisor (-1 means external clock) */
452 module_param(sd_divisor, uint, 0);
453
454 extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
455 module_param(sd_sdmode, uint, 0);
456
457 extern uint sd_hiok; /* Ok to use hi-speed mode */
458 module_param(sd_hiok, uint, 0);
459
460 extern uint sd_f2_blocksize;
461 module_param(sd_f2_blocksize, int, 0);
462
463 extern uint sd_f1_blocksize;
464 module_param(sd_f1_blocksize, int, 0);
465
466 #ifdef BCMSDIOH_STD
467 extern int sd_uhsimode;
468 module_param(sd_uhsimode, int, 0);
469 extern uint sd_tuning_period;
470 module_param(sd_tuning_period, uint, 0);
471 extern int sd_delay_value;
472 module_param(sd_delay_value, uint, 0);
473
474 /* SDIO Drive Strength for UHSI mode specific to SDIO3.0 */
475 extern char dhd_sdiod_uhsi_ds_override[2];
476 module_param_string(dhd_sdiod_uhsi_ds_override, dhd_sdiod_uhsi_ds_override, 2, 0);
477
478 #endif // endif
479
480 #ifdef BCMSDH_MODULE
481 EXPORT_SYMBOL(bcmsdh_attach);
482 EXPORT_SYMBOL(bcmsdh_detach);
483 EXPORT_SYMBOL(bcmsdh_intr_query);
484 EXPORT_SYMBOL(bcmsdh_intr_enable);
485 EXPORT_SYMBOL(bcmsdh_intr_disable);
486 EXPORT_SYMBOL(bcmsdh_intr_reg);
487 EXPORT_SYMBOL(bcmsdh_intr_dereg);
488
489 #if defined(DHD_DEBUG)
490 EXPORT_SYMBOL(bcmsdh_intr_pending);
491 #endif // endif
492
493 #if defined(BT_OVER_SDIO)
494 EXPORT_SYMBOL(bcmsdh_btsdio_interface_init);
495 #endif /* defined (BT_OVER_SDIO) */
496
497 EXPORT_SYMBOL(bcmsdh_devremove_reg);
498 EXPORT_SYMBOL(bcmsdh_cfg_read);
499 EXPORT_SYMBOL(bcmsdh_cfg_write);
500 EXPORT_SYMBOL(bcmsdh_cis_read);
501 EXPORT_SYMBOL(bcmsdh_reg_read);
502 EXPORT_SYMBOL(bcmsdh_reg_write);
503 EXPORT_SYMBOL(bcmsdh_regfail);
504 EXPORT_SYMBOL(bcmsdh_send_buf);
505 EXPORT_SYMBOL(bcmsdh_recv_buf);
506
507 EXPORT_SYMBOL(bcmsdh_rwdata);
508 EXPORT_SYMBOL(bcmsdh_abort);
509 EXPORT_SYMBOL(bcmsdh_query_device);
510 EXPORT_SYMBOL(bcmsdh_query_iofnum);
511 EXPORT_SYMBOL(bcmsdh_iovar_op);
512 EXPORT_SYMBOL(bcmsdh_register);
513 EXPORT_SYMBOL(bcmsdh_unregister);
514 EXPORT_SYMBOL(bcmsdh_chipmatch);
515 EXPORT_SYMBOL(bcmsdh_reset);
516 EXPORT_SYMBOL(bcmsdh_waitlockfree);
517
518 EXPORT_SYMBOL(bcmsdh_get_dstatus);
519 EXPORT_SYMBOL(bcmsdh_cfg_read_word);
520 EXPORT_SYMBOL(bcmsdh_cfg_write_word);
521 EXPORT_SYMBOL(bcmsdh_cur_sbwad);
522 EXPORT_SYMBOL(bcmsdh_chipinfo);
523
524 #endif /* BCMSDH_MODULE */
525