aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-pci.c
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2008-04-04 13:36:59 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-07-15 08:14:40 -0400
commit4489428ab5a49a6f443d9aa17f1d891417787d7b (patch)
treefe95fd7aed4858e03af828805461716eb27d7d7c /drivers/mmc/host/sdhci-pci.c
parent45211e21598441a32e53cf5032b7faeac143df6d (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/host/sdhci-pci.c')
-rw-r--r--drivers/mmc/host/sdhci-pci.c127
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
41struct sdhci_pci_chip; 41struct sdhci_pci_chip;
42struct sdhci_pci_slot;
42 43
43struct sdhci_pci_fixes { 44struct 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
186static 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
200static 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
212static 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
218static 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
148static int jmicron_resume(struct sdhci_pci_chip *chip) 230static 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
541remove:
542 if (chip->fixes && chip->fixes->remove_slot)
543 chip->fixes->remove_slot(slot);
544
427unmap: 545unmap:
428 iounmap(host->ioaddr); 546 iounmap(host->ioaddr);
429 547
@@ -437,7 +555,12 @@ release:
437static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) 555static 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