diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-pci.c')
-rw-r--r-- | drivers/mmc/host/sdhci-pci.c | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c new file mode 100644 index 000000000000..deb607c52c0d --- /dev/null +++ b/drivers/mmc/host/sdhci-pci.c | |||
@@ -0,0 +1,732 @@ | |||
1 | /* linux/drivers/mmc/host/sdhci-pci.c - SDHCI on PCI bus interface | ||
2 | * | ||
3 | * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or (at | ||
8 | * your option) any later version. | ||
9 | * | ||
10 | * Thanks to the following companies for their support: | ||
11 | * | ||
12 | * - JMicron (hardware and technical support) | ||
13 | */ | ||
14 | |||
15 | #include <linux/delay.h> | ||
16 | #include <linux/highmem.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/dma-mapping.h> | ||
19 | |||
20 | #include <linux/mmc/host.h> | ||
21 | |||
22 | #include <asm/scatterlist.h> | ||
23 | #include <asm/io.h> | ||
24 | |||
25 | #include "sdhci.h" | ||
26 | |||
27 | /* | ||
28 | * PCI registers | ||
29 | */ | ||
30 | |||
31 | #define PCI_SDHCI_IFPIO 0x00 | ||
32 | #define PCI_SDHCI_IFDMA 0x01 | ||
33 | #define PCI_SDHCI_IFVENDOR 0x02 | ||
34 | |||
35 | #define PCI_SLOT_INFO 0x40 /* 8 bits */ | ||
36 | #define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7) | ||
37 | #define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07 | ||
38 | |||
39 | #define MAX_SLOTS 8 | ||
40 | |||
41 | struct sdhci_pci_chip; | ||
42 | struct sdhci_pci_slot; | ||
43 | |||
44 | struct sdhci_pci_fixes { | ||
45 | unsigned int quirks; | ||
46 | |||
47 | int (*probe)(struct sdhci_pci_chip*); | ||
48 | |||
49 | int (*probe_slot)(struct sdhci_pci_slot*); | ||
50 | void (*remove_slot)(struct sdhci_pci_slot*, int); | ||
51 | |||
52 | int (*suspend)(struct sdhci_pci_chip*, | ||
53 | pm_message_t); | ||
54 | int (*resume)(struct sdhci_pci_chip*); | ||
55 | }; | ||
56 | |||
57 | struct sdhci_pci_slot { | ||
58 | struct sdhci_pci_chip *chip; | ||
59 | struct sdhci_host *host; | ||
60 | |||
61 | int pci_bar; | ||
62 | }; | ||
63 | |||
64 | struct sdhci_pci_chip { | ||
65 | struct pci_dev *pdev; | ||
66 | |||
67 | unsigned int quirks; | ||
68 | const struct sdhci_pci_fixes *fixes; | ||
69 | |||
70 | int num_slots; /* Slots on controller */ | ||
71 | struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */ | ||
72 | }; | ||
73 | |||
74 | |||
75 | /*****************************************************************************\ | ||
76 | * * | ||
77 | * Hardware specific quirk handling * | ||
78 | * * | ||
79 | \*****************************************************************************/ | ||
80 | |||
81 | static int ricoh_probe(struct sdhci_pci_chip *chip) | ||
82 | { | ||
83 | if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) | ||
84 | chip->quirks |= SDHCI_QUIRK_CLOCK_BEFORE_RESET; | ||
85 | |||
86 | if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG) | ||
87 | chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static const struct sdhci_pci_fixes sdhci_ricoh = { | ||
93 | .probe = ricoh_probe, | ||
94 | .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR, | ||
95 | }; | ||
96 | |||
97 | static const struct sdhci_pci_fixes sdhci_ene_712 = { | ||
98 | .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | | ||
99 | SDHCI_QUIRK_BROKEN_DMA, | ||
100 | }; | ||
101 | |||
102 | static const struct sdhci_pci_fixes sdhci_ene_714 = { | ||
103 | .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | | ||
104 | SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | | ||
105 | SDHCI_QUIRK_BROKEN_DMA, | ||
106 | }; | ||
107 | |||
108 | static const struct sdhci_pci_fixes sdhci_cafe = { | ||
109 | .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | | ||
110 | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, | ||
111 | }; | ||
112 | |||
113 | static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) | ||
114 | { | ||
115 | u8 scratch; | ||
116 | int ret; | ||
117 | |||
118 | ret = pci_read_config_byte(chip->pdev, 0xAE, &scratch); | ||
119 | if (ret) | ||
120 | return ret; | ||
121 | |||
122 | /* | ||
123 | * Turn PMOS on [bit 0], set over current detection to 2.4 V | ||
124 | * [bit 1:2] and enable over current debouncing [bit 6]. | ||
125 | */ | ||
126 | if (on) | ||
127 | scratch |= 0x47; | ||
128 | else | ||
129 | scratch &= ~0x47; | ||
130 | |||
131 | ret = pci_write_config_byte(chip->pdev, 0xAE, scratch); | ||
132 | if (ret) | ||
133 | return ret; | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int jmicron_probe(struct sdhci_pci_chip *chip) | ||
139 | { | ||
140 | int ret; | ||
141 | |||
142 | if (chip->pdev->revision == 0) { | ||
143 | chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR | | ||
144 | SDHCI_QUIRK_32BIT_DMA_SIZE | | ||
145 | SDHCI_QUIRK_32BIT_ADMA_SIZE | | ||
146 | SDHCI_QUIRK_RESET_AFTER_REQUEST; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * JMicron chips can have two interfaces to the same hardware | ||
151 | * in order to work around limitations in Microsoft's driver. | ||
152 | * We need to make sure we only bind to one of them. | ||
153 | * | ||
154 | * This code assumes two things: | ||
155 | * | ||
156 | * 1. The PCI code adds subfunctions in order. | ||
157 | * | ||
158 | * 2. The MMC interface has a lower subfunction number | ||
159 | * than the SD interface. | ||
160 | */ | ||
161 | if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) { | ||
162 | struct pci_dev *sd_dev; | ||
163 | |||
164 | sd_dev = NULL; | ||
165 | while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, | ||
166 | PCI_DEVICE_ID_JMICRON_JMB38X_MMC, sd_dev)) != NULL) { | ||
167 | if ((PCI_SLOT(chip->pdev->devfn) == | ||
168 | PCI_SLOT(sd_dev->devfn)) && | ||
169 | (chip->pdev->bus == sd_dev->bus)) | ||
170 | break; | ||
171 | } | ||
172 | |||
173 | if (sd_dev) { | ||
174 | pci_dev_put(sd_dev); | ||
175 | dev_info(&chip->pdev->dev, "Refusing to bind to " | ||
176 | "secondary interface.\n"); | ||
177 | return -ENODEV; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * JMicron chips need a bit of a nudge to enable the power | ||
183 | * output pins. | ||
184 | */ | ||
185 | ret = jmicron_pmos(chip, 1); | ||
186 | if (ret) { | ||
187 | dev_err(&chip->pdev->dev, "Failure enabling card power\n"); | ||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static void jmicron_enable_mmc(struct sdhci_host *host, int on) | ||
195 | { | ||
196 | u8 scratch; | ||
197 | |||
198 | scratch = readb(host->ioaddr + 0xC0); | ||
199 | |||
200 | if (on) | ||
201 | scratch |= 0x01; | ||
202 | else | ||
203 | scratch &= ~0x01; | ||
204 | |||
205 | writeb(scratch, host->ioaddr + 0xC0); | ||
206 | } | ||
207 | |||
208 | static int jmicron_probe_slot(struct sdhci_pci_slot *slot) | ||
209 | { | ||
210 | if (slot->chip->pdev->revision == 0) { | ||
211 | u16 version; | ||
212 | |||
213 | version = readl(slot->host->ioaddr + SDHCI_HOST_VERSION); | ||
214 | version = (version & SDHCI_VENDOR_VER_MASK) >> | ||
215 | SDHCI_VENDOR_VER_SHIFT; | ||
216 | |||
217 | /* | ||
218 | * Older versions of the chip have lots of nasty glitches | ||
219 | * in the ADMA engine. It's best just to avoid it | ||
220 | * completely. | ||
221 | */ | ||
222 | if (version < 0xAC) | ||
223 | slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * The secondary interface requires a bit set to get the | ||
228 | * interrupts. | ||
229 | */ | ||
230 | if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) | ||
231 | jmicron_enable_mmc(slot->host, 1); | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead) | ||
237 | { | ||
238 | if (dead) | ||
239 | return; | ||
240 | |||
241 | if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) | ||
242 | jmicron_enable_mmc(slot->host, 0); | ||
243 | } | ||
244 | |||
245 | static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state) | ||
246 | { | ||
247 | int i; | ||
248 | |||
249 | if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { | ||
250 | for (i = 0;i < chip->num_slots;i++) | ||
251 | jmicron_enable_mmc(chip->slots[i]->host, 0); | ||
252 | } | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static int jmicron_resume(struct sdhci_pci_chip *chip) | ||
258 | { | ||
259 | int ret, i; | ||
260 | |||
261 | if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { | ||
262 | for (i = 0;i < chip->num_slots;i++) | ||
263 | jmicron_enable_mmc(chip->slots[i]->host, 1); | ||
264 | } | ||
265 | |||
266 | ret = jmicron_pmos(chip, 1); | ||
267 | if (ret) { | ||
268 | dev_err(&chip->pdev->dev, "Failure enabling card power\n"); | ||
269 | return ret; | ||
270 | } | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static const struct sdhci_pci_fixes sdhci_jmicron = { | ||
276 | .probe = jmicron_probe, | ||
277 | |||
278 | .probe_slot = jmicron_probe_slot, | ||
279 | .remove_slot = jmicron_remove_slot, | ||
280 | |||
281 | .suspend = jmicron_suspend, | ||
282 | .resume = jmicron_resume, | ||
283 | }; | ||
284 | |||
285 | static const struct pci_device_id pci_ids[] __devinitdata = { | ||
286 | { | ||
287 | .vendor = PCI_VENDOR_ID_RICOH, | ||
288 | .device = PCI_DEVICE_ID_RICOH_R5C822, | ||
289 | .subvendor = PCI_ANY_ID, | ||
290 | .subdevice = PCI_ANY_ID, | ||
291 | .driver_data = (kernel_ulong_t)&sdhci_ricoh, | ||
292 | }, | ||
293 | |||
294 | { | ||
295 | .vendor = PCI_VENDOR_ID_ENE, | ||
296 | .device = PCI_DEVICE_ID_ENE_CB712_SD, | ||
297 | .subvendor = PCI_ANY_ID, | ||
298 | .subdevice = PCI_ANY_ID, | ||
299 | .driver_data = (kernel_ulong_t)&sdhci_ene_712, | ||
300 | }, | ||
301 | |||
302 | { | ||
303 | .vendor = PCI_VENDOR_ID_ENE, | ||
304 | .device = PCI_DEVICE_ID_ENE_CB712_SD_2, | ||
305 | .subvendor = PCI_ANY_ID, | ||
306 | .subdevice = PCI_ANY_ID, | ||
307 | .driver_data = (kernel_ulong_t)&sdhci_ene_712, | ||
308 | }, | ||
309 | |||
310 | { | ||
311 | .vendor = PCI_VENDOR_ID_ENE, | ||
312 | .device = PCI_DEVICE_ID_ENE_CB714_SD, | ||
313 | .subvendor = PCI_ANY_ID, | ||
314 | .subdevice = PCI_ANY_ID, | ||
315 | .driver_data = (kernel_ulong_t)&sdhci_ene_714, | ||
316 | }, | ||
317 | |||
318 | { | ||
319 | .vendor = PCI_VENDOR_ID_ENE, | ||
320 | .device = PCI_DEVICE_ID_ENE_CB714_SD_2, | ||
321 | .subvendor = PCI_ANY_ID, | ||
322 | .subdevice = PCI_ANY_ID, | ||
323 | .driver_data = (kernel_ulong_t)&sdhci_ene_714, | ||
324 | }, | ||
325 | |||
326 | { | ||
327 | .vendor = PCI_VENDOR_ID_MARVELL, | ||
328 | .device = PCI_DEVICE_ID_MARVELL_CAFE_SD, | ||
329 | .subvendor = PCI_ANY_ID, | ||
330 | .subdevice = PCI_ANY_ID, | ||
331 | .driver_data = (kernel_ulong_t)&sdhci_cafe, | ||
332 | }, | ||
333 | |||
334 | { | ||
335 | .vendor = PCI_VENDOR_ID_JMICRON, | ||
336 | .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD, | ||
337 | .subvendor = PCI_ANY_ID, | ||
338 | .subdevice = PCI_ANY_ID, | ||
339 | .driver_data = (kernel_ulong_t)&sdhci_jmicron, | ||
340 | }, | ||
341 | |||
342 | { | ||
343 | .vendor = PCI_VENDOR_ID_JMICRON, | ||
344 | .device = PCI_DEVICE_ID_JMICRON_JMB38X_MMC, | ||
345 | .subvendor = PCI_ANY_ID, | ||
346 | .subdevice = PCI_ANY_ID, | ||
347 | .driver_data = (kernel_ulong_t)&sdhci_jmicron, | ||
348 | }, | ||
349 | |||
350 | { /* Generic SD host controller */ | ||
351 | PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) | ||
352 | }, | ||
353 | |||
354 | { /* end: all zeroes */ }, | ||
355 | }; | ||
356 | |||
357 | MODULE_DEVICE_TABLE(pci, pci_ids); | ||
358 | |||
359 | /*****************************************************************************\ | ||
360 | * * | ||
361 | * SDHCI core callbacks * | ||
362 | * * | ||
363 | \*****************************************************************************/ | ||
364 | |||
365 | static int sdhci_pci_enable_dma(struct sdhci_host *host) | ||
366 | { | ||
367 | struct sdhci_pci_slot *slot; | ||
368 | struct pci_dev *pdev; | ||
369 | int ret; | ||
370 | |||
371 | slot = sdhci_priv(host); | ||
372 | pdev = slot->chip->pdev; | ||
373 | |||
374 | if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) && | ||
375 | ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && | ||
376 | (host->flags & SDHCI_USE_DMA)) { | ||
377 | dev_warn(&pdev->dev, "Will use DMA mode even though HW " | ||
378 | "doesn't fully claim to support it.\n"); | ||
379 | } | ||
380 | |||
381 | ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); | ||
382 | if (ret) | ||
383 | return ret; | ||
384 | |||
385 | pci_set_master(pdev); | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static struct sdhci_ops sdhci_pci_ops = { | ||
391 | .enable_dma = sdhci_pci_enable_dma, | ||
392 | }; | ||
393 | |||
394 | /*****************************************************************************\ | ||
395 | * * | ||
396 | * Suspend/resume * | ||
397 | * * | ||
398 | \*****************************************************************************/ | ||
399 | |||
400 | #ifdef CONFIG_PM | ||
401 | |||
402 | static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) | ||
403 | { | ||
404 | struct sdhci_pci_chip *chip; | ||
405 | struct sdhci_pci_slot *slot; | ||
406 | int i, ret; | ||
407 | |||
408 | chip = pci_get_drvdata(pdev); | ||
409 | if (!chip) | ||
410 | return 0; | ||
411 | |||
412 | for (i = 0;i < chip->num_slots;i++) { | ||
413 | slot = chip->slots[i]; | ||
414 | if (!slot) | ||
415 | continue; | ||
416 | |||
417 | ret = sdhci_suspend_host(slot->host, state); | ||
418 | |||
419 | if (ret) { | ||
420 | for (i--;i >= 0;i--) | ||
421 | sdhci_resume_host(chip->slots[i]->host); | ||
422 | return ret; | ||
423 | } | ||
424 | } | ||
425 | |||
426 | if (chip->fixes && chip->fixes->suspend) { | ||
427 | ret = chip->fixes->suspend(chip, state); | ||
428 | if (ret) { | ||
429 | for (i = chip->num_slots - 1;i >= 0;i--) | ||
430 | sdhci_resume_host(chip->slots[i]->host); | ||
431 | return ret; | ||
432 | } | ||
433 | } | ||
434 | |||
435 | pci_save_state(pdev); | ||
436 | pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); | ||
437 | pci_disable_device(pdev); | ||
438 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | static int sdhci_pci_resume (struct pci_dev *pdev) | ||
444 | { | ||
445 | struct sdhci_pci_chip *chip; | ||
446 | struct sdhci_pci_slot *slot; | ||
447 | int i, ret; | ||
448 | |||
449 | chip = pci_get_drvdata(pdev); | ||
450 | if (!chip) | ||
451 | return 0; | ||
452 | |||
453 | pci_set_power_state(pdev, PCI_D0); | ||
454 | pci_restore_state(pdev); | ||
455 | ret = pci_enable_device(pdev); | ||
456 | if (ret) | ||
457 | return ret; | ||
458 | |||
459 | if (chip->fixes && chip->fixes->resume) { | ||
460 | ret = chip->fixes->resume(chip); | ||
461 | if (ret) | ||
462 | return ret; | ||
463 | } | ||
464 | |||
465 | for (i = 0;i < chip->num_slots;i++) { | ||
466 | slot = chip->slots[i]; | ||
467 | if (!slot) | ||
468 | continue; | ||
469 | |||
470 | ret = sdhci_resume_host(slot->host); | ||
471 | if (ret) | ||
472 | return ret; | ||
473 | } | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | #else /* CONFIG_PM */ | ||
479 | |||
480 | #define sdhci_pci_suspend NULL | ||
481 | #define sdhci_pci_resume NULL | ||
482 | |||
483 | #endif /* CONFIG_PM */ | ||
484 | |||
485 | /*****************************************************************************\ | ||
486 | * * | ||
487 | * Device probing/removal * | ||
488 | * * | ||
489 | \*****************************************************************************/ | ||
490 | |||
491 | static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( | ||
492 | struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar) | ||
493 | { | ||
494 | struct sdhci_pci_slot *slot; | ||
495 | struct sdhci_host *host; | ||
496 | |||
497 | resource_size_t addr; | ||
498 | |||
499 | int ret; | ||
500 | |||
501 | if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { | ||
502 | dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar); | ||
503 | return ERR_PTR(-ENODEV); | ||
504 | } | ||
505 | |||
506 | if (pci_resource_len(pdev, bar) != 0x100) { | ||
507 | dev_err(&pdev->dev, "Invalid iomem size. You may " | ||
508 | "experience problems.\n"); | ||
509 | } | ||
510 | |||
511 | if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { | ||
512 | dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n"); | ||
513 | return ERR_PTR(-ENODEV); | ||
514 | } | ||
515 | |||
516 | if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) { | ||
517 | dev_err(&pdev->dev, "Unknown interface. Aborting.\n"); | ||
518 | return ERR_PTR(-ENODEV); | ||
519 | } | ||
520 | |||
521 | host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot)); | ||
522 | if (IS_ERR(host)) { | ||
523 | ret = PTR_ERR(host); | ||
524 | goto unmap; | ||
525 | } | ||
526 | |||
527 | slot = sdhci_priv(host); | ||
528 | |||
529 | slot->chip = chip; | ||
530 | slot->host = host; | ||
531 | slot->pci_bar = bar; | ||
532 | |||
533 | host->hw_name = "PCI"; | ||
534 | host->ops = &sdhci_pci_ops; | ||
535 | host->quirks = chip->quirks; | ||
536 | |||
537 | host->irq = pdev->irq; | ||
538 | |||
539 | ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc)); | ||
540 | if (ret) { | ||
541 | dev_err(&pdev->dev, "cannot request region\n"); | ||
542 | return ERR_PTR(ret); | ||
543 | } | ||
544 | |||
545 | addr = pci_resource_start(pdev, bar); | ||
546 | host->ioaddr = ioremap_nocache(addr, pci_resource_len(pdev, bar)); | ||
547 | if (!host->ioaddr) { | ||
548 | dev_err(&pdev->dev, "failed to remap registers\n"); | ||
549 | goto release; | ||
550 | } | ||
551 | |||
552 | if (chip->fixes && chip->fixes->probe_slot) { | ||
553 | ret = chip->fixes->probe_slot(slot); | ||
554 | if (ret) | ||
555 | goto unmap; | ||
556 | } | ||
557 | |||
558 | ret = sdhci_add_host(host); | ||
559 | if (ret) | ||
560 | goto remove; | ||
561 | |||
562 | return slot; | ||
563 | |||
564 | remove: | ||
565 | if (chip->fixes && chip->fixes->remove_slot) | ||
566 | chip->fixes->remove_slot(slot, 0); | ||
567 | |||
568 | unmap: | ||
569 | iounmap(host->ioaddr); | ||
570 | |||
571 | release: | ||
572 | pci_release_region(pdev, bar); | ||
573 | sdhci_free_host(host); | ||
574 | |||
575 | return ERR_PTR(ret); | ||
576 | } | ||
577 | |||
578 | static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) | ||
579 | { | ||
580 | int dead; | ||
581 | u32 scratch; | ||
582 | |||
583 | dead = 0; | ||
584 | scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); | ||
585 | if (scratch == (u32)-1) | ||
586 | dead = 1; | ||
587 | |||
588 | sdhci_remove_host(slot->host, dead); | ||
589 | |||
590 | if (slot->chip->fixes && slot->chip->fixes->remove_slot) | ||
591 | slot->chip->fixes->remove_slot(slot, dead); | ||
592 | |||
593 | pci_release_region(slot->chip->pdev, slot->pci_bar); | ||
594 | |||
595 | sdhci_free_host(slot->host); | ||
596 | } | ||
597 | |||
598 | static int __devinit sdhci_pci_probe(struct pci_dev *pdev, | ||
599 | const struct pci_device_id *ent) | ||
600 | { | ||
601 | struct sdhci_pci_chip *chip; | ||
602 | struct sdhci_pci_slot *slot; | ||
603 | |||
604 | u8 slots, rev, first_bar; | ||
605 | int ret, i; | ||
606 | |||
607 | BUG_ON(pdev == NULL); | ||
608 | BUG_ON(ent == NULL); | ||
609 | |||
610 | pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev); | ||
611 | |||
612 | dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n", | ||
613 | (int)pdev->vendor, (int)pdev->device, (int)rev); | ||
614 | |||
615 | ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots); | ||
616 | if (ret) | ||
617 | return ret; | ||
618 | |||
619 | slots = PCI_SLOT_INFO_SLOTS(slots) + 1; | ||
620 | dev_dbg(&pdev->dev, "found %d slot(s)\n", slots); | ||
621 | if (slots == 0) | ||
622 | return -ENODEV; | ||
623 | |||
624 | BUG_ON(slots > MAX_SLOTS); | ||
625 | |||
626 | ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar); | ||
627 | if (ret) | ||
628 | return ret; | ||
629 | |||
630 | first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK; | ||
631 | |||
632 | if (first_bar > 5) { | ||
633 | dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n"); | ||
634 | return -ENODEV; | ||
635 | } | ||
636 | |||
637 | ret = pci_enable_device(pdev); | ||
638 | if (ret) | ||
639 | return ret; | ||
640 | |||
641 | chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL); | ||
642 | if (!chip) { | ||
643 | ret = -ENOMEM; | ||
644 | goto err; | ||
645 | } | ||
646 | |||
647 | chip->pdev = pdev; | ||
648 | chip->fixes = (const struct sdhci_pci_fixes*)ent->driver_data; | ||
649 | if (chip->fixes) | ||
650 | chip->quirks = chip->fixes->quirks; | ||
651 | chip->num_slots = slots; | ||
652 | |||
653 | pci_set_drvdata(pdev, chip); | ||
654 | |||
655 | if (chip->fixes && chip->fixes->probe) { | ||
656 | ret = chip->fixes->probe(chip); | ||
657 | if (ret) | ||
658 | goto free; | ||
659 | } | ||
660 | |||
661 | for (i = 0;i < slots;i++) { | ||
662 | slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i); | ||
663 | if (IS_ERR(slot)) { | ||
664 | for (i--;i >= 0;i--) | ||
665 | sdhci_pci_remove_slot(chip->slots[i]); | ||
666 | ret = PTR_ERR(slot); | ||
667 | goto free; | ||
668 | } | ||
669 | |||
670 | chip->slots[i] = slot; | ||
671 | } | ||
672 | |||
673 | return 0; | ||
674 | |||
675 | free: | ||
676 | pci_set_drvdata(pdev, NULL); | ||
677 | kfree(chip); | ||
678 | |||
679 | err: | ||
680 | pci_disable_device(pdev); | ||
681 | return ret; | ||
682 | } | ||
683 | |||
684 | static void __devexit sdhci_pci_remove(struct pci_dev *pdev) | ||
685 | { | ||
686 | int i; | ||
687 | struct sdhci_pci_chip *chip; | ||
688 | |||
689 | chip = pci_get_drvdata(pdev); | ||
690 | |||
691 | if (chip) { | ||
692 | for (i = 0;i < chip->num_slots; i++) | ||
693 | sdhci_pci_remove_slot(chip->slots[i]); | ||
694 | |||
695 | pci_set_drvdata(pdev, NULL); | ||
696 | kfree(chip); | ||
697 | } | ||
698 | |||
699 | pci_disable_device(pdev); | ||
700 | } | ||
701 | |||
702 | static struct pci_driver sdhci_driver = { | ||
703 | .name = "sdhci-pci", | ||
704 | .id_table = pci_ids, | ||
705 | .probe = sdhci_pci_probe, | ||
706 | .remove = __devexit_p(sdhci_pci_remove), | ||
707 | .suspend = sdhci_pci_suspend, | ||
708 | .resume = sdhci_pci_resume, | ||
709 | }; | ||
710 | |||
711 | /*****************************************************************************\ | ||
712 | * * | ||
713 | * Driver init/exit * | ||
714 | * * | ||
715 | \*****************************************************************************/ | ||
716 | |||
717 | static int __init sdhci_drv_init(void) | ||
718 | { | ||
719 | return pci_register_driver(&sdhci_driver); | ||
720 | } | ||
721 | |||
722 | static void __exit sdhci_drv_exit(void) | ||
723 | { | ||
724 | pci_unregister_driver(&sdhci_driver); | ||
725 | } | ||
726 | |||
727 | module_init(sdhci_drv_init); | ||
728 | module_exit(sdhci_drv_exit); | ||
729 | |||
730 | MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>"); | ||
731 | MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver"); | ||
732 | MODULE_LICENSE("GPL"); | ||