diff options
| -rw-r--r-- | drivers/mmc/host/sdhci.c | 81 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci.h | 3 | ||||
| -rw-r--r-- | include/linux/mmc/sdhci.h | 1 |
3 files changed, 70 insertions, 15 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a1ab22415883..07cca557c4b5 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 | ||
| 1014 | static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) | 1014 | static 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 e62367491eee..6b0a0ee9ac6e 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 | ||
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index b74c8530e959..a88a799ed7c3 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h | |||
| @@ -117,6 +117,7 @@ struct sdhci_host { | |||
| 117 | 117 | ||
| 118 | unsigned int max_clk; /* Max possible freq (MHz) */ | 118 | unsigned int max_clk; /* Max possible freq (MHz) */ |
| 119 | unsigned int timeout_clk; /* Timeout freq (KHz) */ | 119 | unsigned int timeout_clk; /* Timeout freq (KHz) */ |
| 120 | unsigned int clk_mul; /* Clock Muliplier value */ | ||
| 120 | 121 | ||
| 121 | unsigned int clock; /* Current clock (MHz) */ | 122 | unsigned int clock; /* Current clock (MHz) */ |
| 122 | u8 pwr; /* Current voltage */ | 123 | u8 pwr; /* Current voltage */ |
