blob: 9028cd01d9d03fdfdbf1feb6248d02b2d8c57197 [file] [log] [blame]
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/**
18 * @file bcmsdh_linux.c
19 */
20
21#define __UNDEF_NO_VERSION__
22
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070023#include <linuxver.h>
24
25#include <linux/pci.h>
26#include <linux/completion.h>
27
28#include <osl.h>
29#include <pcicfg.h>
30#include <bcmdefs.h>
31#include <bcmdevs.h>
32
33#if defined(OOB_INTR_ONLY)
34#include <linux/irq.h>
35extern void dhdsdio_isr(void *args);
36#include <bcmutils.h>
37#include <dngl_stats.h>
38#include <dhd.h>
39#endif /* defined(OOB_INTR_ONLY) */
40#if defined(CONFIG_MACH_SANDGATE2G) || defined(CONFIG_MACH_LOGICPD_PXA270)
41#if !defined(BCMPLATFORM_BUS)
42#define BCMPLATFORM_BUS
43#endif /* !defined(BCMPLATFORM_BUS) */
44
45#include <linux/platform_device.h>
46#endif /* CONFIG_MACH_SANDGATE2G */
47
48/**
49 * SDIO Host Controller info
50 */
51typedef struct bcmsdh_hc bcmsdh_hc_t;
52
53struct bcmsdh_hc {
54 bcmsdh_hc_t *next;
55#ifdef BCMPLATFORM_BUS
56 struct device *dev; /* platform device handle */
57#else
58 struct pci_dev *dev; /* pci device handle */
59#endif /* BCMPLATFORM_BUS */
60 osl_t *osh;
61 void *regs; /* SDIO Host Controller address */
62 bcmsdh_info_t *sdh; /* SDIO Host Controller handle */
63 void *ch;
64 unsigned int oob_irq;
65 unsigned long oob_flags; /* OOB Host specifiction
66 as edge and etc */
67 bool oob_irq_registered;
68#if defined(OOB_INTR_ONLY)
69 spinlock_t irq_lock;
70#endif
71};
Jason Cooper5f782de2010-10-06 10:08:01 -040072static bcmsdh_hc_t *sdhcinfo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070073
74/* driver info, initialized when bcmsdh_register is called */
75static bcmsdh_driver_t drvinfo = { NULL, NULL };
76
77/* debugging macros */
78#define SDLX_MSG(x)
79
80/**
81 * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
82 */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070083bool bcmsdh_chipmatch(u16 vendor, u16 device)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070084{
85 /* Add other vendors and devices as required */
86
87#ifdef BCMSDIOH_STD
88 /* Check for Arasan host controller */
89 if (vendor == VENDOR_SI_IMAGE)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -070090 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070091
92 /* Check for BRCM 27XX Standard host controller */
93 if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -070094 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070095
96 /* Check for BRCM Standard host controller */
97 if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -070098 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070099
100 /* Check for TI PCIxx21 Standard host controller */
101 if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700102 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700103
104 if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700105 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700106
107 /* Ricoh R5C822 Standard SDIO Host */
108 if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700109 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700110
111 /* JMicron Standard SDIO Host */
112 if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700113 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700114#endif /* BCMSDIOH_STD */
115#ifdef BCMSDIOH_SPI
116 /* This is the PciSpiHost. */
117 if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
118 printf("Found PCI SPI Host Controller\n");
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700119 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700120 }
121#endif /* BCMSDIOH_SPI */
122
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700123 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700124}
125
126#if defined(BCMPLATFORM_BUS)
127#if defined(BCMLXSDMMC)
128/* forward declarations */
129int bcmsdh_probe(struct device *dev);
130EXPORT_SYMBOL(bcmsdh_probe);
131
132int bcmsdh_remove(struct device *dev);
133EXPORT_SYMBOL(bcmsdh_remove);
134
135#else
136/* forward declarations */
137static int __devinit bcmsdh_probe(struct device *dev);
138static int __devexit bcmsdh_remove(struct device *dev);
139#endif /* BCMLXSDMMC */
140
141#ifndef BCMLXSDMMC
142static struct device_driver bcmsdh_driver = {
143 .name = "pxa2xx-mci",
144 .bus = &platform_bus_type,
145 .probe = bcmsdh_probe,
146 .remove = bcmsdh_remove,
147 .suspend = NULL,
148 .resume = NULL,
149};
150#endif /* BCMLXSDMMC */
151
152#ifndef BCMLXSDMMC
153static
154#endif /* BCMLXSDMMC */
155int bcmsdh_probe(struct device *dev)
156{
157 osl_t *osh = NULL;
158 bcmsdh_hc_t *sdhc = NULL;
Greg Kroah-Hartman3deea902010-10-05 11:15:47 -0700159 unsigned long regs = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700160 bcmsdh_info_t *sdh = NULL;
161#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
162 struct platform_device *pdev;
163 struct resource *r;
164#endif /* BCMLXSDMMC */
165 int irq = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700166 u32 vendevid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700167 unsigned long irq_flags = 0;
168
169#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
170 pdev = to_platform_device(dev);
171 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
172 irq = platform_get_irq(pdev, 0);
173 if (!r || irq == NO_IRQ)
174 return -ENXIO;
175#endif /* BCMLXSDMMC */
176
177#if defined(OOB_INTR_ONLY)
178#ifdef HW_OOB
179 irq_flags =
180 IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL |
181 IORESOURCE_IRQ_SHAREABLE;
182#else
183 irq_flags = IRQF_TRIGGER_FALLING;
184#endif /* HW_OOB */
185 irq = dhd_customer_oob_irq_map(&irq_flags);
186 if (irq < 0) {
187 SDLX_MSG(("%s: Host irq is not defined\n", __func__));
188 return 1;
189 }
190#endif /* defined(OOB_INTR_ONLY) */
191 /* allocate SDIO Host Controller state info */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700192 osh = osl_attach(dev, PCI_BUS, false);
Jason Cooper81e95f92010-10-06 10:07:59 -0400193 if (!osh) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700194 SDLX_MSG(("%s: osl_attach failed\n", __func__));
195 goto err;
196 }
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +0200197 sdhc = kzalloc(sizeof(bcmsdh_hc_t), GFP_ATOMIC);
Jason Cooper81e95f92010-10-06 10:07:59 -0400198 if (!sdhc) {
mike.rapoport@gmail.com97e17d02010-10-13 00:09:09 +0200199 SDLX_MSG(("%s: out of memory\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700200 goto err;
201 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700202 sdhc->osh = osh;
203
204 sdhc->dev = (void *)dev;
205
206#ifdef BCMLXSDMMC
Jason Cooper81e95f92010-10-06 10:07:59 -0400207 sdh = bcmsdh_attach(osh, (void *)0, (void **)&regs, irq);
208 if (!sdh) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700209 SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__));
210 goto err;
211 }
212#else
Jason Cooper81e95f92010-10-06 10:07:59 -0400213 sdh = bcmsdh_attach(osh, (void *)r->start, (void **)&regs, irq);
214 if (!sdh) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700215 SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__));
216 goto err;
217 }
218#endif /* BCMLXSDMMC */
219 sdhc->sdh = sdh;
220 sdhc->oob_irq = irq;
221 sdhc->oob_flags = irq_flags;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700222 sdhc->oob_irq_registered = false; /* to make sure.. */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700223#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 /* Read the vendor/device ID from the CIS */
231 vendevid = bcmsdh_query_device(sdh);
232
233 /* try to attach to the target device */
Jason Cooper81e95f92010-10-06 10:07:59 -0400234 sdhc->ch = drvinfo.attach((vendevid >> 16), (vendevid & 0xFFFF),
235 0, 0, 0, 0, (void *)regs, NULL, sdh);
236 if (!sdhc->ch) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700237 SDLX_MSG(("%s: device attach failed\n", __func__));
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);
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +0200248 kfree(sdhc);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700249 }
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 /* find the SDIO Host Controller state for this pdev
267 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", __func__));
280 return 0;
281 }
282
283 /* release SDIO Host Controller info */
284 osh = sdhc->osh;
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +0200285 kfree(sdhc);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700286 osl_detach(osh);
287
288#if !defined(BCMLXSDMMC)
289 dev_set_drvdata(dev, NULL);
290#endif /* !defined(BCMLXSDMMC) */
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,
300 const struct pci_device_id *ent);
301static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev);
302
303/**
304 * pci id table
305 */
306static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = {
307{
308 .vendor = PCI_ANY_ID,
309 .device = PCI_ANY_ID,
310 .subvendor = PCI_ANY_ID,
311 .subdevice = PCI_ANY_ID,
312 .class = 0,
313 .class_mask = 0,
314 .driver_data = 0,
315},
316{0,}
317};
318
319MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid);
320
321/**
322 * SDIO Host Controller pci driver info
323 */
324static struct pci_driver bcmsdh_pci_driver = {
325 .node = {},
326 .name = "bcmsdh",
327 .id_table = bcmsdh_pci_devid,
328 .probe = bcmsdh_pci_probe,
329 .remove = bcmsdh_pci_remove,
330 .suspend = NULL,
331 .resume = NULL,
332};
333
334extern uint sd_pci_slot; /* Force detection to a particular PCI */
335 /* slot only . Allows for having multiple */
336 /* WL devices at once in a PC */
337 /* Only one instance of dhd will be */
338 /* usable at a time */
339 /* Upper word is bus number, */
340 /* lower word is slot number */
341 /* Default value of 0xFFFFffff turns this */
342 /* off */
343module_param(sd_pci_slot, uint, 0);
344
345/**
346 * Detect supported SDIO Host Controller and attach if found.
347 *
348 * Determine if the device described by pdev is a supported SDIO Host
349 * Controller. If so, attach to it and attach to the target device.
350 */
351static int __devinit
352bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
353{
354 osl_t *osh = NULL;
355 bcmsdh_hc_t *sdhc = NULL;
Greg Kroah-Hartman3deea902010-10-05 11:15:47 -0700356 unsigned long regs;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700357 bcmsdh_info_t *sdh = NULL;
358 int rc;
359
360 if (sd_pci_slot != 0xFFFFffff) {
361 if (pdev->bus->number != (sd_pci_slot >> 16) ||
362 PCI_SLOT(pdev->devfn) != (sd_pci_slot & 0xffff)) {
363 SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n",
364 __func__,
365 bcmsdh_chipmatch(pdev->vendor, pdev->device) ?
366 "Found compatible SDIOHC" :
367 "Probing unknown device",
368 pdev->bus->number, PCI_SLOT(pdev->devfn),
369 pdev->vendor, pdev->device));
370 return -ENODEV;
371 }
372 SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X "
373 "(good PCI location)\n", __func__,
374 bcmsdh_chipmatch(pdev->vendor, pdev->device) ?
375 "Using compatible SDIOHC" : "WARNING, forced use "
376 "of unkown device",
377 pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor,
378 pdev->device));
379 }
380
381 if ((pdev->vendor == VENDOR_TI)
382 && ((pdev->device == PCIXX21_FLASHMEDIA_ID)
383 || (pdev->device == PCIXX21_FLASHMEDIA0_ID))) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700384 u32 config_reg;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700385
386 SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n",
387 __func__));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700388 osh = osl_attach(pdev, PCI_BUS, false);
Jason Cooper81e95f92010-10-06 10:07:59 -0400389 if (!osh) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700390 SDLX_MSG(("%s: osl_attach failed\n", __func__));
391 goto err;
392 }
393
394 config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4);
395
396 /*
397 * Set MMC_SD_DIS bit in FlashMedia Controller.
398 * Disbling the SD/MMC Controller in the FlashMedia Controller
399 * allows the Standard SD Host Controller to take over control
400 * of the SD Slot.
401 */
402 config_reg |= 0x02;
403 OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg);
404 osl_detach(osh);
405 }
406 /* match this pci device with what we support */
407 /* we can't solely rely on this to believe it is
408 our SDIO Host Controller! */
409 if (!bcmsdh_chipmatch(pdev->vendor, pdev->device))
410 return -ENODEV;
411
412 /* this is a pci device we might support */
413 SDLX_MSG(("%s: Found possible SDIO Host Controller: "
414 "bus %d slot %d func %d irq %d\n", __func__,
415 pdev->bus->number, PCI_SLOT(pdev->devfn),
416 PCI_FUNC(pdev->devfn), pdev->irq));
417
418 /* use bcmsdh_query_device() to get the vendor ID of the target device
419 * so it will eventually appear in the Broadcom string on the console
420 */
421
422 /* allocate SDIO Host Controller state info */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700423 osh = osl_attach(pdev, PCI_BUS, false);
Jason Cooper81e95f92010-10-06 10:07:59 -0400424 if (!osh) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700425 SDLX_MSG(("%s: osl_attach failed\n", __func__));
426 goto err;
427 }
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +0200428 sdhc = kzalloc(sizeof(bcmsdh_hc_t), GFP_ATOMIC);
Jason Cooper81e95f92010-10-06 10:07:59 -0400429 if (!sdhc) {
mike.rapoport@gmail.com97e17d02010-10-13 00:09:09 +0200430 SDLX_MSG(("%s: out of memory\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700431 goto err;
432 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700433 sdhc->osh = osh;
434
435 sdhc->dev = pdev;
436
437 /* map to address where host can access */
438 pci_set_master(pdev);
439 rc = pci_enable_device(pdev);
440 if (rc) {
441 SDLX_MSG(("%s: Cannot enable PCI device\n", __func__));
442 goto err;
443 }
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -0700444 sdh = bcmsdh_attach(osh, (void *)(unsigned long)pci_resource_start(pdev, 0),
Jason Cooper81e95f92010-10-06 10:07:59 -0400445 (void **)&regs, pdev->irq);
446 if (!sdh) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700447 SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__));
448 goto err;
449 }
450
451 sdhc->sdh = sdh;
452
453 /* try to attach to the target device */
Jason Cooper81e95f92010-10-06 10:07:59 -0400454 sdhc->ch = drvinfo.attach(VENDOR_BROADCOM, /* pdev->vendor, */
455 bcmsdh_query_device(sdh) & 0xFFFF, 0, 0, 0, 0,
456 (void *)regs, NULL, sdh);
457 if (!sdhc->ch) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700458 SDLX_MSG(("%s: device attach failed\n", __func__));
459 goto err;
460 }
461
462 /* chain SDIO Host Controller info together */
463 sdhc->next = sdhcinfo;
464 sdhcinfo = sdhc;
465
466 return 0;
467
468 /* error handling */
469err:
470 if (sdhc->sdh)
471 bcmsdh_detach(sdhc->osh, sdhc->sdh);
472 if (sdhc)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +0200473 kfree(sdhc);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700474 if (osh)
475 osl_detach(osh);
476 return -ENODEV;
477}
478
479/**
480 * Detach from target devices and SDIO Host Controller
481 */
482static void __devexit bcmsdh_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
488 pdev and take it out from the list */
489 for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
490 if (sdhc->dev == pdev) {
491 if (prev)
492 prev->next = sdhc->next;
493 else
494 sdhcinfo = NULL;
495 break;
496 }
497 prev = sdhc;
498 }
499 if (!sdhc)
500 return;
501
502 drvinfo.detach(sdhc->ch);
503
504 bcmsdh_detach(sdhc->osh, sdhc->sdh);
505
506 /* release SDIO Host Controller info */
507 osh = sdhc->osh;
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +0200508 kfree(sdhc);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700509 osl_detach(osh);
510}
511#endif /* BCMLXSDMMC */
512#endif /* BCMPLATFORM_BUS */
513
514extern int sdio_function_init(void);
515
516int bcmsdh_register(bcmsdh_driver_t *driver)
517{
518 int error = 0;
519
520 drvinfo = *driver;
521
522#if defined(BCMPLATFORM_BUS)
523#if defined(BCMLXSDMMC)
524 SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n"));
525 error = sdio_function_init();
526#else
527 SDLX_MSG(("Intel PXA270 SDIO Driver\n"));
528 error = driver_register(&bcmsdh_driver);
529#endif /* defined(BCMLXSDMMC) */
530 return error;
531#endif /* defined(BCMPLATFORM_BUS) */
532
533#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
Jason Cooper81e95f92010-10-06 10:07:59 -0400534 error = pci_register_driver(&bcmsdh_pci_driver);
535 if (!error)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700536 return 0;
537
Greg Kroah-Hartman8ba9cfd2010-10-07 16:23:24 -0700538 SDLX_MSG(("%s: pci_register_driver failed 0x%x\n", __func__, error));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700539#endif /* BCMPLATFORM_BUS */
540
541 return error;
542}
543
544extern void sdio_function_cleanup(void);
545
546void bcmsdh_unregister(void)
547{
548#if defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
549 driver_unregister(&bcmsdh_driver);
550#endif
551#if defined(BCMLXSDMMC)
552 sdio_function_cleanup();
553#endif /* BCMLXSDMMC */
554#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
555 pci_unregister_driver(&bcmsdh_pci_driver);
556#endif /* BCMPLATFORM_BUS */
557}
558
559#if defined(OOB_INTR_ONLY)
560void bcmsdh_oob_intr_set(bool enable)
561{
562 static bool curstate = 1;
563 unsigned long flags;
564
565 spin_lock_irqsave(&sdhcinfo->irq_lock, flags);
566 if (curstate != enable) {
567 if (enable)
568 enable_irq(sdhcinfo->oob_irq);
569 else
570 disable_irq_nosync(sdhcinfo->oob_irq);
571 curstate = enable;
572 }
573 spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags);
574}
575
576static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
577{
578 dhd_pub_t *dhdp;
579
580 dhdp = (dhd_pub_t *) dev_get_drvdata(sdhcinfo->dev);
581
582 bcmsdh_oob_intr_set(0);
583
584 if (dhdp == NULL) {
585 SDLX_MSG(("Out of band GPIO interrupt fired way too early\n"));
586 return IRQ_HANDLED;
587 }
588
589 WAKE_LOCK_TIMEOUT(dhdp, WAKE_LOCK_TMOUT, 25);
590
591 dhdsdio_isr((void *)dhdp->bus);
592
593 return IRQ_HANDLED;
594}
595
596int bcmsdh_register_oob_intr(void *dhdp)
597{
598 int error = 0;
599
600 SDLX_MSG(("%s Enter\n", __func__));
601
602 sdhcinfo->oob_flags =
603 IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL |
604 IORESOURCE_IRQ_SHAREABLE;
605 dev_set_drvdata(sdhcinfo->dev, dhdp);
606
607 if (!sdhcinfo->oob_irq_registered) {
608 SDLX_MSG(("%s IRQ=%d Type=%X\n", __func__,
609 (int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags));
610 /* Refer to customer Host IRQ docs about
611 proper irqflags definition */
612 error =
613 request_irq(sdhcinfo->oob_irq, wlan_oob_irq,
614 sdhcinfo->oob_flags, "bcmsdh_sdmmc", NULL);
615 if (error)
616 return -ENODEV;
617
618 set_irq_wake(sdhcinfo->oob_irq, 1);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700619 sdhcinfo->oob_irq_registered = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700620 }
621
622 return 0;
623}
624
625void bcmsdh_unregister_oob_intr(void)
626{
627 SDLX_MSG(("%s: Enter\n", __func__));
628
629 set_irq_wake(sdhcinfo->oob_irq, 0);
630 disable_irq(sdhcinfo->oob_irq); /* just in case.. */
631 free_irq(sdhcinfo->oob_irq, NULL);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700632 sdhcinfo->oob_irq_registered = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700633}
634#endif /* defined(OOB_INTR_ONLY) */
635/* Module parameters specific to each host-controller driver */
636
637extern uint sd_msglevel; /* Debug message level */
638module_param(sd_msglevel, uint, 0);
639
640extern uint sd_power; /* 0 = SD Power OFF,
641 1 = SD Power ON. */
642module_param(sd_power, uint, 0);
643
644extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF,
645 1 = SD Clock ON */
646module_param(sd_clock, uint, 0);
647
648extern uint sd_divisor; /* Divisor (-1 means external clock) */
649module_param(sd_divisor, uint, 0);
650
651extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
652module_param(sd_sdmode, uint, 0);
653
654extern uint sd_hiok; /* Ok to use hi-speed mode */
655module_param(sd_hiok, uint, 0);
656
657extern uint sd_f2_blocksize;
658module_param(sd_f2_blocksize, int, 0);