diff options
| -rw-r--r-- | drivers/mmc/host/s3cmci.c | 111 | ||||
| -rw-r--r-- | drivers/mmc/host/s3cmci.h | 4 |
2 files changed, 96 insertions, 19 deletions
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 2af630639910..a73ffb9d7b21 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/clk.h> | 13 | #include <linux/clk.h> |
| 14 | #include <linux/mmc/host.h> | 14 | #include <linux/mmc/host.h> |
| 15 | #include <linux/platform_device.h> | 15 | #include <linux/platform_device.h> |
| 16 | #include <linux/cpufreq.h> | ||
| 16 | #include <linux/irq.h> | 17 | #include <linux/irq.h> |
| 17 | #include <linux/io.h> | 18 | #include <linux/io.h> |
| 18 | 19 | ||
| @@ -1033,10 +1034,33 @@ static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq) | |||
| 1033 | s3cmci_send_request(mmc); | 1034 | s3cmci_send_request(mmc); |
| 1034 | } | 1035 | } |
| 1035 | 1036 | ||
| 1037 | static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios) | ||
| 1038 | { | ||
| 1039 | u32 mci_psc; | ||
| 1040 | |||
| 1041 | /* Set clock */ | ||
| 1042 | for (mci_psc = 0; mci_psc < 255; mci_psc++) { | ||
| 1043 | host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1)); | ||
| 1044 | |||
| 1045 | if (host->real_rate <= ios->clock) | ||
| 1046 | break; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | if (mci_psc > 255) | ||
| 1050 | mci_psc = 255; | ||
| 1051 | |||
| 1052 | host->prescaler = mci_psc; | ||
| 1053 | writel(host->prescaler, host->base + S3C2410_SDIPRE); | ||
| 1054 | |||
| 1055 | /* If requested clock is 0, real_rate will be 0, too */ | ||
| 1056 | if (ios->clock == 0) | ||
| 1057 | host->real_rate = 0; | ||
| 1058 | } | ||
| 1059 | |||
| 1036 | static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 1060 | static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
| 1037 | { | 1061 | { |
| 1038 | struct s3cmci_host *host = mmc_priv(mmc); | 1062 | struct s3cmci_host *host = mmc_priv(mmc); |
| 1039 | u32 mci_psc, mci_con; | 1063 | u32 mci_con; |
| 1040 | 1064 | ||
| 1041 | /* Set the power state */ | 1065 | /* Set the power state */ |
| 1042 | 1066 | ||
| @@ -1074,23 +1098,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 1074 | break; | 1098 | break; |
| 1075 | } | 1099 | } |
| 1076 | 1100 | ||
| 1077 | /* Set clock */ | 1101 | s3cmci_set_clk(host, ios); |
| 1078 | for (mci_psc = 0; mci_psc < 255; mci_psc++) { | ||
| 1079 | host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1)); | ||
| 1080 | |||
| 1081 | if (host->real_rate <= ios->clock) | ||
| 1082 | break; | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | if (mci_psc > 255) | ||
| 1086 | mci_psc = 255; | ||
| 1087 | |||
| 1088 | host->prescaler = mci_psc; | ||
| 1089 | writel(host->prescaler, host->base + S3C2410_SDIPRE); | ||
| 1090 | |||
| 1091 | /* If requested clock is 0, real_rate will be 0, too */ | ||
| 1092 | if (ios->clock == 0) | ||
| 1093 | host->real_rate = 0; | ||
| 1094 | 1102 | ||
| 1095 | /* Set CLOCK_ENABLE */ | 1103 | /* Set CLOCK_ENABLE */ |
| 1096 | if (ios->clock) | 1104 | if (ios->clock) |
| @@ -1148,6 +1156,61 @@ static struct s3c24xx_mci_pdata s3cmci_def_pdata = { | |||
| 1148 | * checks. Any zero fields to ensure reaonable defaults are picked. */ | 1156 | * checks. Any zero fields to ensure reaonable defaults are picked. */ |
| 1149 | }; | 1157 | }; |
| 1150 | 1158 | ||
| 1159 | #ifdef CONFIG_CPU_FREQ | ||
| 1160 | |||
| 1161 | static int s3cmci_cpufreq_transition(struct notifier_block *nb, | ||
| 1162 | unsigned long val, void *data) | ||
| 1163 | { | ||
| 1164 | struct s3cmci_host *host; | ||
| 1165 | struct mmc_host *mmc; | ||
| 1166 | unsigned long newclk; | ||
| 1167 | unsigned long flags; | ||
| 1168 | |||
| 1169 | host = container_of(nb, struct s3cmci_host, freq_transition); | ||
| 1170 | newclk = clk_get_rate(host->clk); | ||
| 1171 | mmc = host->mmc; | ||
| 1172 | |||
| 1173 | if ((val == CPUFREQ_PRECHANGE && newclk > host->clk_rate) || | ||
| 1174 | (val == CPUFREQ_POSTCHANGE && newclk < host->clk_rate)) { | ||
| 1175 | spin_lock_irqsave(&mmc->lock, flags); | ||
| 1176 | |||
| 1177 | host->clk_rate = newclk; | ||
| 1178 | |||
| 1179 | if (mmc->ios.power_mode != MMC_POWER_OFF && | ||
| 1180 | mmc->ios.clock != 0) | ||
| 1181 | s3cmci_set_clk(host, &mmc->ios); | ||
| 1182 | |||
| 1183 | spin_unlock_irqrestore(&mmc->lock, flags); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | return 0; | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | static inline int s3cmci_cpufreq_register(struct s3cmci_host *host) | ||
| 1190 | { | ||
| 1191 | host->freq_transition.notifier_call = s3cmci_cpufreq_transition; | ||
| 1192 | |||
| 1193 | return cpufreq_register_notifier(&host->freq_transition, | ||
| 1194 | CPUFREQ_TRANSITION_NOTIFIER); | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host) | ||
| 1198 | { | ||
| 1199 | cpufreq_unregister_notifier(&host->freq_transition, | ||
| 1200 | CPUFREQ_TRANSITION_NOTIFIER); | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | #else | ||
| 1204 | static inline int s3cmci_cpufreq_register(struct s3cmci_host *host) | ||
| 1205 | { | ||
| 1206 | return 0; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host) | ||
| 1210 | { | ||
| 1211 | } | ||
| 1212 | #endif | ||
| 1213 | |||
| 1151 | static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) | 1214 | static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) |
| 1152 | { | 1215 | { |
| 1153 | struct s3cmci_host *host; | 1216 | struct s3cmci_host *host; |
| @@ -1298,10 +1361,16 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) | |||
| 1298 | (host->is2440?"2440":""), | 1361 | (host->is2440?"2440":""), |
| 1299 | host->base, host->irq, host->irq_cd, host->dma); | 1362 | host->base, host->irq, host->irq_cd, host->dma); |
| 1300 | 1363 | ||
| 1364 | ret = s3cmci_cpufreq_register(host); | ||
| 1365 | if (ret) { | ||
| 1366 | dev_err(&pdev->dev, "failed to register cpufreq\n"); | ||
| 1367 | goto free_dmabuf; | ||
| 1368 | } | ||
| 1369 | |||
| 1301 | ret = mmc_add_host(mmc); | 1370 | ret = mmc_add_host(mmc); |
| 1302 | if (ret) { | 1371 | if (ret) { |
| 1303 | dev_err(&pdev->dev, "failed to add mmc host.\n"); | 1372 | dev_err(&pdev->dev, "failed to add mmc host.\n"); |
| 1304 | goto free_dmabuf; | 1373 | goto free_cpufreq; |
| 1305 | } | 1374 | } |
| 1306 | 1375 | ||
| 1307 | platform_set_drvdata(pdev, mmc); | 1376 | platform_set_drvdata(pdev, mmc); |
| @@ -1309,6 +1378,9 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) | |||
| 1309 | 1378 | ||
| 1310 | return 0; | 1379 | return 0; |
| 1311 | 1380 | ||
| 1381 | free_cpufreq: | ||
| 1382 | s3cmci_cpufreq_deregister(host); | ||
| 1383 | |||
| 1312 | free_dmabuf: | 1384 | free_dmabuf: |
| 1313 | clk_disable(host->clk); | 1385 | clk_disable(host->clk); |
| 1314 | 1386 | ||
| @@ -1342,6 +1414,7 @@ static void s3cmci_shutdown(struct platform_device *pdev) | |||
| 1342 | if (host->irq_cd >= 0) | 1414 | if (host->irq_cd >= 0) |
| 1343 | free_irq(host->irq_cd, host); | 1415 | free_irq(host->irq_cd, host); |
| 1344 | 1416 | ||
| 1417 | s3cmci_cpufreq_deregister(host); | ||
| 1345 | mmc_remove_host(mmc); | 1418 | mmc_remove_host(mmc); |
| 1346 | clk_disable(host->clk); | 1419 | clk_disable(host->clk); |
| 1347 | } | 1420 | } |
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h index 37d9c60010c9..7e39109587f9 100644 --- a/drivers/mmc/host/s3cmci.h +++ b/drivers/mmc/host/s3cmci.h | |||
| @@ -67,4 +67,8 @@ struct s3cmci_host { | |||
| 67 | 67 | ||
| 68 | unsigned int ccnt, dcnt; | 68 | unsigned int ccnt, dcnt; |
| 69 | struct tasklet_struct pio_tasklet; | 69 | struct tasklet_struct pio_tasklet; |
| 70 | |||
| 71 | #ifdef CONFIG_CPU_FREQ | ||
| 72 | struct notifier_block freq_transition; | ||
| 73 | #endif | ||
| 70 | }; | 74 | }; |
