diff options
author | ben@fluff.org.uk <ben@fluff.org.uk> | 2008-10-14 19:17:16 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-10-15 12:04:52 -0400 |
commit | f87e6d00fbd367f2d61fd600b5f8bd6e39d63f3f (patch) | |
tree | 43988efa9573fdf804eea29509b2034fd3077447 /drivers/mmc | |
parent | 9c2e7e40bf85684eebc019e915c39c4c07c734fa (diff) |
s3cmci: cpufreq support
Support for cpu frequency changing.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc')
-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 | }; |