diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2006-06-30 05:22:23 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-07-02 11:02:01 -0400 |
commit | 146ad66eac836c0b976c98f428d73e1f6a75270d (patch) | |
tree | 7f1dd2b34a6225360fab04abffa4c9dd2e124cd2 | |
parent | 51f82bc07a9673d790c2a17de8e3fa8046543f04 (diff) |
[MMC] sdhci: support for multiple voltages
The sdhci controllers can support up to three voltage levels. Detect which
and report back to the MMC layer.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | drivers/mmc/sdhci.c | 60 | ||||
-rw-r--r-- | drivers/mmc/sdhci.h | 10 |
2 files changed, 66 insertions, 4 deletions
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 405b6158cb6c..74912dcac82f 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c | |||
@@ -530,6 +530,46 @@ out: | |||
530 | host->clock = clock; | 530 | host->clock = clock; |
531 | } | 531 | } |
532 | 532 | ||
533 | static void sdhci_set_power(struct sdhci_host *host, unsigned short power) | ||
534 | { | ||
535 | u8 pwr; | ||
536 | |||
537 | if (host->power == power) | ||
538 | return; | ||
539 | |||
540 | writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); | ||
541 | |||
542 | if (power == (unsigned short)-1) | ||
543 | goto out; | ||
544 | |||
545 | pwr = SDHCI_POWER_ON; | ||
546 | |||
547 | switch (power) { | ||
548 | case MMC_VDD_170: | ||
549 | case MMC_VDD_180: | ||
550 | case MMC_VDD_190: | ||
551 | pwr |= SDHCI_POWER_180; | ||
552 | break; | ||
553 | case MMC_VDD_290: | ||
554 | case MMC_VDD_300: | ||
555 | case MMC_VDD_310: | ||
556 | pwr |= SDHCI_POWER_300; | ||
557 | break; | ||
558 | case MMC_VDD_320: | ||
559 | case MMC_VDD_330: | ||
560 | case MMC_VDD_340: | ||
561 | pwr |= SDHCI_POWER_330; | ||
562 | break; | ||
563 | default: | ||
564 | BUG(); | ||
565 | } | ||
566 | |||
567 | writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL); | ||
568 | |||
569 | out: | ||
570 | host->power = power; | ||
571 | } | ||
572 | |||
533 | /*****************************************************************************\ | 573 | /*****************************************************************************\ |
534 | * * | 574 | * * |
535 | * MMC callbacks * | 575 | * MMC callbacks * |
@@ -584,9 +624,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
584 | sdhci_set_clock(host, ios->clock); | 624 | sdhci_set_clock(host, ios->clock); |
585 | 625 | ||
586 | if (ios->power_mode == MMC_POWER_OFF) | 626 | if (ios->power_mode == MMC_POWER_OFF) |
587 | writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); | 627 | sdhci_set_power(host, -1); |
588 | else | 628 | else |
589 | writeb(0xFF, host->ioaddr + SDHCI_POWER_CONTROL); | 629 | sdhci_set_power(host, ios->vdd); |
590 | 630 | ||
591 | ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); | 631 | ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); |
592 | if (ios->bus_width == MMC_BUS_WIDTH_4) | 632 | if (ios->bus_width == MMC_BUS_WIDTH_4) |
@@ -1046,9 +1086,23 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) | |||
1046 | mmc->ops = &sdhci_ops; | 1086 | mmc->ops = &sdhci_ops; |
1047 | mmc->f_min = host->max_clk / 256; | 1087 | mmc->f_min = host->max_clk / 256; |
1048 | mmc->f_max = host->max_clk; | 1088 | mmc->f_max = host->max_clk; |
1049 | mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; | ||
1050 | mmc->caps = MMC_CAP_4_BIT_DATA; | 1089 | mmc->caps = MMC_CAP_4_BIT_DATA; |
1051 | 1090 | ||
1091 | mmc->ocr_avail = 0; | ||
1092 | if (caps & SDHCI_CAN_VDD_330) | ||
1093 | mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; | ||
1094 | else if (caps & SDHCI_CAN_VDD_300) | ||
1095 | mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31; | ||
1096 | else if (caps & SDHCI_CAN_VDD_180) | ||
1097 | mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; | ||
1098 | |||
1099 | if (mmc->ocr_avail == 0) { | ||
1100 | printk(KERN_ERR "%s: Hardware doesn't report any " | ||
1101 | "support voltages.\n", host->slot_descr); | ||
1102 | ret = -ENODEV; | ||
1103 | goto unmap; | ||
1104 | } | ||
1105 | |||
1052 | spin_lock_init(&host->lock); | 1106 | spin_lock_init(&host->lock); |
1053 | 1107 | ||
1054 | /* | 1108 | /* |
diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index 3b270ef486b4..aed4abd37bfd 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h | |||
@@ -67,6 +67,10 @@ | |||
67 | #define SDHCI_CTRL_4BITBUS 0x02 | 67 | #define SDHCI_CTRL_4BITBUS 0x02 |
68 | 68 | ||
69 | #define SDHCI_POWER_CONTROL 0x29 | 69 | #define SDHCI_POWER_CONTROL 0x29 |
70 | #define SDHCI_POWER_ON 0x01 | ||
71 | #define SDHCI_POWER_180 0x0A | ||
72 | #define SDHCI_POWER_300 0x0C | ||
73 | #define SDHCI_POWER_330 0x0E | ||
70 | 74 | ||
71 | #define SDHCI_BLOCK_GAP_CONTROL 0x2A | 75 | #define SDHCI_BLOCK_GAP_CONTROL 0x2A |
72 | 76 | ||
@@ -121,9 +125,12 @@ | |||
121 | /* 3E-3F reserved */ | 125 | /* 3E-3F reserved */ |
122 | 126 | ||
123 | #define SDHCI_CAPABILITIES 0x40 | 127 | #define SDHCI_CAPABILITIES 0x40 |
124 | #define SDHCI_CAN_DO_DMA 0x00400000 | ||
125 | #define SDHCI_CLOCK_BASE_MASK 0x00003F00 | 128 | #define SDHCI_CLOCK_BASE_MASK 0x00003F00 |
126 | #define SDHCI_CLOCK_BASE_SHIFT 8 | 129 | #define SDHCI_CLOCK_BASE_SHIFT 8 |
130 | #define SDHCI_CAN_DO_DMA 0x00400000 | ||
131 | #define SDHCI_CAN_VDD_330 0x01000000 | ||
132 | #define SDHCI_CAN_VDD_300 0x02000000 | ||
133 | #define SDHCI_CAN_VDD_180 0x04000000 | ||
127 | 134 | ||
128 | /* 44-47 reserved for more caps */ | 135 | /* 44-47 reserved for more caps */ |
129 | 136 | ||
@@ -151,6 +158,7 @@ struct sdhci_host { | |||
151 | unsigned int max_clk; /* Max possible freq (MHz) */ | 158 | unsigned int max_clk; /* Max possible freq (MHz) */ |
152 | 159 | ||
153 | unsigned int clock; /* Current clock (MHz) */ | 160 | unsigned int clock; /* Current clock (MHz) */ |
161 | unsigned short power; /* Current voltage */ | ||
154 | 162 | ||
155 | struct mmc_request *mrq; /* Current request */ | 163 | struct mmc_request *mrq; /* Current request */ |
156 | struct mmc_command *cmd; /* Current command */ | 164 | struct mmc_command *cmd; /* Current command */ |