• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * SDIO access interface for drivers - linux specific (pci only)
3  *
4  * Copyright (C) 1999-2019, 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
16  * of the license of that module.  An independent module is a module which is
17  * not 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  *
25  * <<Broadcom-WL-IPTag/Open:>>
26  *
27  * $Id: bcmsdh_linux.c 689948 2017-03-14 05:21:03Z $
28  */
29 
30 /**
31  * @file bcmsdh_linux.c
32  */
33 
34 #define __UNDEF_NO_VERSION__
35 
36 #include <typedefs.h>
37 #include <linuxver.h>
38 #include <linux/pci.h>
39 #include <linux/completion.h>
40 
41 #include <osl.h>
42 #include <pcicfg.h>
43 #include <bcmdefs.h>
44 #include <bcmdevs.h>
45 #include <linux/irq.h>
46 extern void dhdsdio_isr(void *args);
47 #include <bcmutils.h>
48 #include <dngl_stats.h>
49 #include <dhd.h>
50 #include <dhd_linux.h>
51 
52 /* driver info, initialized when bcmsdh_register is called */
53 static bcmsdh_driver_t drvinfo = {NULL, NULL, NULL, NULL};
54 
55 typedef enum {
56     DHD_INTR_INVALID = 0,
57     DHD_INTR_INBAND,
58     DHD_INTR_HWOOB,
59     DHD_INTR_SWOOB
60 } DHD_HOST_INTR_TYPE;
61 
62 /* the BCMSDH module comprises the generic part (bcmsdh.c) and OS specific layer
63  * (e.g. bcmsdh_linux.c). Put all OS specific variables (e.g. irq number and
64  * flags) here rather than in the common structure bcmsdh_info. bcmsdh_info only
65  * keeps a handle (os_ctx) to this structure.
66  */
67 typedef struct bcmsdh_os_info {
68     DHD_HOST_INTR_TYPE intr_type;
69     int oob_irq_num; /* valid when hardware or software oob in use */
70     unsigned long
71         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
90  * Controller.
91  */
bcmsdh_chipmatch(uint16 vendor,uint16 device)92 bool 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,
139                    uint bus_type, 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, &regs);
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) &&                                           \
163     (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
164     if (dev && device_init_wakeup(dev, true) == 0) {
165         bcmsdh_osinfo->dev_wake_enabled = TRUE;
166     }
167 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >               \
168           KERNEL_VERSION(2, 6, 36)) */
169 
170 #if defined(OOB_INTR_ONLY)
171     spin_lock_init(&bcmsdh_osinfo->oob_irq_spinlock);
172     /* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */
173     bcmsdh_osinfo->oob_irq_num = wifi_platform_get_irq_number(
174         adapter_info, &bcmsdh_osinfo->oob_irq_flags);
175     if (bcmsdh_osinfo->oob_irq_num < 0) {
176         SDLX_ERR(("%s: Host OOB irq is not defined\n", __FUNCTION__));
177         goto err;
178     }
179 #endif /* defined(BCMLXSDMMC) */
180 
181     /* Read the vendor/device ID from the CIS */
182     vendevid = bcmsdh_query_device(bcmsdh);
183     /* try to attach to the target device */
184     bcmsdh_osinfo->context =
185         drvinfo.probe((vendevid >> 0x10), (vendevid & 0xFFFF), bus_num, slot_num,
186                       0, bus_type, (void *)regs, osh, bcmsdh);
187     if (bcmsdh_osinfo->context == NULL) {
188         SDLX_ERR(("%s: device attach failed\n", __FUNCTION__));
189         goto err;
190     }
191 
192     return bcmsdh;
193 
194     /* error handling */
195 err:
196     if (bcmsdh != NULL) {
197         bcmsdh_detach(osh, bcmsdh);
198     }
199     if (bcmsdh_osinfo != NULL) {
200         MFREE(osh, bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
201     }
202     return NULL;
203 }
204 
bcmsdh_remove(bcmsdh_info_t * bcmsdh)205 int bcmsdh_remove(bcmsdh_info_t *bcmsdh)
206 {
207     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
208 
209 #if !defined(CONFIG_HAS_WAKELOCK) &&                                           \
210     (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
211     if (bcmsdh_osinfo->dev) {
212         device_init_wakeup(bcmsdh_osinfo->dev, false);
213     }
214     bcmsdh_osinfo->dev_wake_enabled = FALSE;
215 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >               \
216           KERNEL_VERSION(2, 6, 36)) */
217 
218     drvinfo.remove(bcmsdh_osinfo->context);
219     MFREE(bcmsdh->osh, bcmsdh->os_cxt, sizeof(bcmsdh_os_info_t));
220     bcmsdh_detach(bcmsdh->osh, bcmsdh);
221 
222     return 0;
223 }
224 
225 #ifdef DHD_WAKE_STATUS
bcmsdh_get_total_wake(bcmsdh_info_t * bcmsdh)226 int bcmsdh_get_total_wake(bcmsdh_info_t *bcmsdh)
227 {
228     return bcmsdh->total_wake_count;
229 }
230 
bcmsdh_set_get_wake(bcmsdh_info_t * bcmsdh,int flag)231 int bcmsdh_set_get_wake(bcmsdh_info_t *bcmsdh, int flag)
232 {
233 #if defined(OOB_INTR_ONLY)
234     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
235     unsigned long flags;
236 #endif
237     int ret = 0;
238 
239 #if defined(OOB_INTR_ONLY)
240     spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
241 
242     ret = bcmsdh->pkt_wake;
243     bcmsdh->total_wake_count += flag;
244     bcmsdh->pkt_wake = flag;
245 
246     spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
247 #endif
248     return ret;
249 }
250 #endif /* DHD_WAKE_STATUS */
251 
bcmsdh_suspend(bcmsdh_info_t * bcmsdh)252 int bcmsdh_suspend(bcmsdh_info_t *bcmsdh)
253 {
254     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
255 
256     if (drvinfo.suspend && drvinfo.suspend(bcmsdh_osinfo->context)) {
257         return -EBUSY;
258     }
259     return 0;
260 }
261 
bcmsdh_resume(bcmsdh_info_t * bcmsdh)262 int bcmsdh_resume(bcmsdh_info_t *bcmsdh)
263 {
264     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
265 
266     if (drvinfo.resume) {
267         return drvinfo.resume(bcmsdh_osinfo->context);
268     }
269     return 0;
270 }
271 
272 extern int bcmsdh_register_client_driver(void);
273 extern void bcmsdh_unregister_client_driver(void);
274 extern int sdio_func_reg_notify(void *semaphore);
275 extern void sdio_func_unreg_notify(void);
276 
277 #if defined(BCMLXSDMMC)
bcmsdh_reg_sdio_notify(void * semaphore)278 int bcmsdh_reg_sdio_notify(void *semaphore)
279 {
280     return sdio_func_reg_notify(semaphore);
281 }
282 
bcmsdh_unreg_sdio_notify(void)283 void bcmsdh_unreg_sdio_notify(void)
284 {
285     sdio_func_unreg_notify();
286 }
287 #endif /* defined(BCMLXSDMMC) */
288 
bcmsdh_register(bcmsdh_driver_t * driver)289 int bcmsdh_register(bcmsdh_driver_t *driver)
290 {
291     int error = 0;
292 
293     drvinfo = *driver;
294     SDLX_MSG(("%s: register client driver\n", __FUNCTION__));
295     error = bcmsdh_register_client_driver();
296     if (error) {
297         SDLX_ERR(("%s: failed %d\n", __FUNCTION__, error));
298     }
299 
300     return error;
301 }
302 
bcmsdh_unregister(void)303 void bcmsdh_unregister(void)
304 {
305 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
306     if (bcmsdh_pci_driver.node.next == NULL) {
307         return;
308     }
309 #endif // endif
310 
311     bcmsdh_unregister_client_driver();
312 }
313 
bcmsdh_dev_pm_stay_awake(bcmsdh_info_t * bcmsdh)314 void bcmsdh_dev_pm_stay_awake(bcmsdh_info_t *bcmsdh)
315 {
316 #if !defined(CONFIG_HAS_WAKELOCK) &&                                           \
317     (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
318     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
319     pm_stay_awake(bcmsdh_osinfo->dev);
320 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >               \
321           KERNEL_VERSION(2, 6, 36)) */
322 }
323 
bcmsdh_dev_relax(bcmsdh_info_t * bcmsdh)324 void bcmsdh_dev_relax(bcmsdh_info_t *bcmsdh)
325 {
326 #if !defined(CONFIG_HAS_WAKELOCK) &&                                           \
327     (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
328     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
329     pm_relax(bcmsdh_osinfo->dev);
330 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >               \
331           KERNEL_VERSION(2, 6, 36)) */
332 }
333 
bcmsdh_dev_pm_enabled(bcmsdh_info_t * bcmsdh)334 bool bcmsdh_dev_pm_enabled(bcmsdh_info_t *bcmsdh)
335 {
336     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
337 
338     return bcmsdh_osinfo->dev_wake_enabled;
339 }
340 
341 #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID)
bcmsdh_oob_intr_set(bcmsdh_info_t * bcmsdh,bool enable)342 void bcmsdh_oob_intr_set(bcmsdh_info_t *bcmsdh, bool enable)
343 {
344     unsigned long flags;
345     bcmsdh_os_info_t *bcmsdh_osinfo;
346 
347     if (!bcmsdh) {
348         return;
349     }
350 
351     bcmsdh_osinfo = bcmsdh->os_cxt;
352     spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
353     if (bcmsdh_osinfo->oob_irq_enabled != enable) {
354         if (enable) {
355             enable_irq(bcmsdh_osinfo->oob_irq_num);
356         } else {
357             disable_irq_nosync(bcmsdh_osinfo->oob_irq_num);
358         }
359         bcmsdh_osinfo->oob_irq_enabled = enable;
360     }
361     spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
362 }
363 
wlan_oob_irq(int irq,void * dev_id)364 static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
365 {
366     bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)dev_id;
367     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
368 
369 #ifndef BCMSPI_ANDROID
370     bcmsdh_oob_intr_set(bcmsdh, FALSE);
371 #endif /* !BCMSPI_ANDROID */
372     bcmsdh_osinfo->oob_irq_handler(bcmsdh_osinfo->oob_irq_handler_context);
373 
374     return IRQ_HANDLED;
375 }
376 
bcmsdh_oob_intr_register(bcmsdh_info_t * bcmsdh,bcmsdh_cb_fn_t oob_irq_handler,void * oob_irq_handler_context)377 int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh,
378                              bcmsdh_cb_fn_t oob_irq_handler,
379                              void *oob_irq_handler_context)
380 {
381     int err = 0;
382     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
383 
384     if (bcmsdh_osinfo->oob_irq_registered) {
385         SDLX_ERR(("%s: irq is already registered\n", __FUNCTION__));
386         return -EBUSY;
387     }
388 #ifdef HW_OOB
389     SDLX_MSG(("%s: HW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
390               (int)bcmsdh_osinfo->oob_irq_num,
391               (int)bcmsdh_osinfo->oob_irq_flags));
392 #else
393     SDLX_MSG(("%s: SW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
394               (int)bcmsdh_osinfo->oob_irq_num,
395               (int)bcmsdh_osinfo->oob_irq_flags));
396 #endif
397     bcmsdh_osinfo->oob_irq_handler = oob_irq_handler;
398     bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context;
399     bcmsdh_osinfo->oob_irq_enabled = TRUE;
400     bcmsdh_osinfo->oob_irq_registered = TRUE;
401     err = request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
402                       bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
403     if (err) {
404         SDLX_ERR(("%s: request_irq failed with %d\n", __FUNCTION__, err));
405         bcmsdh_osinfo->oob_irq_enabled = FALSE;
406         bcmsdh_osinfo->oob_irq_registered = FALSE;
407         return err;
408     }
409 
410 #if defined(DISABLE_WOWLAN)
411     SDLX_MSG(("%s: disable_irq_wake\n", __FUNCTION__));
412     bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
413 #else
414 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
415     if (device_may_wakeup(bcmsdh_osinfo->dev)) {
416 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
417         err = enable_irq_wake(bcmsdh_osinfo->oob_irq_num);
418         if (err) {
419             SDLX_ERR(
420                 ("%s: enable_irq_wake failed with %d\n", __FUNCTION__, err));
421         } else {
422             bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;
423         }
424 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
425     }
426 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
427 #endif
428 
429     return 0;
430 }
431 
bcmsdh_oob_intr_unregister(bcmsdh_info_t * bcmsdh)432 void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh)
433 {
434     int err = 0;
435     bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
436 
437     SDLX_MSG(("%s: Enter\n", __FUNCTION__));
438     if (!bcmsdh_osinfo->oob_irq_registered) {
439         SDLX_MSG(("%s: irq is not registered\n", __FUNCTION__));
440         return;
441     }
442     if (bcmsdh_osinfo->oob_irq_wake_enabled) {
443 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
444         if (device_may_wakeup(bcmsdh_osinfo->dev)) {
445 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
446             err = disable_irq_wake(bcmsdh_osinfo->oob_irq_num);
447             if (!err) {
448                 bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
449             }
450 #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
451         }
452 #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
453     }
454     if (bcmsdh_osinfo->oob_irq_enabled) {
455         disable_irq(bcmsdh_osinfo->oob_irq_num);
456         bcmsdh_osinfo->oob_irq_enabled = FALSE;
457     }
458     free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh);
459     bcmsdh_osinfo->oob_irq_registered = FALSE;
460 }
461 #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */
462 
463 /* Module parameters specific to each host-controller driver */
464 
465 extern uint sd_msglevel; /* Debug message level */
466 module_param(sd_msglevel, uint, 0);
467 
468 extern uint sd_power; /* 0 = SD Power OFF, 1 = SD Power ON. */
469 module_param(sd_power, uint, 0);
470 
471 extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
472 module_param(sd_clock, uint, 0);
473 
474 extern uint sd_divisor; /* Divisor (-1 means external clock) */
475 module_param(sd_divisor, uint, 0);
476 
477 extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
478 module_param(sd_sdmode, uint, 0);
479 
480 extern uint sd_hiok; /* Ok to use hi-speed mode */
481 module_param(sd_hiok, uint, 0);
482 
483 extern uint sd_f2_blocksize;
484 module_param(sd_f2_blocksize, int, 0);
485 
486 extern uint sd_f1_blocksize;
487 module_param(sd_f1_blocksize, int, 0);
488 
489 #ifdef BCMSDIOH_STD
490 extern int sd_uhsimode;
491 module_param(sd_uhsimode, int, 0);
492 extern uint sd_tuning_period;
493 module_param(sd_tuning_period, uint, 0);
494 extern int sd_delay_value;
495 module_param(sd_delay_value, uint, 0);
496 
497 /* SDIO Drive Strength for UHSI mode specific to SDIO3.0 */
498 extern char dhd_sdiod_uhsi_ds_override[2];
499 module_param_string(dhd_sdiod_uhsi_ds_override, dhd_sdiod_uhsi_ds_override, 2,
500                     0);
501 
502 #endif // endif
503 
504 #ifdef BCMSDH_MODULE
505 EXPORT_SYMBOL(bcmsdh_attach);
506 EXPORT_SYMBOL(bcmsdh_detach);
507 EXPORT_SYMBOL(bcmsdh_intr_query);
508 EXPORT_SYMBOL(bcmsdh_intr_enable);
509 EXPORT_SYMBOL(bcmsdh_intr_disable);
510 EXPORT_SYMBOL(bcmsdh_intr_reg);
511 EXPORT_SYMBOL(bcmsdh_intr_dereg);
512 
513 #if defined(DHD_DEBUG)
514 EXPORT_SYMBOL(bcmsdh_intr_pending);
515 #endif // endif
516 
517 #if defined(BT_OVER_SDIO)
518 EXPORT_SYMBOL(bcmsdh_btsdio_interface_init);
519 #endif /* defined (BT_OVER_SDIO) */
520 
521 EXPORT_SYMBOL(bcmsdh_devremove_reg);
522 EXPORT_SYMBOL(bcmsdh_cfg_read);
523 EXPORT_SYMBOL(bcmsdh_cfg_write);
524 EXPORT_SYMBOL(bcmsdh_cis_read);
525 EXPORT_SYMBOL(bcmsdh_reg_read);
526 EXPORT_SYMBOL(bcmsdh_reg_write);
527 EXPORT_SYMBOL(bcmsdh_regfail);
528 EXPORT_SYMBOL(bcmsdh_send_buf);
529 EXPORT_SYMBOL(bcmsdh_recv_buf);
530 
531 EXPORT_SYMBOL(bcmsdh_rwdata);
532 EXPORT_SYMBOL(bcmsdh_abort);
533 EXPORT_SYMBOL(bcmsdh_query_device);
534 EXPORT_SYMBOL(bcmsdh_query_iofnum);
535 EXPORT_SYMBOL(bcmsdh_iovar_op);
536 EXPORT_SYMBOL(bcmsdh_register);
537 EXPORT_SYMBOL(bcmsdh_unregister);
538 EXPORT_SYMBOL(bcmsdh_chipmatch);
539 EXPORT_SYMBOL(bcmsdh_reset);
540 EXPORT_SYMBOL(bcmsdh_waitlockfree);
541 
542 EXPORT_SYMBOL(bcmsdh_get_dstatus);
543 EXPORT_SYMBOL(bcmsdh_cfg_read_word);
544 EXPORT_SYMBOL(bcmsdh_cfg_write_word);
545 EXPORT_SYMBOL(bcmsdh_cur_sbwad);
546 EXPORT_SYMBOL(bcmsdh_chipinfo);
547 
548 #endif /* BCMSDH_MODULE */
549