2 * Copyright (c) 2010 Broadcom Corporation
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.
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.
18 * @file bcmsdh_linux.c
21 #define __UNDEF_NO_VERSION__
26 #include <linux/pci.h>
27 #include <linux/completion.h>
34 #if defined(OOB_INTR_ONLY)
35 #include <linux/irq.h>
36 extern void dhdsdio_isr(void *args
);
38 #include <dngl_stats.h>
40 #endif /* defined(OOB_INTR_ONLY) */
41 #if defined(CONFIG_MACH_SANDGATE2G) || defined(CONFIG_MACH_LOGICPD_PXA270)
42 #if !defined(BCMPLATFORM_BUS)
43 #define BCMPLATFORM_BUS
44 #endif /* !defined(BCMPLATFORM_BUS) */
46 #include <linux/platform_device.h>
47 #endif /* CONFIG_MACH_SANDGATE2G */
50 * SDIO Host Controller info
52 typedef struct bcmsdh_hc bcmsdh_hc_t
;
56 #ifdef BCMPLATFORM_BUS
57 struct device
*dev
; /* platform device handle */
59 struct pci_dev
*dev
; /* pci device handle */
60 #endif /* BCMPLATFORM_BUS */
62 void *regs
; /* SDIO Host Controller address */
63 bcmsdh_info_t
*sdh
; /* SDIO Host Controller handle */
66 unsigned long oob_flags
; /* OOB Host specifiction
68 bool oob_irq_registered
;
69 #if defined(OOB_INTR_ONLY)
73 static bcmsdh_hc_t
*sdhcinfo
= NULL
;
75 /* driver info, initialized when bcmsdh_register is called */
76 static bcmsdh_driver_t drvinfo
= { NULL
, NULL
};
78 /* debugging macros */
82 * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
84 bool bcmsdh_chipmatch(uint16 vendor
, uint16 device
)
86 /* Add other vendors and devices as required */
89 /* Check for Arasan host controller */
90 if (vendor
== VENDOR_SI_IMAGE
)
93 /* Check for BRCM 27XX Standard host controller */
94 if (device
== BCM27XX_SDIOH_ID
&& vendor
== VENDOR_BROADCOM
)
97 /* Check for BRCM Standard host controller */
98 if (device
== SDIOH_FPGA_ID
&& vendor
== VENDOR_BROADCOM
)
101 /* Check for TI PCIxx21 Standard host controller */
102 if (device
== PCIXX21_SDIOH_ID
&& vendor
== VENDOR_TI
)
105 if (device
== PCIXX21_SDIOH0_ID
&& vendor
== VENDOR_TI
)
108 /* Ricoh R5C822 Standard SDIO Host */
109 if (device
== R5C822_SDIOH_ID
&& vendor
== VENDOR_RICOH
)
112 /* JMicron Standard SDIO Host */
113 if (device
== JMICRON_SDIOH_ID
&& vendor
== VENDOR_JMICRON
)
115 #endif /* BCMSDIOH_STD */
117 /* This is the PciSpiHost. */
118 if (device
== SPIH_FPGA_ID
&& vendor
== VENDOR_BROADCOM
) {
119 printf("Found PCI SPI Host Controller\n");
122 #endif /* BCMSDIOH_SPI */
127 #if defined(BCMPLATFORM_BUS)
128 #if defined(BCMLXSDMMC)
129 /* forward declarations */
130 int bcmsdh_probe(struct device
*dev
);
131 EXPORT_SYMBOL(bcmsdh_probe
);
133 int bcmsdh_remove(struct device
*dev
);
134 EXPORT_SYMBOL(bcmsdh_remove
);
137 /* forward declarations */
138 static int __devinit
bcmsdh_probe(struct device
*dev
);
139 static int __devexit
bcmsdh_remove(struct device
*dev
);
140 #endif /* BCMLXSDMMC */
143 static struct device_driver bcmsdh_driver
= {
144 .name
= "pxa2xx-mci",
145 .bus
= &platform_bus_type
,
146 .probe
= bcmsdh_probe
,
147 .remove
= bcmsdh_remove
,
151 #endif /* BCMLXSDMMC */
155 #endif /* BCMLXSDMMC */
156 int bcmsdh_probe(struct device
*dev
)
159 bcmsdh_hc_t
*sdhc
= NULL
;
160 unsigned long regs
= 0;
161 bcmsdh_info_t
*sdh
= NULL
;
162 #if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
163 struct platform_device
*pdev
;
165 #endif /* BCMLXSDMMC */
168 unsigned long irq_flags
= 0;
170 #if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
171 pdev
= to_platform_device(dev
);
172 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
173 irq
= platform_get_irq(pdev
, 0);
174 if (!r
|| irq
== NO_IRQ
)
176 #endif /* BCMLXSDMMC */
178 #if defined(OOB_INTR_ONLY)
181 IORESOURCE_IRQ
| IORESOURCE_IRQ_HIGHLEVEL
|
182 IORESOURCE_IRQ_SHAREABLE
;
184 irq_flags
= IRQF_TRIGGER_FALLING
;
186 irq
= dhd_customer_oob_irq_map(&irq_flags
);
188 SDLX_MSG(("%s: Host irq is not defined\n", __func__
));
191 #endif /* defined(OOB_INTR_ONLY) */
192 /* allocate SDIO Host Controller state info */
193 if (!(osh
= osl_attach(dev
, PCI_BUS
, FALSE
))) {
194 SDLX_MSG(("%s: osl_attach failed\n", __func__
));
197 if (!(sdhc
= MALLOC(osh
, sizeof(bcmsdh_hc_t
)))) {
198 SDLX_MSG(("%s: out of memory, allocated %d bytes\n",
199 __func__
, MALLOCED(osh
)));
202 bzero(sdhc
, sizeof(bcmsdh_hc_t
));
205 sdhc
->dev
= (void *)dev
;
208 if (!(sdh
= bcmsdh_attach(osh
, (void *)0,
209 (void **)®s
, irq
))) {
210 SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__
));
214 if (!(sdh
= bcmsdh_attach(osh
, (void *)r
->start
,
215 (void **)®s
, irq
))) {
216 SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__
));
219 #endif /* BCMLXSDMMC */
222 sdhc
->oob_flags
= irq_flags
;
223 sdhc
->oob_irq_registered
= FALSE
; /* to make sure.. */
224 #if defined(OOB_INTR_ONLY)
225 spin_lock_init(&sdhc
->irq_lock
);
228 /* chain SDIO Host Controller info together */
229 sdhc
->next
= sdhcinfo
;
231 /* Read the vendor/device ID from the CIS */
232 vendevid
= bcmsdh_query_device(sdh
);
234 /* try to attach to the target device */
235 if (!(sdhc
->ch
= drvinfo
.attach((vendevid
>> 16),
236 (vendevid
& 0xFFFF), 0, 0, 0, 0,
237 (void *)regs
, NULL
, sdh
))) {
238 SDLX_MSG(("%s: device attach failed\n", __func__
));
248 bcmsdh_detach(sdhc
->osh
, sdhc
->sdh
);
249 MFREE(osh
, sdhc
, sizeof(bcmsdh_hc_t
));
258 #endif /* BCMLXSDMMC */
259 int bcmsdh_remove(struct device
*dev
)
261 bcmsdh_hc_t
*sdhc
, *prev
;
265 drvinfo
.detach(sdhc
->ch
);
266 bcmsdh_detach(sdhc
->osh
, sdhc
->sdh
);
267 /* find the SDIO Host Controller state for this pdev
268 and take it out from the list */
269 for (sdhc
= sdhcinfo
, prev
= NULL
; sdhc
; sdhc
= sdhc
->next
) {
270 if (sdhc
->dev
== (void *)dev
) {
272 prev
->next
= sdhc
->next
;
280 SDLX_MSG(("%s: failed\n", __func__
));
284 /* release SDIO Host Controller info */
286 MFREE(osh
, sdhc
, sizeof(bcmsdh_hc_t
));
289 #if !defined(BCMLXSDMMC)
290 dev_set_drvdata(dev
, NULL
);
291 #endif /* !defined(BCMLXSDMMC) */
296 #else /* BCMPLATFORM_BUS */
298 #if !defined(BCMLXSDMMC)
299 /* forward declarations for PCI probe and remove functions. */
300 static int __devinit
bcmsdh_pci_probe(struct pci_dev
*pdev
,
301 const struct pci_device_id
*ent
);
302 static void __devexit
bcmsdh_pci_remove(struct pci_dev
*pdev
);
307 static struct pci_device_id bcmsdh_pci_devid
[] __devinitdata
= {
309 .vendor
= PCI_ANY_ID
,
310 .device
= PCI_ANY_ID
,
311 .subvendor
= PCI_ANY_ID
,
312 .subdevice
= PCI_ANY_ID
,
320 MODULE_DEVICE_TABLE(pci
, bcmsdh_pci_devid
);
323 * SDIO Host Controller pci driver info
325 static struct pci_driver bcmsdh_pci_driver
= {
328 .id_table
= bcmsdh_pci_devid
,
329 .probe
= bcmsdh_pci_probe
,
330 .remove
= bcmsdh_pci_remove
,
335 extern 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 */
344 module_param(sd_pci_slot
, uint
, 0);
347 * Detect supported SDIO Host Controller and attach if found.
349 * Determine if the device described by pdev is a supported SDIO Host
350 * Controller. If so, attach to it and attach to the target device.
353 bcmsdh_pci_probe(struct pci_dev
*pdev
, const struct pci_device_id
*ent
)
356 bcmsdh_hc_t
*sdhc
= NULL
;
358 bcmsdh_info_t
*sdh
= NULL
;
361 if (sd_pci_slot
!= 0xFFFFffff) {
362 if (pdev
->bus
->number
!= (sd_pci_slot
>> 16) ||
363 PCI_SLOT(pdev
->devfn
) != (sd_pci_slot
& 0xffff)) {
364 SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n",
366 bcmsdh_chipmatch(pdev
->vendor
, pdev
->device
) ?
367 "Found compatible SDIOHC" :
368 "Probing unknown device",
369 pdev
->bus
->number
, PCI_SLOT(pdev
->devfn
),
370 pdev
->vendor
, pdev
->device
));
373 SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X "
374 "(good PCI location)\n", __func__
,
375 bcmsdh_chipmatch(pdev
->vendor
, pdev
->device
) ?
376 "Using compatible SDIOHC" : "WARNING, forced use "
378 pdev
->bus
->number
, PCI_SLOT(pdev
->devfn
), pdev
->vendor
,
382 if ((pdev
->vendor
== VENDOR_TI
)
383 && ((pdev
->device
== PCIXX21_FLASHMEDIA_ID
)
384 || (pdev
->device
== PCIXX21_FLASHMEDIA0_ID
))) {
387 SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n",
389 if (!(osh
= osl_attach(pdev
, PCI_BUS
, FALSE
))) {
390 SDLX_MSG(("%s: osl_attach failed\n", __func__
));
394 config_reg
= OSL_PCI_READ_CONFIG(osh
, 0x4c, 4);
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
403 OSL_PCI_WRITE_CONFIG(osh
, 0x4c, 4, config_reg
);
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
))
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
));
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
422 /* allocate SDIO Host Controller state info */
423 if (!(osh
= osl_attach(pdev
, PCI_BUS
, FALSE
))) {
424 SDLX_MSG(("%s: osl_attach failed\n", __func__
));
427 if (!(sdhc
= MALLOC(osh
, sizeof(bcmsdh_hc_t
)))) {
428 SDLX_MSG(("%s: out of memory, allocated %d bytes\n",
429 __func__
, MALLOCED(osh
)));
432 bzero(sdhc
, sizeof(bcmsdh_hc_t
));
437 /* map to address where host can access */
438 pci_set_master(pdev
);
439 rc
= pci_enable_device(pdev
);
441 SDLX_MSG(("%s: Cannot enable PCI device\n", __func__
));
446 bcmsdh_attach(osh
, (void *)(uintptr
) pci_resource_start(pdev
, 0),
447 (void **)®s
, pdev
->irq
))) {
448 SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__
));
454 /* try to attach to the target device */
455 if (!(sdhc
->ch
= drvinfo
.attach(VENDOR_BROADCOM
, /* pdev->vendor, */
456 bcmsdh_query_device(sdh
) & 0xFFFF, 0, 0,
457 0, 0, (void *)regs
, NULL
, sdh
))) {
458 SDLX_MSG(("%s: device attach failed\n", __func__
));
462 /* chain SDIO Host Controller info together */
463 sdhc
->next
= sdhcinfo
;
471 bcmsdh_detach(sdhc
->osh
, sdhc
->sdh
);
473 MFREE(osh
, sdhc
, sizeof(bcmsdh_hc_t
));
480 * Detach from target devices and SDIO Host Controller
482 static void __devexit
bcmsdh_pci_remove(struct pci_dev
*pdev
)
484 bcmsdh_hc_t
*sdhc
, *prev
;
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
) {
492 prev
->next
= sdhc
->next
;
502 drvinfo
.detach(sdhc
->ch
);
504 bcmsdh_detach(sdhc
->osh
, sdhc
->sdh
);
506 /* release SDIO Host Controller info */
508 MFREE(osh
, sdhc
, sizeof(bcmsdh_hc_t
));
511 #endif /* BCMLXSDMMC */
512 #endif /* BCMPLATFORM_BUS */
514 extern int sdio_function_init(void);
516 int bcmsdh_register(bcmsdh_driver_t
*driver
)
522 #if defined(BCMPLATFORM_BUS)
523 #if defined(BCMLXSDMMC)
524 SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n"));
525 error
= sdio_function_init();
527 SDLX_MSG(("Intel PXA270 SDIO Driver\n"));
528 error
= driver_register(&bcmsdh_driver
);
529 #endif /* defined(BCMLXSDMMC) */
531 #endif /* defined(BCMPLATFORM_BUS) */
533 #if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
534 if (!(error
= pci_register_driver(&bcmsdh_pci_driver
)))
537 SDLX_MSG(("%s: pci_module_init failed 0x%x\n", __func__
, error
));
538 #endif /* BCMPLATFORM_BUS */
543 extern void sdio_function_cleanup(void);
545 void bcmsdh_unregister(void)
547 #if defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
548 driver_unregister(&bcmsdh_driver
);
550 #if defined(BCMLXSDMMC)
551 sdio_function_cleanup();
552 #endif /* BCMLXSDMMC */
553 #if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
554 pci_unregister_driver(&bcmsdh_pci_driver
);
555 #endif /* BCMPLATFORM_BUS */
558 #if defined(OOB_INTR_ONLY)
559 void bcmsdh_oob_intr_set(bool enable
)
561 static bool curstate
= 1;
564 spin_lock_irqsave(&sdhcinfo
->irq_lock
, flags
);
565 if (curstate
!= enable
) {
567 enable_irq(sdhcinfo
->oob_irq
);
569 disable_irq_nosync(sdhcinfo
->oob_irq
);
572 spin_unlock_irqrestore(&sdhcinfo
->irq_lock
, flags
);
575 static irqreturn_t
wlan_oob_irq(int irq
, void *dev_id
)
579 dhdp
= (dhd_pub_t
*) dev_get_drvdata(sdhcinfo
->dev
);
581 bcmsdh_oob_intr_set(0);
584 SDLX_MSG(("Out of band GPIO interrupt fired way too early\n"));
588 WAKE_LOCK_TIMEOUT(dhdp
, WAKE_LOCK_TMOUT
, 25);
590 dhdsdio_isr((void *)dhdp
->bus
);
595 int bcmsdh_register_oob_intr(void *dhdp
)
599 SDLX_MSG(("%s Enter\n", __func__
));
601 sdhcinfo
->oob_flags
=
602 IORESOURCE_IRQ
| IORESOURCE_IRQ_HIGHLEVEL
|
603 IORESOURCE_IRQ_SHAREABLE
;
604 dev_set_drvdata(sdhcinfo
->dev
, dhdp
);
606 if (!sdhcinfo
->oob_irq_registered
) {
607 SDLX_MSG(("%s IRQ=%d Type=%X\n", __func__
,
608 (int)sdhcinfo
->oob_irq
, (int)sdhcinfo
->oob_flags
));
609 /* Refer to customer Host IRQ docs about
610 proper irqflags definition */
612 request_irq(sdhcinfo
->oob_irq
, wlan_oob_irq
,
613 sdhcinfo
->oob_flags
, "bcmsdh_sdmmc", NULL
);
617 set_irq_wake(sdhcinfo
->oob_irq
, 1);
618 sdhcinfo
->oob_irq_registered
= TRUE
;
624 void bcmsdh_unregister_oob_intr(void)
626 SDLX_MSG(("%s: Enter\n", __func__
));
628 set_irq_wake(sdhcinfo
->oob_irq
, 0);
629 disable_irq(sdhcinfo
->oob_irq
); /* just in case.. */
630 free_irq(sdhcinfo
->oob_irq
, NULL
);
631 sdhcinfo
->oob_irq_registered
= FALSE
;
633 #endif /* defined(OOB_INTR_ONLY) */
634 /* Module parameters specific to each host-controller driver */
636 extern uint sd_msglevel
; /* Debug message level */
637 module_param(sd_msglevel
, uint
, 0);
639 extern uint sd_power
; /* 0 = SD Power OFF,
641 module_param(sd_power
, uint
, 0);
643 extern uint sd_clock
; /* SD Clock Control, 0 = SD Clock OFF,
645 module_param(sd_clock
, uint
, 0);
647 extern uint sd_divisor
; /* Divisor (-1 means external clock) */
648 module_param(sd_divisor
, uint
, 0);
650 extern uint sd_sdmode
; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
651 module_param(sd_sdmode
, uint
, 0);
653 extern uint sd_hiok
; /* Ok to use hi-speed mode */
654 module_param(sd_hiok
, uint
, 0);
656 extern uint sd_f2_blocksize
;
657 module_param(sd_f2_blocksize
, int, 0);