aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorArindam Nath <arindam.nath@amd.com>2011-05-05 02:49:06 -0400
committerChris Ball <cjb@laptop.org>2011-05-24 23:53:48 -0400
commitc3ed3877625f10d600b0eca2ca48a68c46aed660 (patch)
treed8170541551dca7abcefa118c4681d7294e3456d /drivers/mmc
parent4d55c5a13a189a80d40383f02c8026f9a87d7c87 (diff)
mmc: sdhci: add support for programmable clock mode
Host Controller v3.00 supports programmable clock mode as an optional feature. The support for this mode is indicated by non-zero value in bits 48-55 of the Capabilities register. If supported, the actual value of Clock Multiplier is one more than the value provided in the bit fields. We only set Clock Generator Select (bit 5) and SDCLK Frequency Select (bits 8-15) of the Clock Control register in case Preset Value Enable is not set, otherwise these fields are automatically set by the Host Controller based on the UHS mode selected. Also, since the maximum and minimum clock frequency in this mode can be (Base Clock * Clock Mul) and (Base Clock * Clock Mul)/1024 respectively, f_max and f_min have been recalculated to reflect this change. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath <arindam.nath@amd.com> Reviewed-by: Philip Rakity <prakity@marvell.com> Tested-by: Philip Rakity <prakity@marvell.com> Acked-by: Zhangfei Gao <zhangfei.gao@marvell.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci.c81
-rw-r--r--drivers/mmc/host/sdhci.h3
2 files changed, 69 insertions, 15 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index a1ab2241588..07cca557c4b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1013,8 +1013,8 @@ static void sdhci_finish_command(struct sdhci_host *host)
1013 1013
1014static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 1014static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
1015{ 1015{
1016 int div; 1016 int div = 0; /* Initialized for compiler warning */
1017 u16 clk; 1017 u16 clk = 0;
1018 unsigned long timeout; 1018 unsigned long timeout;
1019 1019
1020 if (clock == host->clock) 1020 if (clock == host->clock)
@@ -1032,14 +1032,45 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
1032 goto out; 1032 goto out;
1033 1033
1034 if (host->version >= SDHCI_SPEC_300) { 1034 if (host->version >= SDHCI_SPEC_300) {
1035 /* Version 3.00 divisors must be a multiple of 2. */ 1035 /*
1036 if (host->max_clk <= clock) 1036 * Check if the Host Controller supports Programmable Clock
1037 div = 1; 1037 * Mode.
1038 else { 1038 */
1039 for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { 1039 if (host->clk_mul) {
1040 if ((host->max_clk / div) <= clock) 1040 u16 ctrl;
1041 break; 1041
1042 /*
1043 * We need to figure out whether the Host Driver needs
1044 * to select Programmable Clock Mode, or the value can
1045 * be set automatically by the Host Controller based on
1046 * the Preset Value registers.
1047 */
1048 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
1049 if (!(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
1050 for (div = 1; div <= 1024; div++) {
1051 if (((host->max_clk * host->clk_mul) /
1052 div) <= clock)
1053 break;
1054 }
1055 /*
1056 * Set Programmable Clock Mode in the Clock
1057 * Control register.
1058 */
1059 clk = SDHCI_PROG_CLOCK_MODE;
1060 div--;
1061 }
1062 } else {
1063 /* Version 3.00 divisors must be a multiple of 2. */
1064 if (host->max_clk <= clock)
1065 div = 1;
1066 else {
1067 for (div = 2; div < SDHCI_MAX_DIV_SPEC_300;
1068 div += 2) {
1069 if ((host->max_clk / div) <= clock)
1070 break;
1071 }
1042 } 1072 }
1073 div >>= 1;
1043 } 1074 }
1044 } else { 1075 } else {
1045 /* Version 2.00 divisors must be a power of 2. */ 1076 /* Version 2.00 divisors must be a power of 2. */
@@ -1047,10 +1078,10 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
1047 if ((host->max_clk / div) <= clock) 1078 if ((host->max_clk / div) <= clock)
1048 break; 1079 break;
1049 } 1080 }
1081 div >>= 1;
1050 } 1082 }
1051 div >>= 1;
1052 1083
1053 clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; 1084 clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
1054 clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) 1085 clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
1055 << SDHCI_DIVIDER_HI_SHIFT; 1086 << SDHCI_DIVIDER_HI_SHIFT;
1056 clk |= SDHCI_CLOCK_INT_EN; 1087 clk |= SDHCI_CLOCK_INT_EN;
@@ -2308,17 +2339,37 @@ int sdhci_add_host(struct sdhci_host *host)
2308 host->timeout_clk *= 1000; 2339 host->timeout_clk *= 1000;
2309 2340
2310 /* 2341 /*
2342 * In case of Host Controller v3.00, find out whether clock
2343 * multiplier is supported.
2344 */
2345 host->clk_mul = (caps[1] & SDHCI_CLOCK_MUL_MASK) >>
2346 SDHCI_CLOCK_MUL_SHIFT;
2347
2348 /*
2349 * In case the value in Clock Multiplier is 0, then programmable
2350 * clock mode is not supported, otherwise the actual clock
2351 * multiplier is one more than the value of Clock Multiplier
2352 * in the Capabilities Register.
2353 */
2354 if (host->clk_mul)
2355 host->clk_mul += 1;
2356
2357 /*
2311 * Set host parameters. 2358 * Set host parameters.
2312 */ 2359 */
2313 mmc->ops = &sdhci_ops; 2360 mmc->ops = &sdhci_ops;
2361 mmc->f_max = host->max_clk;
2314 if (host->ops->get_min_clock) 2362 if (host->ops->get_min_clock)
2315 mmc->f_min = host->ops->get_min_clock(host); 2363 mmc->f_min = host->ops->get_min_clock(host);
2316 else if (host->version >= SDHCI_SPEC_300) 2364 else if (host->version >= SDHCI_SPEC_300) {
2317 mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; 2365 if (host->clk_mul) {
2318 else 2366 mmc->f_min = (host->max_clk * host->clk_mul) / 1024;
2367 mmc->f_max = host->max_clk * host->clk_mul;
2368 } else
2369 mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300;
2370 } else
2319 mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; 2371 mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
2320 2372
2321 mmc->f_max = host->max_clk;
2322 mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; 2373 mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE;
2323 2374
2324 /* 2375 /*
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index e62367491ee..6b0a0ee9ac6 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -101,6 +101,7 @@
101#define SDHCI_DIV_MASK 0xFF 101#define SDHCI_DIV_MASK 0xFF
102#define SDHCI_DIV_MASK_LEN 8 102#define SDHCI_DIV_MASK_LEN 8
103#define SDHCI_DIV_HI_MASK 0x300 103#define SDHCI_DIV_HI_MASK 0x300
104#define SDHCI_PROG_CLOCK_MODE 0x0020
104#define SDHCI_CLOCK_CARD_EN 0x0004 105#define SDHCI_CLOCK_CARD_EN 0x0004
105#define SDHCI_CLOCK_INT_STABLE 0x0002 106#define SDHCI_CLOCK_INT_STABLE 0x0002
106#define SDHCI_CLOCK_INT_EN 0x0001 107#define SDHCI_CLOCK_INT_EN 0x0001
@@ -191,6 +192,8 @@
191#define SDHCI_DRIVER_TYPE_C 0x00000020 192#define SDHCI_DRIVER_TYPE_C 0x00000020
192#define SDHCI_DRIVER_TYPE_D 0x00000040 193#define SDHCI_DRIVER_TYPE_D 0x00000040
193#define SDHCI_USE_SDR50_TUNING 0x00002000 194#define SDHCI_USE_SDR50_TUNING 0x00002000
195#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
196#define SDHCI_CLOCK_MUL_SHIFT 16
194 197
195#define SDHCI_CAPABILITIES_1 0x44 198#define SDHCI_CAPABILITIES_1 0x44
196 199