1/*
2 * SDIO access interface for drivers - linux specific (pci only)
3 *
4 * Copyright (C) 1999-2012, 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 309796 2012-01-20 18:21:51Z $
25 */
26
27/**
28 * @file bcmsdh_linux.c
29 */
30
31#define __UNDEF_NO_VERSION__
32
33#include <typedefs.h>
34#include <linuxver.h>
35
36#include <linux/pci.h>
37#include <linux/completion.h>
38
39#include <osl.h>
40#include <pcicfg.h>
41#include <bcmdefs.h>
42#include <bcmdevs.h>
43
44#if defined(OOB_INTR_ONLY)
45#include <linux/irq.h>
46extern void dhdsdio_isr(void * args);
47#include <bcmutils.h>
48#include <dngl_stats.h>
49#include <dhd.h>
50#endif /* defined(OOB_INTR_ONLY) */
51
52/**
53 * SDIO Host Controller info
54 */
55typedef struct bcmsdh_hc bcmsdh_hc_t;
56
57struct bcmsdh_hc {
58	bcmsdh_hc_t *next;
59#ifdef BCMPLATFORM_BUS
60	struct device *dev;			/* platform device handle */
61#else
62	struct pci_dev *dev;		/* pci device handle */
63#endif /* BCMPLATFORM_BUS */
64	osl_t *osh;
65	void *regs;			/* SDIO Host Controller address */
66	bcmsdh_info_t *sdh;		/* SDIO Host Controller handle */
67	void *ch;
68	unsigned int oob_irq;
69	unsigned long oob_flags; /* OOB Host specifiction as edge and etc */
70	bool oob_irq_registered;
71	bool oob_irq_enable_flag;
72#if defined(OOB_INTR_ONLY)
73	spinlock_t irq_lock;
74#endif
75};
76static bcmsdh_hc_t *sdhcinfo = NULL;
77
78/* driver info, initialized when bcmsdh_register is called */
79static bcmsdh_driver_t drvinfo = {NULL, NULL};
80
81/* debugging macros */
82#define SDLX_MSG(x)
83
84/**
85 * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
86 */
87bool
88bcmsdh_chipmatch(uint16 vendor, uint16 device)
89{
90	/* Add other vendors and devices as required */
91
92#ifdef BCMSDIOH_STD
93	/* Check for Arasan host controller */
94	if (vendor == VENDOR_SI_IMAGE) {
95		return (TRUE);
96	}
97	/* Check for BRCM 27XX Standard host controller */
98	if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) {
99		return (TRUE);
100	}
101	/* Check for BRCM Standard host controller */
102	if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
103		return (TRUE);
104	}
105	/* Check for TI PCIxx21 Standard host controller */
106	if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) {
107		return (TRUE);
108	}
109	if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) {
110		return (TRUE);
111	}
112	/* Ricoh R5C822 Standard SDIO Host */
113	if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) {
114		return (TRUE);
115	}
116	/* JMicron Standard SDIO Host */
117	if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) {
118		return (TRUE);
119	}
120
121#endif /* BCMSDIOH_STD */
122#ifdef BCMSDIOH_SPI
123	/* This is the PciSpiHost. */
124	if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
125		printf("Found PCI SPI Host Controller\n");
126		return (TRUE);
127	}
128
129#endif /* BCMSDIOH_SPI */
130
131	return (FALSE);
132}
133
134#if defined(BCMPLATFORM_BUS)
135#if defined(BCMLXSDMMC)
136/* forward declarations */
137int bcmsdh_probe(struct device *dev);
138int bcmsdh_remove(struct device *dev);
139
140EXPORT_SYMBOL(bcmsdh_probe);
141EXPORT_SYMBOL(bcmsdh_remove);
142
143#else
144/* forward declarations */
145static int __devinit bcmsdh_probe(struct device *dev);
146static int __devexit bcmsdh_remove(struct device *dev);
147#endif /* BCMLXSDMMC */
148
149#ifndef BCMLXSDMMC
150static
151#endif /* BCMLXSDMMC */
152int bcmsdh_probe(struct device *dev)
153{
154	osl_t *osh = NULL;
155	bcmsdh_hc_t *sdhc = NULL;
156	ulong regs = 0;
157	bcmsdh_info_t *sdh = NULL;
158#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
159	struct platform_device *pdev;
160	struct resource *r;
161#endif /* BCMLXSDMMC */
162	int irq = 0;
163	uint32 vendevid;
164	unsigned long irq_flags = 0;
165
166#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
167	pdev = to_platform_device(dev);
168	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
169	irq = platform_get_irq(pdev, 0);
170	if (!r || irq == NO_IRQ)
171		return -ENXIO;
172#endif /* BCMLXSDMMC */
173
174#if defined(OOB_INTR_ONLY)
175#ifdef HW_OOB
176	irq_flags =
177		IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE;
178#else
179	 irq_flags = IRQF_TRIGGER_FALLING;
180#endif /* HW_OOB */
181
182	/* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */
183	irq = dhd_customer_oob_irq_map(&irq_flags);
184	if  (irq < 0) {
185		SDLX_MSG(("%s: Host irq is not defined\n", __FUNCTION__));
186		return 1;
187	}
188#endif /* defined(OOB_INTR_ONLY) */
189	/* allocate SDIO Host Controller state info */
190	if (!(osh = osl_attach(dev, PCI_BUS, FALSE))) {
191		SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
192		goto err;
193	}
194	if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) {
195		SDLX_MSG(("%s: out of memory, allocated %d bytes\n",
196			__FUNCTION__,
197			MALLOCED(osh)));
198		goto err;
199	}
200	bzero(sdhc, sizeof(bcmsdh_hc_t));
201	sdhc->osh = osh;
202
203	sdhc->dev = (void *)dev;
204
205#ifdef BCMLXSDMMC
206	if (!(sdh = bcmsdh_attach(osh, (void *)0,
207	                          (void **)&regs, irq))) {
208		SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
209		goto err;
210	}
211#else
212	if (!(sdh = bcmsdh_attach(osh, (void *)r->start,
213	                          (void **)&regs, irq))) {
214		SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
215		goto err;
216	}
217#endif /* BCMLXSDMMC */
218	sdhc->sdh = sdh;
219	sdhc->oob_irq = irq;
220	sdhc->oob_flags = irq_flags;
221	sdhc->oob_irq_registered = FALSE;	/* to make sure.. */
222	sdhc->oob_irq_enable_flag = FALSE;
223#if defined(OOB_INTR_ONLY)
224	spin_lock_init(&sdhc->irq_lock);
225#endif
226
227	/* chain SDIO Host Controller info together */
228	sdhc->next = sdhcinfo;
229	sdhcinfo = sdhc;
230
231	/* Read the vendor/device ID from the CIS */
232	vendevid = bcmsdh_query_device(sdh);
233	/* try to attach to the target device */
234	if (!(sdhc->ch = drvinfo.attach((vendevid >> 16),
235	                                 (vendevid & 0xFFFF), 0, 0, 0, 0,
236	                                (void *)regs, NULL, sdh))) {
237		SDLX_MSG(("%s: device attach failed\n", __FUNCTION__));
238		goto err;
239	}
240
241	return 0;
242
243	/* error handling */
244err:
245	if (sdhc) {
246		if (sdhc->sdh)
247			bcmsdh_detach(sdhc->osh, sdhc->sdh);
248		MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
249	}
250	if (osh)
251		osl_detach(osh);
252	return -ENODEV;
253}
254
255#ifndef BCMLXSDMMC
256static
257#endif /* BCMLXSDMMC */
258int bcmsdh_remove(struct device *dev)
259{
260	bcmsdh_hc_t *sdhc, *prev;
261	osl_t *osh;
262
263	sdhc = sdhcinfo;
264	drvinfo.detach(sdhc->ch);
265	bcmsdh_detach(sdhc->osh, sdhc->sdh);
266
267	/* find the SDIO Host Controller state for this pdev and take it out from the list */
268	for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
269		if (sdhc->dev == (void *)dev) {
270			if (prev)
271				prev->next = sdhc->next;
272			else
273				sdhcinfo = NULL;
274			break;
275		}
276		prev = sdhc;
277	}
278	if (!sdhc) {
279		SDLX_MSG(("%s: failed\n", __FUNCTION__));
280		return 0;
281	}
282
283	/* release SDIO Host Controller info */
284	osh = sdhc->osh;
285	MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
286	osl_detach(osh);
287
288#if !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY)
289	dev_set_drvdata(dev, NULL);
290#endif /* !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) */
291
292	return 0;
293}
294
295#else /* BCMPLATFORM_BUS */
296
297#if !defined(BCMLXSDMMC)
298/* forward declarations for PCI probe and remove functions. */
299static int __devinit bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
300static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev);
301
302/**
303 * pci id table
304 */
305static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = {
306	{ vendor: PCI_ANY_ID,
307	device: PCI_ANY_ID,
308	subvendor: PCI_ANY_ID,
309	subdevice: PCI_ANY_ID,
310	class: 0,
311	class_mask: 0,
312	driver_data: 0,
313	},
314	{ 0, }
315};
316MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid);
317
318/**
319 * SDIO Host Controller pci driver info
320 */
321static struct pci_driver bcmsdh_pci_driver = {
322	node:		{},
323	name:		"bcmsdh",
324	id_table:	bcmsdh_pci_devid,
325	probe:		bcmsdh_pci_probe,
326	remove:		bcmsdh_pci_remove,
327#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
328	save_state:	NULL,
329#endif
330	suspend:	NULL,
331	resume:		NULL,
332	};
333
334
335extern uint sd_pci_slot;	/* Force detection to a particular PCI */
336							/* slot only . Allows for having multiple */
337							/* WL devices at once in a PC */
338							/* Only one instance of dhd will be */
339							/* usable at a time */
340							/* Upper word is bus number, */
341							/* lower word is slot number */
342							/* Default value of 0xffffffff turns this */
343							/* off */
344module_param(sd_pci_slot, uint, 0);
345
346
347/**
348 * Detect supported SDIO Host Controller and attach if found.
349 *
350 * Determine if the device described by pdev is a supported SDIO Host
351 * Controller.  If so, attach to it and attach to the target device.
352 */
353static int __devinit
354bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
355{
356	osl_t *osh = NULL;
357	bcmsdh_hc_t *sdhc = NULL;
358	ulong regs;
359	bcmsdh_info_t *sdh = NULL;
360	int rc;
361
362	if (sd_pci_slot != 0xFFFFffff) {
363		if (pdev->bus->number != (sd_pci_slot>>16) ||
364			PCI_SLOT(pdev->devfn) != (sd_pci_slot&0xffff)) {
365			SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n",
366				__FUNCTION__,
367				bcmsdh_chipmatch(pdev->vendor, pdev->device)
368				?"Found compatible SDIOHC"
369				:"Probing unknown device",
370				pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor,
371				pdev->device));
372			return -ENODEV;
373		}
374		SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X (good PCI location)\n",
375			__FUNCTION__,
376			bcmsdh_chipmatch(pdev->vendor, pdev->device)
377			?"Using compatible SDIOHC"
378			:"WARNING, forced use of unkown device",
379			pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, pdev->device));
380	}
381
382	if ((pdev->vendor == VENDOR_TI) && ((pdev->device == PCIXX21_FLASHMEDIA_ID) ||
383	    (pdev->device == PCIXX21_FLASHMEDIA0_ID))) {
384		uint32 config_reg;
385
386		SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n", __FUNCTION__));
387		if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) {
388			SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
389			goto err;
390		}
391
392		config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4);
393
394		/*
395		 * Set MMC_SD_DIS bit in FlashMedia Controller.
396		 * Disbling the SD/MMC Controller in the FlashMedia Controller
397		 * allows the Standard SD Host Controller to take over control
398		 * of the SD Slot.
399		 */
400		config_reg |= 0x02;
401		OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg);
402		osl_detach(osh);
403	}
404	/* match this pci device with what we support */
405	/* we can't solely rely on this to believe it is our SDIO Host Controller! */
406	if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) {
407		return -ENODEV;
408	}
409
410	/* this is a pci device we might support */
411	SDLX_MSG(("%s: Found possible SDIO Host Controller: bus %d slot %d func %d irq %d\n",
412		__FUNCTION__,
413		pdev->bus->number, PCI_SLOT(pdev->devfn),
414		PCI_FUNC(pdev->devfn), pdev->irq));
415
416	/* use bcmsdh_query_device() to get the vendor ID of the target device so
417	 * it will eventually appear in the Broadcom string on the console
418	 */
419
420	/* allocate SDIO Host Controller state info */
421	if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) {
422		SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
423		goto err;
424	}
425	if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) {
426		SDLX_MSG(("%s: out of memory, allocated %d bytes\n",
427			__FUNCTION__,
428			MALLOCED(osh)));
429		goto err;
430	}
431	bzero(sdhc, sizeof(bcmsdh_hc_t));
432	sdhc->osh = osh;
433
434	sdhc->dev = pdev;
435
436	/* map to address where host can access */
437	pci_set_master(pdev);
438	rc = pci_enable_device(pdev);
439	if (rc) {
440		SDLX_MSG(("%s: Cannot enable PCI device\n", __FUNCTION__));
441		goto err;
442	}
443	if (!(sdh = bcmsdh_attach(osh, (void *)(uintptr)pci_resource_start(pdev, 0),
444	                          (void **)&regs, pdev->irq))) {
445		SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
446		goto err;
447	}
448
449	sdhc->sdh = sdh;
450
451	/* try to attach to the target device */
452	if (!(sdhc->ch = drvinfo.attach(VENDOR_BROADCOM, /* pdev->vendor, */
453	                                bcmsdh_query_device(sdh) & 0xFFFF, 0, 0, 0, 0,
454	                                (void *)regs, NULL, sdh))) {
455		SDLX_MSG(("%s: device attach failed\n", __FUNCTION__));
456		goto err;
457	}
458
459	/* chain SDIO Host Controller info together */
460	sdhc->next = sdhcinfo;
461	sdhcinfo = sdhc;
462
463	return 0;
464
465	/* error handling */
466err:
467	if (sdhc) {
468		if (sdhc->sdh)
469			bcmsdh_detach(sdhc->osh, sdhc->sdh);
470		MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
471	}
472	if (osh)
473		osl_detach(osh);
474	return -ENODEV;
475}
476
477
478/**
479 * Detach from target devices and SDIO Host Controller
480 */
481static void __devexit
482bcmsdh_pci_remove(struct pci_dev *pdev)
483{
484	bcmsdh_hc_t *sdhc, *prev;
485	osl_t *osh;
486
487	/* find the SDIO Host Controller state for this pdev and take it out from the list */
488	for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
489		if (sdhc->dev == pdev) {
490			if (prev)
491				prev->next = sdhc->next;
492			else
493				sdhcinfo = NULL;
494			break;
495		}
496		prev = sdhc;
497	}
498	if (!sdhc)
499		return;
500
501	drvinfo.detach(sdhc->ch);
502
503	bcmsdh_detach(sdhc->osh, sdhc->sdh);
504
505	/* release SDIO Host Controller info */
506	osh = sdhc->osh;
507	MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
508	osl_detach(osh);
509}
510#endif /* BCMLXSDMMC */
511#endif /* BCMPLATFORM_BUS */
512
513extern int sdio_function_init(void);
514
515extern int sdio_func_reg_notify(void* semaphore);
516extern void sdio_func_unreg_notify(void);
517
518int bcmsdh_reg_sdio_notify(void* semaphore)
519{
520	int ret = -1;
521
522#ifdef BCMLXSDMMC
523	ret = sdio_func_reg_notify(semaphore);
524#endif
525
526	return ret;
527}
528
529void bcmsdh_unreg_sdio_notify(void)
530{
531#ifdef BCMLXSDMMC
532	sdio_func_unreg_notify();
533#endif
534
535}
536
537int
538bcmsdh_register(bcmsdh_driver_t *driver)
539{
540	int error = 0;
541
542	drvinfo = *driver;
543
544#if defined(BCMPLATFORM_BUS)
545	SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n"));
546	error = sdio_function_init();
547	return error;
548#endif /* defined(BCMPLATFORM_BUS) */
549
550#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
551#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
552	if (!(error = pci_module_init(&bcmsdh_pci_driver)))
553		return 0;
554#else
555	if (!(error = pci_register_driver(&bcmsdh_pci_driver)))
556		return 0;
557#endif
558
559	SDLX_MSG(("%s: pci_module_init failed 0x%x\n", __FUNCTION__, error));
560#endif /* BCMPLATFORM_BUS */
561
562	return error;
563}
564
565extern void sdio_function_cleanup(void);
566
567void
568bcmsdh_unregister(void)
569{
570#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
571	if (bcmsdh_pci_driver.node.next)
572#endif
573
574#if defined(BCMLXSDMMC)
575	sdio_function_cleanup();
576#endif /* BCMLXSDMMC */
577
578#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
579	pci_unregister_driver(&bcmsdh_pci_driver);
580#endif /* BCMPLATFORM_BUS */
581}
582
583#if defined(OOB_INTR_ONLY)
584void bcmsdh_oob_intr_set(bool enable)
585{
586	static bool curstate = 1;
587	unsigned long flags;
588
589	spin_lock_irqsave(&sdhcinfo->irq_lock, flags);
590	if (curstate != enable) {
591		if (enable)
592			enable_irq(sdhcinfo->oob_irq);
593		else
594			disable_irq_nosync(sdhcinfo->oob_irq);
595		curstate = enable;
596	}
597	spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags);
598}
599
600static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
601{
602	dhd_pub_t *dhdp;
603
604	dhdp = (dhd_pub_t *)dev_get_drvdata(sdhcinfo->dev);
605
606	bcmsdh_oob_intr_set(0);
607
608	if (dhdp == NULL) {
609		SDLX_MSG(("Out of band GPIO interrupt fired way too early\n"));
610		return IRQ_HANDLED;
611	}
612
613	dhdsdio_isr((void *)dhdp->bus);
614
615	return IRQ_HANDLED;
616}
617
618int bcmsdh_register_oob_intr(void * dhdp)
619{
620	int error = 0;
621
622	SDLX_MSG(("%s Enter \n", __FUNCTION__));
623
624	/* IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; */
625
626	dev_set_drvdata(sdhcinfo->dev, dhdp);
627
628	if (!sdhcinfo->oob_irq_registered) {
629		SDLX_MSG(("%s IRQ=%d Type=%X \n", __FUNCTION__,
630			(int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags));
631		/* Refer to customer Host IRQ docs about proper irqflags definition */
632		error = request_irq(sdhcinfo->oob_irq, wlan_oob_irq, sdhcinfo->oob_flags,
633			"bcmsdh_sdmmc", NULL);
634		if (error)
635			return -ENODEV;
636
637		enable_irq_wake(sdhcinfo->oob_irq);
638		sdhcinfo->oob_irq_registered = TRUE;
639		sdhcinfo->oob_irq_enable_flag = TRUE;
640	}
641
642	return 0;
643}
644
645void *bcmsdh_get_drvdata(void)
646{
647	if (!sdhcinfo)
648		return NULL;
649	return dev_get_drvdata(sdhcinfo->dev);
650}
651void bcmsdh_set_irq(int flag)
652{
653	if (sdhcinfo->oob_irq_registered && sdhcinfo->oob_irq_enable_flag != flag) {
654		SDLX_MSG(("%s Flag = %d", __FUNCTION__, flag));
655		sdhcinfo->oob_irq_enable_flag = flag;
656		if (flag) {
657			enable_irq(sdhcinfo->oob_irq);
658			enable_irq_wake(sdhcinfo->oob_irq);
659		} else {
660			disable_irq_wake(sdhcinfo->oob_irq);
661			disable_irq(sdhcinfo->oob_irq);
662		}
663	}
664}
665
666void bcmsdh_unregister_oob_intr(void)
667{
668	SDLX_MSG(("%s: Enter\n", __FUNCTION__));
669
670	if (sdhcinfo->oob_irq_registered == TRUE) {
671		bcmsdh_set_irq(FALSE);
672		free_irq(sdhcinfo->oob_irq, NULL);
673		sdhcinfo->oob_irq_registered = FALSE;
674	}
675}
676#endif /* defined(OOB_INTR_ONLY) */
677
678/* Module parameters specific to each host-controller driver */
679
680extern uint sd_msglevel;	/* Debug message level */
681module_param(sd_msglevel, uint, 0);
682
683extern uint sd_power;	/* 0 = SD Power OFF, 1 = SD Power ON. */
684module_param(sd_power, uint, 0);
685
686extern uint sd_clock;	/* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
687module_param(sd_clock, uint, 0);
688
689extern uint sd_divisor;	/* Divisor (-1 means external clock) */
690module_param(sd_divisor, uint, 0);
691
692extern uint sd_sdmode;	/* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
693module_param(sd_sdmode, uint, 0);
694
695extern uint sd_hiok;	/* Ok to use hi-speed mode */
696module_param(sd_hiok, uint, 0);
697
698extern uint sd_f2_blocksize;
699module_param(sd_f2_blocksize, int, 0);
700
701#ifdef BCMSDIOH_STD
702extern int sd_uhsimode;
703module_param(sd_uhsimode, int, 0);
704#endif
705
706#ifdef BCMSDH_MODULE
707EXPORT_SYMBOL(bcmsdh_attach);
708EXPORT_SYMBOL(bcmsdh_detach);
709EXPORT_SYMBOL(bcmsdh_intr_query);
710EXPORT_SYMBOL(bcmsdh_intr_enable);
711EXPORT_SYMBOL(bcmsdh_intr_disable);
712EXPORT_SYMBOL(bcmsdh_intr_reg);
713EXPORT_SYMBOL(bcmsdh_intr_dereg);
714
715#if defined(DHD_DEBUG)
716EXPORT_SYMBOL(bcmsdh_intr_pending);
717#endif
718
719EXPORT_SYMBOL(bcmsdh_devremove_reg);
720EXPORT_SYMBOL(bcmsdh_cfg_read);
721EXPORT_SYMBOL(bcmsdh_cfg_write);
722EXPORT_SYMBOL(bcmsdh_cis_read);
723EXPORT_SYMBOL(bcmsdh_reg_read);
724EXPORT_SYMBOL(bcmsdh_reg_write);
725EXPORT_SYMBOL(bcmsdh_regfail);
726EXPORT_SYMBOL(bcmsdh_send_buf);
727EXPORT_SYMBOL(bcmsdh_recv_buf);
728
729EXPORT_SYMBOL(bcmsdh_rwdata);
730EXPORT_SYMBOL(bcmsdh_abort);
731EXPORT_SYMBOL(bcmsdh_query_device);
732EXPORT_SYMBOL(bcmsdh_query_iofnum);
733EXPORT_SYMBOL(bcmsdh_iovar_op);
734EXPORT_SYMBOL(bcmsdh_register);
735EXPORT_SYMBOL(bcmsdh_unregister);
736EXPORT_SYMBOL(bcmsdh_chipmatch);
737EXPORT_SYMBOL(bcmsdh_reset);
738EXPORT_SYMBOL(bcmsdh_waitlockfree);
739
740EXPORT_SYMBOL(bcmsdh_get_dstatus);
741EXPORT_SYMBOL(bcmsdh_cfg_read_word);
742EXPORT_SYMBOL(bcmsdh_cfg_write_word);
743EXPORT_SYMBOL(bcmsdh_cur_sbwad);
744EXPORT_SYMBOL(bcmsdh_chipinfo);
745
746#endif /* BCMSDH_MODULE */
747