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