aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-pci.c
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2008-03-18 12:35:49 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-07-15 08:14:39 -0400
commitb8c86fc5d8deaa5a6dc49c2c1ed144e6838bf0f3 (patch)
treea6405b8f5843760fa6f93c763f5850738d3142cd /drivers/mmc/host/sdhci-pci.c
parentc9b74c5b8fb807187f6b1db09012828fcd2d7e73 (diff)
sdhci: move pci stuff to separate module
The SDHCI interface is not PCI specific, yet the Linux driver was intimitely connected to the PCI bus. This patch properly separates the PCI specific portion from the bus independent code. This patch is based on work by Ben Dooks but he did not have time to complete it. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc/host/sdhci-pci.c')
-rw-r--r--drivers/mmc/host/sdhci-pci.c486
1 files changed, 486 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..2a3dd6cc7bf7
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -0,0 +1,486 @@
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
41static const struct pci_device_id pci_ids[] __devinitdata = {
42 {
43 .vendor = PCI_VENDOR_ID_RICOH,
44 .device = PCI_DEVICE_ID_RICOH_R5C822,
45 .subvendor = PCI_VENDOR_ID_IBM,
46 .subdevice = PCI_ANY_ID,
47 .driver_data = SDHCI_QUIRK_CLOCK_BEFORE_RESET |
48 SDHCI_QUIRK_FORCE_DMA,
49 },
50
51 {
52 .vendor = PCI_VENDOR_ID_RICOH,
53 .device = PCI_DEVICE_ID_RICOH_R5C822,
54 .subvendor = PCI_VENDOR_ID_SAMSUNG,
55 .subdevice = PCI_ANY_ID,
56 .driver_data = SDHCI_QUIRK_FORCE_DMA |
57 SDHCI_QUIRK_NO_CARD_NO_RESET,
58 },
59
60 {
61 .vendor = PCI_VENDOR_ID_RICOH,
62 .device = PCI_DEVICE_ID_RICOH_R5C822,
63 .subvendor = PCI_ANY_ID,
64 .subdevice = PCI_ANY_ID,
65 .driver_data = SDHCI_QUIRK_FORCE_DMA,
66 },
67
68 {
69 .vendor = PCI_VENDOR_ID_TI,
70 .device = PCI_DEVICE_ID_TI_XX21_XX11_SD,
71 .subvendor = PCI_ANY_ID,
72 .subdevice = PCI_ANY_ID,
73 .driver_data = SDHCI_QUIRK_FORCE_DMA,
74 },
75
76 {
77 .vendor = PCI_VENDOR_ID_ENE,
78 .device = PCI_DEVICE_ID_ENE_CB712_SD,
79 .subvendor = PCI_ANY_ID,
80 .subdevice = PCI_ANY_ID,
81 .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
82 SDHCI_QUIRK_BROKEN_DMA,
83 },
84
85 {
86 .vendor = PCI_VENDOR_ID_ENE,
87 .device = PCI_DEVICE_ID_ENE_CB712_SD_2,
88 .subvendor = PCI_ANY_ID,
89 .subdevice = PCI_ANY_ID,
90 .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
91 SDHCI_QUIRK_BROKEN_DMA,
92 },
93
94 {
95 .vendor = PCI_VENDOR_ID_ENE,
96 .device = PCI_DEVICE_ID_ENE_CB714_SD,
97 .subvendor = PCI_ANY_ID,
98 .subdevice = PCI_ANY_ID,
99 .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
100 SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
101 SDHCI_QUIRK_BROKEN_DMA,
102 },
103
104 {
105 .vendor = PCI_VENDOR_ID_ENE,
106 .device = PCI_DEVICE_ID_ENE_CB714_SD_2,
107 .subvendor = PCI_ANY_ID,
108 .subdevice = PCI_ANY_ID,
109 .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
110 SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
111 SDHCI_QUIRK_BROKEN_DMA,
112 },
113
114 {
115 .vendor = PCI_VENDOR_ID_MARVELL,
116 .device = PCI_DEVICE_ID_MARVELL_CAFE_SD,
117 .subvendor = PCI_ANY_ID,
118 .subdevice = PCI_ANY_ID,
119 .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
120 SDHCI_QUIRK_INCR_TIMEOUT_CONTROL,
121 },
122
123 {
124 .vendor = PCI_VENDOR_ID_JMICRON,
125 .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD,
126 .subvendor = PCI_ANY_ID,
127 .subdevice = PCI_ANY_ID,
128 .driver_data = SDHCI_QUIRK_32BIT_DMA_ADDR |
129 SDHCI_QUIRK_32BIT_DMA_SIZE |
130 SDHCI_QUIRK_RESET_AFTER_REQUEST,
131 },
132
133 { /* Generic SD host controller */
134 PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
135 },
136
137 { /* end: all zeroes */ },
138};
139
140MODULE_DEVICE_TABLE(pci, pci_ids);
141
142struct sdhci_pci_chip;
143
144struct sdhci_pci_slot {
145 struct sdhci_pci_chip *chip;
146 struct sdhci_host *host;
147
148 int pci_bar;
149};
150
151struct sdhci_pci_chip {
152 struct pci_dev *pdev;
153 unsigned int quirks;
154
155 int num_slots; /* Slots on controller */
156 struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
157};
158
159/*****************************************************************************\
160 * *
161 * SDHCI core callbacks *
162 * *
163\*****************************************************************************/
164
165static int sdhci_pci_enable_dma(struct sdhci_host *host)
166{
167 struct sdhci_pci_slot *slot;
168 struct pci_dev *pdev;
169 int ret;
170
171 slot = sdhci_priv(host);
172 pdev = slot->chip->pdev;
173
174 if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) &&
175 ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) &&
176 (host->flags & SDHCI_USE_DMA)) {
177 dev_warn(&pdev->dev, "Will use DMA mode even though HW "
178 "doesn't fully claim to support it.\n");
179 }
180
181 ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
182 if (ret)
183 return ret;
184
185 pci_set_master(pdev);
186
187 return 0;
188}
189
190static struct sdhci_ops sdhci_pci_ops = {
191 .enable_dma = sdhci_pci_enable_dma,
192};
193
194/*****************************************************************************\
195 * *
196 * Suspend/resume *
197 * *
198\*****************************************************************************/
199
200#ifdef CONFIG_PM
201
202static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
203{
204 struct sdhci_pci_chip *chip;
205 struct sdhci_pci_slot *slot;
206 int i, ret;
207
208 chip = pci_get_drvdata(pdev);
209 if (!chip)
210 return 0;
211
212 for (i = 0;i < chip->num_slots;i++) {
213 slot = chip->slots[i];
214 if (!slot)
215 continue;
216
217 ret = sdhci_suspend_host(slot->host, state);
218
219 if (ret) {
220 for (i--;i >= 0;i--)
221 sdhci_resume_host(chip->slots[i]->host);
222 return ret;
223 }
224 }
225
226 pci_save_state(pdev);
227 pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
228 pci_disable_device(pdev);
229 pci_set_power_state(pdev, pci_choose_state(pdev, state));
230
231 return 0;
232}
233
234static int sdhci_pci_resume (struct pci_dev *pdev)
235{
236 struct sdhci_pci_chip *chip;
237 struct sdhci_pci_slot *slot;
238 int i, ret;
239
240 chip = pci_get_drvdata(pdev);
241 if (!chip)
242 return 0;
243
244 pci_set_power_state(pdev, PCI_D0);
245 pci_restore_state(pdev);
246 ret = pci_enable_device(pdev);
247 if (ret)
248 return ret;
249
250 for (i = 0;i < chip->num_slots;i++) {
251 slot = chip->slots[i];
252 if (!slot)
253 continue;
254
255 ret = sdhci_resume_host(slot->host);
256 if (ret)
257 return ret;
258 }
259
260 return 0;
261}
262
263#else /* CONFIG_PM */
264
265#define sdhci_pci_suspend NULL
266#define sdhci_pci_resume NULL
267
268#endif /* CONFIG_PM */
269
270/*****************************************************************************\
271 * *
272 * Device probing/removal *
273 * *
274\*****************************************************************************/
275
276static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
277 struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar)
278{
279 struct sdhci_pci_slot *slot;
280 struct sdhci_host *host;
281
282 resource_size_t addr;
283
284 int ret;
285
286 if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
287 dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
288 return ERR_PTR(-ENODEV);
289 }
290
291 if (pci_resource_len(pdev, bar) != 0x100) {
292 dev_err(&pdev->dev, "Invalid iomem size. You may "
293 "experience problems.\n");
294 }
295
296 if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
297 dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n");
298 return ERR_PTR(-ENODEV);
299 }
300
301 if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {
302 dev_err(&pdev->dev, "Unknown interface. Aborting.\n");
303 return ERR_PTR(-ENODEV);
304 }
305
306 host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
307 if (IS_ERR(host)) {
308 ret = PTR_ERR(host);
309 goto unmap;
310 }
311
312 slot = sdhci_priv(host);
313
314 slot->chip = chip;
315 slot->host = host;
316 slot->pci_bar = bar;
317
318 host->hw_name = "PCI";
319 host->ops = &sdhci_pci_ops;
320 host->quirks = chip->quirks;
321
322 host->irq = pdev->irq;
323
324 ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
325 if (ret) {
326 dev_err(&pdev->dev, "cannot request region\n");
327 return ERR_PTR(ret);
328 }
329
330 addr = pci_resource_start(pdev, bar);
331 host->ioaddr = ioremap_nocache(addr, pci_resource_len(pdev, bar));
332 if (!host->ioaddr) {
333 dev_err(&pdev->dev, "failed to remap registers\n");
334 goto release;
335 }
336
337 ret = sdhci_add_host(host);
338 if (ret)
339 goto unmap;
340
341 return slot;
342
343unmap:
344 iounmap(host->ioaddr);
345
346release:
347 pci_release_region(pdev, bar);
348 sdhci_free_host(host);
349
350 return ERR_PTR(ret);
351}
352
353static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
354{
355 sdhci_remove_host(slot->host);
356 pci_release_region(slot->chip->pdev, slot->pci_bar);
357 sdhci_free_host(slot->host);
358}
359
360static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
361 const struct pci_device_id *ent)
362{
363 struct sdhci_pci_chip *chip;
364 struct sdhci_pci_slot *slot;
365
366 u8 slots, rev, first_bar;
367 int ret, i;
368
369 BUG_ON(pdev == NULL);
370 BUG_ON(ent == NULL);
371
372 pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
373
374 dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n",
375 (int)pdev->vendor, (int)pdev->device, (int)rev);
376
377 ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
378 if (ret)
379 return ret;
380
381 slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
382 dev_dbg(&pdev->dev, "found %d slot(s)\n", slots);
383 if (slots == 0)
384 return -ENODEV;
385
386 BUG_ON(slots > MAX_SLOTS);
387
388 ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
389 if (ret)
390 return ret;
391
392 first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
393
394 if (first_bar > 5) {
395 dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n");
396 return -ENODEV;
397 }
398
399 ret = pci_enable_device(pdev);
400 if (ret)
401 return ret;
402
403 chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL);
404 if (!chip) {
405 ret = -ENOMEM;
406 goto err;
407 }
408
409 chip->pdev = pdev;
410 chip->quirks = ent->driver_data;
411 chip->num_slots = slots;
412
413 pci_set_drvdata(pdev, chip);
414
415 for (i = 0;i < slots;i++) {
416 slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
417 if (IS_ERR(slot)) {
418 for (i--;i >= 0;i--)
419 sdhci_pci_remove_slot(chip->slots[i]);
420 ret = PTR_ERR(slot);
421 goto free;
422 }
423
424 chip->slots[i] = slot;
425 }
426
427 return 0;
428
429free:
430 pci_set_drvdata(pdev, NULL);
431 kfree(chip);
432
433err:
434 pci_disable_device(pdev);
435 return ret;
436}
437
438static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
439{
440 int i;
441 struct sdhci_pci_chip *chip;
442
443 chip = pci_get_drvdata(pdev);
444
445 if (chip) {
446 for (i = 0;i < chip->num_slots; i++)
447 sdhci_pci_remove_slot(chip->slots[i]);
448
449 pci_set_drvdata(pdev, NULL);
450 kfree(chip);
451 }
452
453 pci_disable_device(pdev);
454}
455
456static struct pci_driver sdhci_driver = {
457 .name = "sdhci-pci",
458 .id_table = pci_ids,
459 .probe = sdhci_pci_probe,
460 .remove = __devexit_p(sdhci_pci_remove),
461 .suspend = sdhci_pci_suspend,
462 .resume = sdhci_pci_resume,
463};
464
465/*****************************************************************************\
466 * *
467 * Driver init/exit *
468 * *
469\*****************************************************************************/
470
471static int __init sdhci_drv_init(void)
472{
473 return pci_register_driver(&sdhci_driver);
474}
475
476static void __exit sdhci_drv_exit(void)
477{
478 pci_unregister_driver(&sdhci_driver);
479}
480
481module_init(sdhci_drv_init);
482module_exit(sdhci_drv_exit);
483
484MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
485MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver");
486MODULE_LICENSE("GPL");