Commit | Line | Data |
---|---|---|
cf2b4488 HP |
1 | /* |
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 | ||
23 | #include <typedefs.h> | |
24 | #include <linuxver.h> | |
25 | ||
26 | #include <linux/pci.h> | |
27 | #include <linux/completion.h> | |
28 | ||
29 | #include <osl.h> | |
30 | #include <pcicfg.h> | |
31 | #include <bcmdefs.h> | |
32 | #include <bcmdevs.h> | |
33 | ||
34 | #if defined(OOB_INTR_ONLY) | |
35 | #include <linux/irq.h> | |
36 | extern void dhdsdio_isr(void *args); | |
37 | #include <bcmutils.h> | |
38 | #include <dngl_stats.h> | |
39 | #include <dhd.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) */ | |
45 | ||
46 | #include <linux/platform_device.h> | |
47 | #endif /* CONFIG_MACH_SANDGATE2G */ | |
48 | ||
49 | /** | |
50 | * SDIO Host Controller info | |
51 | */ | |
52 | typedef struct bcmsdh_hc bcmsdh_hc_t; | |
53 | ||
54 | struct bcmsdh_hc { | |
55 | bcmsdh_hc_t *next; | |
56 | #ifdef BCMPLATFORM_BUS | |
57 | struct device *dev; /* platform device handle */ | |
58 | #else | |
59 | struct pci_dev *dev; /* pci device handle */ | |
60 | #endif /* BCMPLATFORM_BUS */ | |
61 | osl_t *osh; | |
62 | void *regs; /* SDIO Host Controller address */ | |
63 | bcmsdh_info_t *sdh; /* SDIO Host Controller handle */ | |
64 | void *ch; | |
65 | unsigned int oob_irq; | |
66 | unsigned long oob_flags; /* OOB Host specifiction | |
67 | as edge and etc */ | |
68 | bool oob_irq_registered; | |
69 | #if defined(OOB_INTR_ONLY) | |
70 | spinlock_t irq_lock; | |
71 | #endif | |
72 | }; | |
5f782dee | 73 | static bcmsdh_hc_t *sdhcinfo; |
cf2b4488 HP |
74 | |
75 | /* driver info, initialized when bcmsdh_register is called */ | |
76 | static bcmsdh_driver_t drvinfo = { NULL, NULL }; | |
77 | ||
78 | /* debugging macros */ | |
79 | #define SDLX_MSG(x) | |
80 | ||
81 | /** | |
82 | * Checks to see if vendor and device IDs match a supported SDIO Host Controller. | |
83 | */ | |
7d4df48e | 84 | bool bcmsdh_chipmatch(u16 vendor, u16 device) |
cf2b4488 HP |
85 | { |
86 | /* Add other vendors and devices as required */ | |
87 | ||
88 | #ifdef BCMSDIOH_STD | |
89 | /* Check for Arasan host controller */ | |
90 | if (vendor == VENDOR_SI_IMAGE) | |
0f0881b0 | 91 | return true; |
cf2b4488 HP |
92 | |
93 | /* Check for BRCM 27XX Standard host controller */ | |
94 | if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) | |
0f0881b0 | 95 | return true; |
cf2b4488 HP |
96 | |
97 | /* Check for BRCM Standard host controller */ | |
98 | if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) | |
0f0881b0 | 99 | return true; |
cf2b4488 HP |
100 | |
101 | /* Check for TI PCIxx21 Standard host controller */ | |
102 | if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) | |
0f0881b0 | 103 | return true; |
cf2b4488 HP |
104 | |
105 | if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) | |
0f0881b0 | 106 | return true; |
cf2b4488 HP |
107 | |
108 | /* Ricoh R5C822 Standard SDIO Host */ | |
109 | if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) | |
0f0881b0 | 110 | return true; |
cf2b4488 HP |
111 | |
112 | /* JMicron Standard SDIO Host */ | |
113 | if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) | |
0f0881b0 | 114 | return true; |
cf2b4488 HP |
115 | #endif /* BCMSDIOH_STD */ |
116 | #ifdef BCMSDIOH_SPI | |
117 | /* This is the PciSpiHost. */ | |
118 | if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) { | |
119 | printf("Found PCI SPI Host Controller\n"); | |
0f0881b0 | 120 | return true; |
cf2b4488 HP |
121 | } |
122 | #endif /* BCMSDIOH_SPI */ | |
123 | ||
0965ae88 | 124 | return false; |
cf2b4488 HP |
125 | } |
126 | ||
127 | #if defined(BCMPLATFORM_BUS) | |
128 | #if defined(BCMLXSDMMC) | |
129 | /* forward declarations */ | |
130 | int bcmsdh_probe(struct device *dev); | |
131 | EXPORT_SYMBOL(bcmsdh_probe); | |
132 | ||
133 | int bcmsdh_remove(struct device *dev); | |
134 | EXPORT_SYMBOL(bcmsdh_remove); | |
135 | ||
136 | #else | |
137 | /* forward declarations */ | |
138 | static int __devinit bcmsdh_probe(struct device *dev); | |
139 | static int __devexit bcmsdh_remove(struct device *dev); | |
140 | #endif /* BCMLXSDMMC */ | |
141 | ||
142 | #ifndef 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, | |
148 | .suspend = NULL, | |
149 | .resume = NULL, | |
150 | }; | |
151 | #endif /* BCMLXSDMMC */ | |
152 | ||
153 | #ifndef BCMLXSDMMC | |
154 | static | |
155 | #endif /* BCMLXSDMMC */ | |
156 | int bcmsdh_probe(struct device *dev) | |
157 | { | |
158 | osl_t *osh = NULL; | |
159 | bcmsdh_hc_t *sdhc = NULL; | |
3deea904 | 160 | unsigned long regs = 0; |
cf2b4488 HP |
161 | bcmsdh_info_t *sdh = NULL; |
162 | #if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) | |
163 | struct platform_device *pdev; | |
164 | struct resource *r; | |
165 | #endif /* BCMLXSDMMC */ | |
166 | int irq = 0; | |
66cbd3ab | 167 | u32 vendevid; |
cf2b4488 HP |
168 | unsigned long irq_flags = 0; |
169 | ||
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) | |
175 | return -ENXIO; | |
176 | #endif /* BCMLXSDMMC */ | |
177 | ||
178 | #if defined(OOB_INTR_ONLY) | |
179 | #ifdef HW_OOB | |
180 | irq_flags = | |
181 | IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | | |
182 | IORESOURCE_IRQ_SHAREABLE; | |
183 | #else | |
184 | irq_flags = IRQF_TRIGGER_FALLING; | |
185 | #endif /* HW_OOB */ | |
186 | irq = dhd_customer_oob_irq_map(&irq_flags); | |
187 | if (irq < 0) { | |
188 | SDLX_MSG(("%s: Host irq is not defined\n", __func__)); | |
189 | return 1; | |
190 | } | |
191 | #endif /* defined(OOB_INTR_ONLY) */ | |
192 | /* allocate SDIO Host Controller state info */ | |
0965ae88 | 193 | osh = osl_attach(dev, PCI_BUS, false); |
81e95f9d | 194 | if (!osh) { |
cf2b4488 HP |
195 | SDLX_MSG(("%s: osl_attach failed\n", __func__)); |
196 | goto err; | |
197 | } | |
5fcc1fcb | 198 | sdhc = kzalloc(sizeof(bcmsdh_hc_t), GFP_ATOMIC); |
81e95f9d | 199 | if (!sdhc) { |
97e17d0e | 200 | SDLX_MSG(("%s: out of memory\n", __func__)); |
cf2b4488 HP |
201 | goto err; |
202 | } | |
cf2b4488 HP |
203 | sdhc->osh = osh; |
204 | ||
205 | sdhc->dev = (void *)dev; | |
206 | ||
207 | #ifdef BCMLXSDMMC | |
81e95f9d JC |
208 | sdh = bcmsdh_attach(osh, (void *)0, (void **)®s, irq); |
209 | if (!sdh) { | |
cf2b4488 HP |
210 | SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__)); |
211 | goto err; | |
212 | } | |
213 | #else | |
81e95f9d JC |
214 | sdh = bcmsdh_attach(osh, (void *)r->start, (void **)®s, irq); |
215 | if (!sdh) { | |
cf2b4488 HP |
216 | SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__)); |
217 | goto err; | |
218 | } | |
219 | #endif /* BCMLXSDMMC */ | |
220 | sdhc->sdh = sdh; | |
221 | sdhc->oob_irq = irq; | |
222 | sdhc->oob_flags = irq_flags; | |
0965ae88 | 223 | sdhc->oob_irq_registered = false; /* to make sure.. */ |
cf2b4488 HP |
224 | #if defined(OOB_INTR_ONLY) |
225 | spin_lock_init(&sdhc->irq_lock); | |
226 | #endif | |
227 | ||
228 | /* chain SDIO Host Controller info together */ | |
229 | sdhc->next = sdhcinfo; | |
230 | sdhcinfo = sdhc; | |
231 | /* Read the vendor/device ID from the CIS */ | |
232 | vendevid = bcmsdh_query_device(sdh); | |
233 | ||
234 | /* try to attach to the target device */ | |
81e95f9d JC |
235 | sdhc->ch = drvinfo.attach((vendevid >> 16), (vendevid & 0xFFFF), |
236 | 0, 0, 0, 0, (void *)regs, NULL, sdh); | |
237 | if (!sdhc->ch) { | |
cf2b4488 HP |
238 | SDLX_MSG(("%s: device attach failed\n", __func__)); |
239 | goto err; | |
240 | } | |
241 | ||
242 | return 0; | |
243 | ||
244 | /* error handling */ | |
245 | err: | |
246 | if (sdhc) { | |
247 | if (sdhc->sdh) | |
248 | bcmsdh_detach(sdhc->osh, sdhc->sdh); | |
182acb3c | 249 | kfree(sdhc); |
cf2b4488 HP |
250 | } |
251 | if (osh) | |
252 | osl_detach(osh); | |
253 | return -ENODEV; | |
254 | } | |
255 | ||
256 | #ifndef BCMLXSDMMC | |
257 | static | |
258 | #endif /* BCMLXSDMMC */ | |
259 | int bcmsdh_remove(struct device *dev) | |
260 | { | |
261 | bcmsdh_hc_t *sdhc, *prev; | |
262 | osl_t *osh; | |
263 | ||
264 | sdhc = sdhcinfo; | |
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) { | |
271 | if (prev) | |
272 | prev->next = sdhc->next; | |
273 | else | |
274 | sdhcinfo = NULL; | |
275 | break; | |
276 | } | |
277 | prev = sdhc; | |
278 | } | |
279 | if (!sdhc) { | |
280 | SDLX_MSG(("%s: failed\n", __func__)); | |
281 | return 0; | |
282 | } | |
283 | ||
284 | /* release SDIO Host Controller info */ | |
285 | osh = sdhc->osh; | |
182acb3c | 286 | kfree(sdhc); |
cf2b4488 HP |
287 | osl_detach(osh); |
288 | ||
289 | #if !defined(BCMLXSDMMC) | |
290 | dev_set_drvdata(dev, NULL); | |
291 | #endif /* !defined(BCMLXSDMMC) */ | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
296 | #else /* BCMPLATFORM_BUS */ | |
297 | ||
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); | |
303 | ||
304 | /** | |
305 | * pci id table | |
306 | */ | |
307 | static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = { | |
308 | { | |
309 | .vendor = PCI_ANY_ID, | |
310 | .device = PCI_ANY_ID, | |
311 | .subvendor = PCI_ANY_ID, | |
312 | .subdevice = PCI_ANY_ID, | |
313 | .class = 0, | |
314 | .class_mask = 0, | |
315 | .driver_data = 0, | |
316 | }, | |
317 | {0,} | |
318 | }; | |
319 | ||
320 | MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid); | |
321 | ||
322 | /** | |
323 | * SDIO Host Controller pci driver info | |
324 | */ | |
325 | static struct pci_driver bcmsdh_pci_driver = { | |
326 | .node = {}, | |
327 | .name = "bcmsdh", | |
328 | .id_table = bcmsdh_pci_devid, | |
329 | .probe = bcmsdh_pci_probe, | |
330 | .remove = bcmsdh_pci_remove, | |
331 | .suspend = NULL, | |
332 | .resume = NULL, | |
333 | }; | |
334 | ||
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 */ | |
343 | /* off */ | |
344 | module_param(sd_pci_slot, uint, 0); | |
345 | ||
346 | /** | |
347 | * Detect supported SDIO Host Controller and attach if found. | |
348 | * | |
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. | |
351 | */ | |
352 | static int __devinit | |
353 | bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |
354 | { | |
355 | osl_t *osh = NULL; | |
356 | bcmsdh_hc_t *sdhc = NULL; | |
3deea904 | 357 | unsigned long regs; |
cf2b4488 HP |
358 | bcmsdh_info_t *sdh = NULL; |
359 | int rc; | |
360 | ||
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", | |
365 | __func__, | |
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)); | |
371 | return -ENODEV; | |
372 | } | |
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 " | |
377 | "of unkown device", | |
378 | pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, | |
379 | pdev->device)); | |
380 | } | |
381 | ||
382 | if ((pdev->vendor == VENDOR_TI) | |
383 | && ((pdev->device == PCIXX21_FLASHMEDIA_ID) | |
384 | || (pdev->device == PCIXX21_FLASHMEDIA0_ID))) { | |
66cbd3ab | 385 | u32 config_reg; |
cf2b4488 HP |
386 | |
387 | SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n", | |
388 | __func__)); | |
0965ae88 | 389 | osh = osl_attach(pdev, PCI_BUS, false); |
81e95f9d | 390 | if (!osh) { |
cf2b4488 HP |
391 | SDLX_MSG(("%s: osl_attach failed\n", __func__)); |
392 | goto err; | |
393 | } | |
394 | ||
395 | config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4); | |
396 | ||
397 | /* | |
398 | * Set MMC_SD_DIS bit in FlashMedia Controller. | |
399 | * Disbling the SD/MMC Controller in the FlashMedia Controller | |
400 | * allows the Standard SD Host Controller to take over control | |
401 | * of the SD Slot. | |
402 | */ | |
403 | config_reg |= 0x02; | |
404 | OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg); | |
405 | osl_detach(osh); | |
406 | } | |
407 | /* match this pci device with what we support */ | |
408 | /* we can't solely rely on this to believe it is | |
409 | our SDIO Host Controller! */ | |
410 | if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) | |
411 | return -ENODEV; | |
412 | ||
413 | /* this is a pci device we might support */ | |
414 | SDLX_MSG(("%s: Found possible SDIO Host Controller: " | |
415 | "bus %d slot %d func %d irq %d\n", __func__, | |
416 | pdev->bus->number, PCI_SLOT(pdev->devfn), | |
417 | PCI_FUNC(pdev->devfn), pdev->irq)); | |
418 | ||
419 | /* use bcmsdh_query_device() to get the vendor ID of the target device | |
420 | * so it will eventually appear in the Broadcom string on the console | |
421 | */ | |
422 | ||
423 | /* allocate SDIO Host Controller state info */ | |
0965ae88 | 424 | osh = osl_attach(pdev, PCI_BUS, false); |
81e95f9d | 425 | if (!osh) { |
cf2b4488 HP |
426 | SDLX_MSG(("%s: osl_attach failed\n", __func__)); |
427 | goto err; | |
428 | } | |
5fcc1fcb | 429 | sdhc = kzalloc(sizeof(bcmsdh_hc_t), GFP_ATOMIC); |
81e95f9d | 430 | if (!sdhc) { |
97e17d0e | 431 | SDLX_MSG(("%s: out of memory\n", __func__)); |
cf2b4488 HP |
432 | goto err; |
433 | } | |
cf2b4488 HP |
434 | sdhc->osh = osh; |
435 | ||
436 | sdhc->dev = pdev; | |
437 | ||
438 | /* map to address where host can access */ | |
439 | pci_set_master(pdev); | |
440 | rc = pci_enable_device(pdev); | |
441 | if (rc) { | |
442 | SDLX_MSG(("%s: Cannot enable PCI device\n", __func__)); | |
443 | goto err; | |
444 | } | |
81e95f9d JC |
445 | sdh = bcmsdh_attach(osh, (void *)(uintptr) pci_resource_start(pdev, 0), |
446 | (void **)®s, pdev->irq); | |
447 | if (!sdh) { | |
cf2b4488 HP |
448 | SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__)); |
449 | goto err; | |
450 | } | |
451 | ||
452 | sdhc->sdh = sdh; | |
453 | ||
454 | /* try to attach to the target device */ | |
81e95f9d JC |
455 | sdhc->ch = drvinfo.attach(VENDOR_BROADCOM, /* pdev->vendor, */ |
456 | bcmsdh_query_device(sdh) & 0xFFFF, 0, 0, 0, 0, | |
457 | (void *)regs, NULL, sdh); | |
458 | if (!sdhc->ch) { | |
cf2b4488 HP |
459 | SDLX_MSG(("%s: device attach failed\n", __func__)); |
460 | goto err; | |
461 | } | |
462 | ||
463 | /* chain SDIO Host Controller info together */ | |
464 | sdhc->next = sdhcinfo; | |
465 | sdhcinfo = sdhc; | |
466 | ||
467 | return 0; | |
468 | ||
469 | /* error handling */ | |
470 | err: | |
471 | if (sdhc->sdh) | |
472 | bcmsdh_detach(sdhc->osh, sdhc->sdh); | |
473 | if (sdhc) | |
182acb3c | 474 | kfree(sdhc); |
cf2b4488 HP |
475 | if (osh) |
476 | osl_detach(osh); | |
477 | return -ENODEV; | |
478 | } | |
479 | ||
480 | /** | |
481 | * Detach from target devices and SDIO Host Controller | |
482 | */ | |
483 | static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev) | |
484 | { | |
485 | bcmsdh_hc_t *sdhc, *prev; | |
486 | osl_t *osh; | |
487 | ||
488 | /* find the SDIO Host Controller state for this | |
489 | pdev and take it out from the list */ | |
490 | for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { | |
491 | if (sdhc->dev == pdev) { | |
492 | if (prev) | |
493 | prev->next = sdhc->next; | |
494 | else | |
495 | sdhcinfo = NULL; | |
496 | break; | |
497 | } | |
498 | prev = sdhc; | |
499 | } | |
500 | if (!sdhc) | |
501 | return; | |
502 | ||
503 | drvinfo.detach(sdhc->ch); | |
504 | ||
505 | bcmsdh_detach(sdhc->osh, sdhc->sdh); | |
506 | ||
507 | /* release SDIO Host Controller info */ | |
508 | osh = sdhc->osh; | |
182acb3c | 509 | kfree(sdhc); |
cf2b4488 HP |
510 | osl_detach(osh); |
511 | } | |
512 | #endif /* BCMLXSDMMC */ | |
513 | #endif /* BCMPLATFORM_BUS */ | |
514 | ||
515 | extern int sdio_function_init(void); | |
516 | ||
517 | int bcmsdh_register(bcmsdh_driver_t *driver) | |
518 | { | |
519 | int error = 0; | |
520 | ||
521 | drvinfo = *driver; | |
522 | ||
523 | #if defined(BCMPLATFORM_BUS) | |
524 | #if defined(BCMLXSDMMC) | |
525 | SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n")); | |
526 | error = sdio_function_init(); | |
527 | #else | |
528 | SDLX_MSG(("Intel PXA270 SDIO Driver\n")); | |
529 | error = driver_register(&bcmsdh_driver); | |
530 | #endif /* defined(BCMLXSDMMC) */ | |
531 | return error; | |
532 | #endif /* defined(BCMPLATFORM_BUS) */ | |
533 | ||
534 | #if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) | |
81e95f9d JC |
535 | error = pci_register_driver(&bcmsdh_pci_driver); |
536 | if (!error) | |
cf2b4488 HP |
537 | return 0; |
538 | ||
8ba9cfdb | 539 | SDLX_MSG(("%s: pci_register_driver failed 0x%x\n", __func__, error)); |
cf2b4488 HP |
540 | #endif /* BCMPLATFORM_BUS */ |
541 | ||
542 | return error; | |
543 | } | |
544 | ||
545 | extern void sdio_function_cleanup(void); | |
546 | ||
547 | void bcmsdh_unregister(void) | |
548 | { | |
549 | #if defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) | |
550 | driver_unregister(&bcmsdh_driver); | |
551 | #endif | |
552 | #if defined(BCMLXSDMMC) | |
553 | sdio_function_cleanup(); | |
554 | #endif /* BCMLXSDMMC */ | |
555 | #if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) | |
556 | pci_unregister_driver(&bcmsdh_pci_driver); | |
557 | #endif /* BCMPLATFORM_BUS */ | |
558 | } | |
559 | ||
560 | #if defined(OOB_INTR_ONLY) | |
561 | void bcmsdh_oob_intr_set(bool enable) | |
562 | { | |
563 | static bool curstate = 1; | |
564 | unsigned long flags; | |
565 | ||
566 | spin_lock_irqsave(&sdhcinfo->irq_lock, flags); | |
567 | if (curstate != enable) { | |
568 | if (enable) | |
569 | enable_irq(sdhcinfo->oob_irq); | |
570 | else | |
571 | disable_irq_nosync(sdhcinfo->oob_irq); | |
572 | curstate = enable; | |
573 | } | |
574 | spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags); | |
575 | } | |
576 | ||
577 | static irqreturn_t wlan_oob_irq(int irq, void *dev_id) | |
578 | { | |
579 | dhd_pub_t *dhdp; | |
580 | ||
581 | dhdp = (dhd_pub_t *) dev_get_drvdata(sdhcinfo->dev); | |
582 | ||
583 | bcmsdh_oob_intr_set(0); | |
584 | ||
585 | if (dhdp == NULL) { | |
586 | SDLX_MSG(("Out of band GPIO interrupt fired way too early\n")); | |
587 | return IRQ_HANDLED; | |
588 | } | |
589 | ||
590 | WAKE_LOCK_TIMEOUT(dhdp, WAKE_LOCK_TMOUT, 25); | |
591 | ||
592 | dhdsdio_isr((void *)dhdp->bus); | |
593 | ||
594 | return IRQ_HANDLED; | |
595 | } | |
596 | ||
597 | int bcmsdh_register_oob_intr(void *dhdp) | |
598 | { | |
599 | int error = 0; | |
600 | ||
601 | SDLX_MSG(("%s Enter\n", __func__)); | |
602 | ||
603 | sdhcinfo->oob_flags = | |
604 | IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | | |
605 | IORESOURCE_IRQ_SHAREABLE; | |
606 | dev_set_drvdata(sdhcinfo->dev, dhdp); | |
607 | ||
608 | if (!sdhcinfo->oob_irq_registered) { | |
609 | SDLX_MSG(("%s IRQ=%d Type=%X\n", __func__, | |
610 | (int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags)); | |
611 | /* Refer to customer Host IRQ docs about | |
612 | proper irqflags definition */ | |
613 | error = | |
614 | request_irq(sdhcinfo->oob_irq, wlan_oob_irq, | |
615 | sdhcinfo->oob_flags, "bcmsdh_sdmmc", NULL); | |
616 | if (error) | |
617 | return -ENODEV; | |
618 | ||
619 | set_irq_wake(sdhcinfo->oob_irq, 1); | |
0f0881b0 | 620 | sdhcinfo->oob_irq_registered = true; |
cf2b4488 HP |
621 | } |
622 | ||
623 | return 0; | |
624 | } | |
625 | ||
626 | void bcmsdh_unregister_oob_intr(void) | |
627 | { | |
628 | SDLX_MSG(("%s: Enter\n", __func__)); | |
629 | ||
630 | set_irq_wake(sdhcinfo->oob_irq, 0); | |
631 | disable_irq(sdhcinfo->oob_irq); /* just in case.. */ | |
632 | free_irq(sdhcinfo->oob_irq, NULL); | |
0965ae88 | 633 | sdhcinfo->oob_irq_registered = false; |
cf2b4488 HP |
634 | } |
635 | #endif /* defined(OOB_INTR_ONLY) */ | |
636 | /* Module parameters specific to each host-controller driver */ | |
637 | ||
638 | extern uint sd_msglevel; /* Debug message level */ | |
639 | module_param(sd_msglevel, uint, 0); | |
640 | ||
641 | extern uint sd_power; /* 0 = SD Power OFF, | |
642 | 1 = SD Power ON. */ | |
643 | module_param(sd_power, uint, 0); | |
644 | ||
645 | extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, | |
646 | 1 = SD Clock ON */ | |
647 | module_param(sd_clock, uint, 0); | |
648 | ||
649 | extern uint sd_divisor; /* Divisor (-1 means external clock) */ | |
650 | module_param(sd_divisor, uint, 0); | |
651 | ||
652 | extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */ | |
653 | module_param(sd_sdmode, uint, 0); | |
654 | ||
655 | extern uint sd_hiok; /* Ok to use hi-speed mode */ | |
656 | module_param(sd_hiok, uint, 0); | |
657 | ||
658 | extern uint sd_f2_blocksize; | |
659 | module_param(sd_f2_blocksize, int, 0); |