aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/s3cmci.c210
-rw-r--r--drivers/mmc/host/s3cmci.h6
2 files changed, 164 insertions, 52 deletions
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index ae16d845d746..3b2085b57769 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -3,6 +3,9 @@
3 * 3 *
4 * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de> 4 * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
5 * 5 *
6 * Current driver maintained by Ben Dooks and Simtec Electronics
7 * Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
8 *
6 * This program is free software; you can redistribute it and/or modify 9 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as 10 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation. 11 * published by the Free Software Foundation.
@@ -13,6 +16,7 @@
13#include <linux/clk.h> 16#include <linux/clk.h>
14#include <linux/mmc/host.h> 17#include <linux/mmc/host.h>
15#include <linux/platform_device.h> 18#include <linux/platform_device.h>
19#include <linux/cpufreq.h>
16#include <linux/irq.h> 20#include <linux/irq.h>
17#include <linux/io.h> 21#include <linux/io.h>
18 22
@@ -39,9 +43,9 @@ enum dbg_channels {
39 dbg_conf = (1 << 8), 43 dbg_conf = (1 << 8),
40}; 44};
41 45
42static const int dbgmap_err = dbg_err | dbg_fail; 46static const int dbgmap_err = dbg_fail;
43static const int dbgmap_info = dbg_info | dbg_conf; 47static const int dbgmap_info = dbg_info | dbg_conf;
44static const int dbgmap_debug = dbg_debug; 48static const int dbgmap_debug = dbg_err | dbg_debug;
45 49
46#define dbg(host, channels, args...) \ 50#define dbg(host, channels, args...) \
47 do { \ 51 do { \
@@ -189,7 +193,7 @@ static inline void clear_imask(struct s3cmci_host *host)
189} 193}
190 194
191static inline int get_data_buffer(struct s3cmci_host *host, 195static inline int get_data_buffer(struct s3cmci_host *host,
192 u32 *words, u32 **pointer) 196 u32 *bytes, u32 **pointer)
193{ 197{
194 struct scatterlist *sg; 198 struct scatterlist *sg;
195 199
@@ -206,7 +210,7 @@ static inline int get_data_buffer(struct s3cmci_host *host,
206 } 210 }
207 sg = &host->mrq->data->sg[host->pio_sgptr]; 211 sg = &host->mrq->data->sg[host->pio_sgptr];
208 212
209 *words = sg->length >> 2; 213 *bytes = sg->length;
210 *pointer = sg_virt(sg); 214 *pointer = sg_virt(sg);
211 215
212 host->pio_sgptr++; 216 host->pio_sgptr++;
@@ -222,7 +226,7 @@ static inline u32 fifo_count(struct s3cmci_host *host)
222 u32 fifostat = readl(host->base + S3C2410_SDIFSTA); 226 u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
223 227
224 fifostat &= S3C2410_SDIFSTA_COUNTMASK; 228 fifostat &= S3C2410_SDIFSTA_COUNTMASK;
225 return fifostat >> 2; 229 return fifostat;
226} 230}
227 231
228static inline u32 fifo_free(struct s3cmci_host *host) 232static inline u32 fifo_free(struct s3cmci_host *host)
@@ -230,13 +234,15 @@ static inline u32 fifo_free(struct s3cmci_host *host)
230 u32 fifostat = readl(host->base + S3C2410_SDIFSTA); 234 u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
231 235
232 fifostat &= S3C2410_SDIFSTA_COUNTMASK; 236 fifostat &= S3C2410_SDIFSTA_COUNTMASK;
233 return (63 - fifostat) >> 2; 237 return 63 - fifostat;
234} 238}
235 239
236static void do_pio_read(struct s3cmci_host *host) 240static void do_pio_read(struct s3cmci_host *host)
237{ 241{
238 int res; 242 int res;
239 u32 fifo; 243 u32 fifo;
244 u32 *ptr;
245 u32 fifo_words;
240 void __iomem *from_ptr; 246 void __iomem *from_ptr;
241 247
242 /* write real prescaler to host, it might be set slow to fix */ 248 /* write real prescaler to host, it might be set slow to fix */
@@ -245,8 +251,8 @@ static void do_pio_read(struct s3cmci_host *host)
245 from_ptr = host->base + host->sdidata; 251 from_ptr = host->base + host->sdidata;
246 252
247 while ((fifo = fifo_count(host))) { 253 while ((fifo = fifo_count(host))) {
248 if (!host->pio_words) { 254 if (!host->pio_bytes) {
249 res = get_data_buffer(host, &host->pio_words, 255 res = get_data_buffer(host, &host->pio_bytes,
250 &host->pio_ptr); 256 &host->pio_ptr);
251 if (res) { 257 if (res) {
252 host->pio_active = XFER_NONE; 258 host->pio_active = XFER_NONE;
@@ -259,26 +265,47 @@ static void do_pio_read(struct s3cmci_host *host)
259 265
260 dbg(host, dbg_pio, 266 dbg(host, dbg_pio,
261 "pio_read(): new target: [%i]@[%p]\n", 267 "pio_read(): new target: [%i]@[%p]\n",
262 host->pio_words, host->pio_ptr); 268 host->pio_bytes, host->pio_ptr);
263 } 269 }
264 270
265 dbg(host, dbg_pio, 271 dbg(host, dbg_pio,
266 "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", 272 "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n",
267 fifo, host->pio_words, 273 fifo, host->pio_bytes,
268 readl(host->base + S3C2410_SDIDCNT)); 274 readl(host->base + S3C2410_SDIDCNT));
269 275
270 if (fifo > host->pio_words) 276 /* If we have reached the end of the block, we can
271 fifo = host->pio_words; 277 * read a word and get 1 to 3 bytes. If we in the
278 * middle of the block, we have to read full words,
279 * otherwise we will write garbage, so round down to
280 * an even multiple of 4. */
281 if (fifo >= host->pio_bytes)
282 fifo = host->pio_bytes;
283 else
284 fifo -= fifo & 3;
272 285
273 host->pio_words -= fifo; 286 host->pio_bytes -= fifo;
274 host->pio_count += fifo; 287 host->pio_count += fifo;
275 288
276 while (fifo--) 289 fifo_words = fifo >> 2;
277 *(host->pio_ptr++) = readl(from_ptr); 290 ptr = host->pio_ptr;
291 while (fifo_words--)
292 *ptr++ = readl(from_ptr);
293 host->pio_ptr = ptr;
294
295 if (fifo & 3) {
296 u32 n = fifo & 3;
297 u32 data = readl(from_ptr);
298 u8 *p = (u8 *)host->pio_ptr;
299
300 while (n--) {
301 *p++ = data;
302 data >>= 8;
303 }
304 }
278 } 305 }
279 306
280 if (!host->pio_words) { 307 if (!host->pio_bytes) {
281 res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); 308 res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr);
282 if (res) { 309 if (res) {
283 dbg(host, dbg_pio, 310 dbg(host, dbg_pio,
284 "pio_read(): complete (no more buffers).\n"); 311 "pio_read(): complete (no more buffers).\n");
@@ -298,12 +325,13 @@ static void do_pio_write(struct s3cmci_host *host)
298 void __iomem *to_ptr; 325 void __iomem *to_ptr;
299 int res; 326 int res;
300 u32 fifo; 327 u32 fifo;
328 u32 *ptr;
301 329
302 to_ptr = host->base + host->sdidata; 330 to_ptr = host->base + host->sdidata;
303 331
304 while ((fifo = fifo_free(host))) { 332 while ((fifo = fifo_free(host))) {
305 if (!host->pio_words) { 333 if (!host->pio_bytes) {
306 res = get_data_buffer(host, &host->pio_words, 334 res = get_data_buffer(host, &host->pio_bytes,
307 &host->pio_ptr); 335 &host->pio_ptr);
308 if (res) { 336 if (res) {
309 dbg(host, dbg_pio, 337 dbg(host, dbg_pio,
@@ -315,18 +343,27 @@ static void do_pio_write(struct s3cmci_host *host)
315 343
316 dbg(host, dbg_pio, 344 dbg(host, dbg_pio,
317 "pio_write(): new source: [%i]@[%p]\n", 345 "pio_write(): new source: [%i]@[%p]\n",
318 host->pio_words, host->pio_ptr); 346 host->pio_bytes, host->pio_ptr);
319 347
320 } 348 }
321 349
322 if (fifo > host->pio_words) 350 /* If we have reached the end of the block, we have to
323 fifo = host->pio_words; 351 * write exactly the remaining number of bytes. If we
352 * in the middle of the block, we have to write full
353 * words, so round down to an even multiple of 4. */
354 if (fifo >= host->pio_bytes)
355 fifo = host->pio_bytes;
356 else
357 fifo -= fifo & 3;
324 358
325 host->pio_words -= fifo; 359 host->pio_bytes -= fifo;
326 host->pio_count += fifo; 360 host->pio_count += fifo;
327 361
362 fifo = (fifo + 3) >> 2;
363 ptr = host->pio_ptr;
328 while (fifo--) 364 while (fifo--)
329 writel(*(host->pio_ptr++), to_ptr); 365 writel(*ptr++, to_ptr);
366 host->pio_ptr = ptr;
330 } 367 }
331 368
332 enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); 369 enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
@@ -349,9 +386,9 @@ static void pio_tasklet(unsigned long data)
349 clear_imask(host); 386 clear_imask(host);
350 if (host->pio_active != XFER_NONE) { 387 if (host->pio_active != XFER_NONE) {
351 dbg(host, dbg_err, "unfinished %s " 388 dbg(host, dbg_err, "unfinished %s "
352 "- pio_count:[%u] pio_words:[%u]\n", 389 "- pio_count:[%u] pio_bytes:[%u]\n",
353 (host->pio_active == XFER_READ) ? "read" : "write", 390 (host->pio_active == XFER_READ) ? "read" : "write",
354 host->pio_count, host->pio_words); 391 host->pio_count, host->pio_bytes);
355 392
356 if (host->mrq->data) 393 if (host->mrq->data)
357 host->mrq->data->error = -EINVAL; 394 host->mrq->data->error = -EINVAL;
@@ -812,11 +849,10 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
812 /* We cannot deal with unaligned blocks with more than 849 /* We cannot deal with unaligned blocks with more than
813 * one block being transfered. */ 850 * one block being transfered. */
814 851
815 if (data->blocks > 1) 852 if (data->blocks > 1) {
853 pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);
816 return -EINVAL; 854 return -EINVAL;
817 855 }
818 /* No support yet for non-word block transfers. */
819 return -EINVAL;
820 } 856 }
821 857
822 while (readl(host->base + S3C2410_SDIDSTA) & 858 while (readl(host->base + S3C2410_SDIDSTA) &
@@ -896,7 +932,7 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
896 BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); 932 BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
897 933
898 host->pio_sgptr = 0; 934 host->pio_sgptr = 0;
899 host->pio_words = 0; 935 host->pio_bytes = 0;
900 host->pio_count = 0; 936 host->pio_count = 0;
901 host->pio_active = rw ? XFER_WRITE : XFER_READ; 937 host->pio_active = rw ? XFER_WRITE : XFER_READ;
902 938
@@ -1033,10 +1069,33 @@ static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
1033 s3cmci_send_request(mmc); 1069 s3cmci_send_request(mmc);
1034} 1070}
1035 1071
1072static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios)
1073{
1074 u32 mci_psc;
1075
1076 /* Set clock */
1077 for (mci_psc = 0; mci_psc < 255; mci_psc++) {
1078 host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
1079
1080 if (host->real_rate <= ios->clock)
1081 break;
1082 }
1083
1084 if (mci_psc > 255)
1085 mci_psc = 255;
1086
1087 host->prescaler = mci_psc;
1088 writel(host->prescaler, host->base + S3C2410_SDIPRE);
1089
1090 /* If requested clock is 0, real_rate will be 0, too */
1091 if (ios->clock == 0)
1092 host->real_rate = 0;
1093}
1094
1036static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 1095static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
1037{ 1096{
1038 struct s3cmci_host *host = mmc_priv(mmc); 1097 struct s3cmci_host *host = mmc_priv(mmc);
1039 u32 mci_psc, mci_con; 1098 u32 mci_con;
1040 1099
1041 /* Set the power state */ 1100 /* Set the power state */
1042 1101
@@ -1074,23 +1133,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
1074 break; 1133 break;
1075 } 1134 }
1076 1135
1077 /* Set clock */ 1136 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 1137
1095 /* Set CLOCK_ENABLE */ 1138 /* Set CLOCK_ENABLE */
1096 if (ios->clock) 1139 if (ios->clock)
@@ -1148,6 +1191,61 @@ static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
1148 * checks. Any zero fields to ensure reaonable defaults are picked. */ 1191 * checks. Any zero fields to ensure reaonable defaults are picked. */
1149}; 1192};
1150 1193
1194#ifdef CONFIG_CPU_FREQ
1195
1196static int s3cmci_cpufreq_transition(struct notifier_block *nb,
1197 unsigned long val, void *data)
1198{
1199 struct s3cmci_host *host;
1200 struct mmc_host *mmc;
1201 unsigned long newclk;
1202 unsigned long flags;
1203
1204 host = container_of(nb, struct s3cmci_host, freq_transition);
1205 newclk = clk_get_rate(host->clk);
1206 mmc = host->mmc;
1207
1208 if ((val == CPUFREQ_PRECHANGE && newclk > host->clk_rate) ||
1209 (val == CPUFREQ_POSTCHANGE && newclk < host->clk_rate)) {
1210 spin_lock_irqsave(&mmc->lock, flags);
1211
1212 host->clk_rate = newclk;
1213
1214 if (mmc->ios.power_mode != MMC_POWER_OFF &&
1215 mmc->ios.clock != 0)
1216 s3cmci_set_clk(host, &mmc->ios);
1217
1218 spin_unlock_irqrestore(&mmc->lock, flags);
1219 }
1220
1221 return 0;
1222}
1223
1224static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
1225{
1226 host->freq_transition.notifier_call = s3cmci_cpufreq_transition;
1227
1228 return cpufreq_register_notifier(&host->freq_transition,
1229 CPUFREQ_TRANSITION_NOTIFIER);
1230}
1231
1232static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
1233{
1234 cpufreq_unregister_notifier(&host->freq_transition,
1235 CPUFREQ_TRANSITION_NOTIFIER);
1236}
1237
1238#else
1239static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
1240{
1241 return 0;
1242}
1243
1244static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
1245{
1246}
1247#endif
1248
1151static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) 1249static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
1152{ 1250{
1153 struct s3cmci_host *host; 1251 struct s3cmci_host *host;
@@ -1298,10 +1396,16 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
1298 (host->is2440?"2440":""), 1396 (host->is2440?"2440":""),
1299 host->base, host->irq, host->irq_cd, host->dma); 1397 host->base, host->irq, host->irq_cd, host->dma);
1300 1398
1399 ret = s3cmci_cpufreq_register(host);
1400 if (ret) {
1401 dev_err(&pdev->dev, "failed to register cpufreq\n");
1402 goto free_dmabuf;
1403 }
1404
1301 ret = mmc_add_host(mmc); 1405 ret = mmc_add_host(mmc);
1302 if (ret) { 1406 if (ret) {
1303 dev_err(&pdev->dev, "failed to add mmc host.\n"); 1407 dev_err(&pdev->dev, "failed to add mmc host.\n");
1304 goto free_dmabuf; 1408 goto free_cpufreq;
1305 } 1409 }
1306 1410
1307 platform_set_drvdata(pdev, mmc); 1411 platform_set_drvdata(pdev, mmc);
@@ -1309,6 +1413,9 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
1309 1413
1310 return 0; 1414 return 0;
1311 1415
1416 free_cpufreq:
1417 s3cmci_cpufreq_deregister(host);
1418
1312 free_dmabuf: 1419 free_dmabuf:
1313 clk_disable(host->clk); 1420 clk_disable(host->clk);
1314 1421
@@ -1342,6 +1449,7 @@ static void s3cmci_shutdown(struct platform_device *pdev)
1342 if (host->irq_cd >= 0) 1449 if (host->irq_cd >= 0)
1343 free_irq(host->irq_cd, host); 1450 free_irq(host->irq_cd, host);
1344 1451
1452 s3cmci_cpufreq_deregister(host);
1345 mmc_remove_host(mmc); 1453 mmc_remove_host(mmc);
1346 clk_disable(host->clk); 1454 clk_disable(host->clk);
1347} 1455}
@@ -1455,7 +1563,7 @@ module_exit(s3cmci_exit);
1455 1563
1456MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver"); 1564MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
1457MODULE_LICENSE("GPL v2"); 1565MODULE_LICENSE("GPL v2");
1458MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>"); 1566MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>");
1459MODULE_ALIAS("platform:s3c2410-sdi"); 1567MODULE_ALIAS("platform:s3c2410-sdi");
1460MODULE_ALIAS("platform:s3c2412-sdi"); 1568MODULE_ALIAS("platform:s3c2412-sdi");
1461MODULE_ALIAS("platform:s3c2440-sdi"); 1569MODULE_ALIAS("platform:s3c2440-sdi");
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
index 37d9c60010c9..ca1ba3d58cfd 100644
--- a/drivers/mmc/host/s3cmci.h
+++ b/drivers/mmc/host/s3cmci.h
@@ -51,7 +51,7 @@ struct s3cmci_host {
51 int dma_complete; 51 int dma_complete;
52 52
53 u32 pio_sgptr; 53 u32 pio_sgptr;
54 u32 pio_words; 54 u32 pio_bytes;
55 u32 pio_count; 55 u32 pio_count;
56 u32 *pio_ptr; 56 u32 *pio_ptr;
57#define XFER_NONE 0 57#define XFER_NONE 0
@@ -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};