diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2008-04-04 13:36:59 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-07-15 08:14:40 -0400 |
commit | 4489428ab5a49a6f443d9aa17f1d891417787d7b (patch) | |
tree | fe95fd7aed4858e03af828805461716eb27d7d7c /drivers/mmc | |
parent | 45211e21598441a32e53cf5032b7faeac143df6d (diff) |
sdhci: support JMicron secondary interface
JMicron chips sometimes have two interfaces to work around limitations
in Microsoft's sdhci driver. This patch allows us to use either interface.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/sdhci-pci.c | 127 |
1 files changed, 125 insertions, 2 deletions
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index ef77ed1bd114..5dcb4958e47b 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c | |||
@@ -39,12 +39,18 @@ | |||
39 | #define MAX_SLOTS 8 | 39 | #define MAX_SLOTS 8 |
40 | 40 | ||
41 | struct sdhci_pci_chip; | 41 | struct sdhci_pci_chip; |
42 | struct sdhci_pci_slot; | ||
42 | 43 | ||
43 | struct sdhci_pci_fixes { | 44 | struct sdhci_pci_fixes { |
44 | unsigned int quirks; | 45 | unsigned int quirks; |
45 | 46 | ||
46 | int (*probe)(struct sdhci_pci_chip*); | 47 | int (*probe)(struct sdhci_pci_chip*); |
47 | 48 | ||
49 | int (*probe_slot)(struct sdhci_pci_slot*); | ||
50 | void (*remove_slot)(struct sdhci_pci_slot*); | ||
51 | |||
52 | int (*suspend)(struct sdhci_pci_chip*, | ||
53 | pm_message_t); | ||
48 | int (*resume)(struct sdhci_pci_chip*); | 54 | int (*resume)(struct sdhci_pci_chip*); |
49 | }; | 55 | }; |
50 | 56 | ||
@@ -133,6 +139,38 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) | |||
133 | int ret; | 139 | int ret; |
134 | 140 | ||
135 | /* | 141 | /* |
142 | * JMicron chips can have two interfaces to the same hardware | ||
143 | * in order to work around limitations in Microsoft's driver. | ||
144 | * We need to make sure we only bind to one of them. | ||
145 | * | ||
146 | * This code assumes two things: | ||
147 | * | ||
148 | * 1. The PCI code adds subfunctions in order. | ||
149 | * | ||
150 | * 2. The MMC interface has a lower subfunction number | ||
151 | * than the SD interface. | ||
152 | */ | ||
153 | if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) { | ||
154 | struct pci_dev *sd_dev; | ||
155 | |||
156 | sd_dev = NULL; | ||
157 | while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, | ||
158 | PCI_DEVICE_ID_JMICRON_JMB38X_MMC, sd_dev)) != NULL) { | ||
159 | if ((PCI_SLOT(chip->pdev->devfn) == | ||
160 | PCI_SLOT(sd_dev->devfn)) && | ||
161 | (chip->pdev->bus == sd_dev->bus)) | ||
162 | break; | ||
163 | } | ||
164 | |||
165 | if (sd_dev) { | ||
166 | pci_dev_put(sd_dev); | ||
167 | dev_info(&chip->pdev->dev, "Refusing to bind to " | ||
168 | "secondary interface.\n"); | ||
169 | return -ENODEV; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | /* | ||
136 | * JMicron chips need a bit of a nudge to enable the power | 174 | * JMicron chips need a bit of a nudge to enable the power |
137 | * output pins. | 175 | * output pins. |
138 | */ | 176 | */ |
@@ -145,9 +183,58 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) | |||
145 | return 0; | 183 | return 0; |
146 | } | 184 | } |
147 | 185 | ||
186 | static void jmicron_enable_mmc(struct sdhci_host *host, int on) | ||
187 | { | ||
188 | u8 scratch; | ||
189 | |||
190 | scratch = readb(host->ioaddr + 0xC0); | ||
191 | |||
192 | if (on) | ||
193 | scratch |= 0x01; | ||
194 | else | ||
195 | scratch &= ~0x01; | ||
196 | |||
197 | writeb(scratch, host->ioaddr + 0xC0); | ||
198 | } | ||
199 | |||
200 | static int jmicron_probe_slot(struct sdhci_pci_slot *slot) | ||
201 | { | ||
202 | /* | ||
203 | * The secondary interface requires a bit set to get the | ||
204 | * interrupts. | ||
205 | */ | ||
206 | if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) | ||
207 | jmicron_enable_mmc(slot->host, 1); | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static void jmicron_remove_slot(struct sdhci_pci_slot *slot) | ||
213 | { | ||
214 | if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) | ||
215 | jmicron_enable_mmc(slot->host, 0); | ||
216 | } | ||
217 | |||
218 | static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state) | ||
219 | { | ||
220 | int i; | ||
221 | |||
222 | if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { | ||
223 | for (i = 0;i < chip->num_slots;i++) | ||
224 | jmicron_enable_mmc(chip->slots[i]->host, 0); | ||
225 | } | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
148 | static int jmicron_resume(struct sdhci_pci_chip *chip) | 230 | static int jmicron_resume(struct sdhci_pci_chip *chip) |
149 | { | 231 | { |
150 | int ret; | 232 | int ret, i; |
233 | |||
234 | if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) { | ||
235 | for (i = 0;i < chip->num_slots;i++) | ||
236 | jmicron_enable_mmc(chip->slots[i]->host, 1); | ||
237 | } | ||
151 | 238 | ||
152 | ret = jmicron_pmos(chip, 1); | 239 | ret = jmicron_pmos(chip, 1); |
153 | if (ret) { | 240 | if (ret) { |
@@ -165,6 +252,10 @@ static const struct sdhci_pci_fixes sdhci_jmicron = { | |||
165 | 252 | ||
166 | .probe = jmicron_probe, | 253 | .probe = jmicron_probe, |
167 | 254 | ||
255 | .probe_slot = jmicron_probe_slot, | ||
256 | .remove_slot = jmicron_remove_slot, | ||
257 | |||
258 | .suspend = jmicron_suspend, | ||
168 | .resume = jmicron_resume, | 259 | .resume = jmicron_resume, |
169 | }; | 260 | }; |
170 | 261 | ||
@@ -225,6 +316,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = { | |||
225 | .driver_data = (kernel_ulong_t)&sdhci_jmicron, | 316 | .driver_data = (kernel_ulong_t)&sdhci_jmicron, |
226 | }, | 317 | }, |
227 | 318 | ||
319 | { | ||
320 | .vendor = PCI_VENDOR_ID_JMICRON, | ||
321 | .device = PCI_DEVICE_ID_JMICRON_JMB38X_MMC, | ||
322 | .subvendor = PCI_ANY_ID, | ||
323 | .subdevice = PCI_ANY_ID, | ||
324 | .driver_data = (kernel_ulong_t)&sdhci_jmicron, | ||
325 | }, | ||
326 | |||
228 | { /* Generic SD host controller */ | 327 | { /* Generic SD host controller */ |
229 | PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) | 328 | PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) |
230 | }, | 329 | }, |
@@ -301,6 +400,15 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) | |||
301 | } | 400 | } |
302 | } | 401 | } |
303 | 402 | ||
403 | if (chip->fixes && chip->fixes->suspend) { | ||
404 | ret = chip->fixes->suspend(chip, state); | ||
405 | if (ret) { | ||
406 | for (i = chip->num_slots - 1;i >= 0;i--) | ||
407 | sdhci_resume_host(chip->slots[i]->host); | ||
408 | return ret; | ||
409 | } | ||
410 | } | ||
411 | |||
304 | pci_save_state(pdev); | 412 | pci_save_state(pdev); |
305 | pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); | 413 | pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); |
306 | pci_disable_device(pdev); | 414 | pci_disable_device(pdev); |
@@ -418,12 +526,22 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( | |||
418 | goto release; | 526 | goto release; |
419 | } | 527 | } |
420 | 528 | ||
529 | if (chip->fixes && chip->fixes->probe_slot) { | ||
530 | ret = chip->fixes->probe_slot(slot); | ||
531 | if (ret) | ||
532 | goto unmap; | ||
533 | } | ||
534 | |||
421 | ret = sdhci_add_host(host); | 535 | ret = sdhci_add_host(host); |
422 | if (ret) | 536 | if (ret) |
423 | goto unmap; | 537 | goto remove; |
424 | 538 | ||
425 | return slot; | 539 | return slot; |
426 | 540 | ||
541 | remove: | ||
542 | if (chip->fixes && chip->fixes->remove_slot) | ||
543 | chip->fixes->remove_slot(slot); | ||
544 | |||
427 | unmap: | 545 | unmap: |
428 | iounmap(host->ioaddr); | 546 | iounmap(host->ioaddr); |
429 | 547 | ||
@@ -437,7 +555,12 @@ release: | |||
437 | static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) | 555 | static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) |
438 | { | 556 | { |
439 | sdhci_remove_host(slot->host); | 557 | sdhci_remove_host(slot->host); |
558 | |||
559 | if (slot->chip->fixes && slot->chip->fixes->remove_slot) | ||
560 | slot->chip->fixes->remove_slot(slot); | ||
561 | |||
440 | pci_release_region(slot->chip->pdev, slot->pci_bar); | 562 | pci_release_region(slot->chip->pdev, slot->pci_bar); |
563 | |||
441 | sdhci_free_host(slot->host); | 564 | sdhci_free_host(slot->host); |
442 | } | 565 | } |
443 | 566 | ||