aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/host/s3cmci.c80
-rw-r--r--drivers/mmc/host/s3cmci.h2
2 files changed, 54 insertions, 28 deletions
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index a73ffb9d7b21..bb412331e3d7 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -190,7 +190,7 @@ static inline void clear_imask(struct s3cmci_host *host)
190} 190}
191 191
192static inline int get_data_buffer(struct s3cmci_host *host, 192static inline int get_data_buffer(struct s3cmci_host *host,
193 u32 *words, u32 **pointer) 193 u32 *bytes, u32 **pointer)
194{ 194{
195 struct scatterlist *sg; 195 struct scatterlist *sg;
196 196
@@ -207,7 +207,7 @@ static inline int get_data_buffer(struct s3cmci_host *host,
207 } 207 }
208 sg = &host->mrq->data->sg[host->pio_sgptr]; 208 sg = &host->mrq->data->sg[host->pio_sgptr];
209 209
210 *words = sg->length >> 2; 210 *bytes = sg->length;
211 *pointer = sg_virt(sg); 211 *pointer = sg_virt(sg);
212 212
213 host->pio_sgptr++; 213 host->pio_sgptr++;
@@ -223,7 +223,7 @@ static inline u32 fifo_count(struct s3cmci_host *host)
223 u32 fifostat = readl(host->base + S3C2410_SDIFSTA); 223 u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
224 224
225 fifostat &= S3C2410_SDIFSTA_COUNTMASK; 225 fifostat &= S3C2410_SDIFSTA_COUNTMASK;
226 return fifostat >> 2; 226 return fifostat;
227} 227}
228 228
229static inline u32 fifo_free(struct s3cmci_host *host) 229static inline u32 fifo_free(struct s3cmci_host *host)
@@ -231,13 +231,14 @@ static inline u32 fifo_free(struct s3cmci_host *host)
231 u32 fifostat = readl(host->base + S3C2410_SDIFSTA); 231 u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
232 232
233 fifostat &= S3C2410_SDIFSTA_COUNTMASK; 233 fifostat &= S3C2410_SDIFSTA_COUNTMASK;
234 return (63 - fifostat) >> 2; 234 return 63 - fifostat;
235} 235}
236 236
237static void do_pio_read(struct s3cmci_host *host) 237static void do_pio_read(struct s3cmci_host *host)
238{ 238{
239 int res; 239 int res;
240 u32 fifo; 240 u32 fifo;
241 u32 fifo_words;
241 void __iomem *from_ptr; 242 void __iomem *from_ptr;
242 243
243 /* write real prescaler to host, it might be set slow to fix */ 244 /* write real prescaler to host, it might be set slow to fix */
@@ -246,8 +247,8 @@ static void do_pio_read(struct s3cmci_host *host)
246 from_ptr = host->base + host->sdidata; 247 from_ptr = host->base + host->sdidata;
247 248
248 while ((fifo = fifo_count(host))) { 249 while ((fifo = fifo_count(host))) {
249 if (!host->pio_words) { 250 if (!host->pio_bytes) {
250 res = get_data_buffer(host, &host->pio_words, 251 res = get_data_buffer(host, &host->pio_bytes,
251 &host->pio_ptr); 252 &host->pio_ptr);
252 if (res) { 253 if (res) {
253 host->pio_active = XFER_NONE; 254 host->pio_active = XFER_NONE;
@@ -260,26 +261,45 @@ static void do_pio_read(struct s3cmci_host *host)
260 261
261 dbg(host, dbg_pio, 262 dbg(host, dbg_pio,
262 "pio_read(): new target: [%i]@[%p]\n", 263 "pio_read(): new target: [%i]@[%p]\n",
263 host->pio_words, host->pio_ptr); 264 host->pio_bytes, host->pio_ptr);
264 } 265 }
265 266
266 dbg(host, dbg_pio, 267 dbg(host, dbg_pio,
267 "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", 268 "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n",
268 fifo, host->pio_words, 269 fifo, host->pio_bytes,
269 readl(host->base + S3C2410_SDIDCNT)); 270 readl(host->base + S3C2410_SDIDCNT));
270 271
271 if (fifo > host->pio_words) 272 /* If we have reached the end of the block, we can
272 fifo = host->pio_words; 273 * read a word and get 1 to 3 bytes. If we in the
274 * middle of the block, we have to read full words,
275 * otherwise we will write garbage, so round down to
276 * an even multiple of 4. */
277 if (fifo >= host->pio_bytes)
278 fifo = host->pio_bytes;
279 else
280 fifo -= fifo & 3;
273 281
274 host->pio_words -= fifo; 282 host->pio_bytes -= fifo;
275 host->pio_count += fifo; 283 host->pio_count += fifo;
276 284
277 while (fifo--) 285 fifo_words = fifo >> 2;
286 while (fifo_words--)
278 *(host->pio_ptr++) = readl(from_ptr); 287 *(host->pio_ptr++) = readl(from_ptr);
288
289 if (fifo & 3) {
290 u32 n = fifo & 3;
291 u32 data = readl(from_ptr);
292 u8 *p = (u8 *)host->pio_ptr;
293
294 while (n--) {
295 *p++ = data;
296 data >>= 8;
297 }
298 }
279 } 299 }
280 300
281 if (!host->pio_words) { 301 if (!host->pio_bytes) {
282 res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); 302 res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr);
283 if (res) { 303 if (res) {
284 dbg(host, dbg_pio, 304 dbg(host, dbg_pio,
285 "pio_read(): complete (no more buffers).\n"); 305 "pio_read(): complete (no more buffers).\n");
@@ -303,8 +323,8 @@ static void do_pio_write(struct s3cmci_host *host)
303 to_ptr = host->base + host->sdidata; 323 to_ptr = host->base + host->sdidata;
304 324
305 while ((fifo = fifo_free(host))) { 325 while ((fifo = fifo_free(host))) {
306 if (!host->pio_words) { 326 if (!host->pio_bytes) {
307 res = get_data_buffer(host, &host->pio_words, 327 res = get_data_buffer(host, &host->pio_bytes,
308 &host->pio_ptr); 328 &host->pio_ptr);
309 if (res) { 329 if (res) {
310 dbg(host, dbg_pio, 330 dbg(host, dbg_pio,
@@ -316,16 +336,23 @@ static void do_pio_write(struct s3cmci_host *host)
316 336
317 dbg(host, dbg_pio, 337 dbg(host, dbg_pio,
318 "pio_write(): new source: [%i]@[%p]\n", 338 "pio_write(): new source: [%i]@[%p]\n",
319 host->pio_words, host->pio_ptr); 339 host->pio_bytes, host->pio_ptr);
320 340
321 } 341 }
322 342
323 if (fifo > host->pio_words) 343 /* If we have reached the end of the block, we have to
324 fifo = host->pio_words; 344 * write exactly the remaining number of bytes. If we
345 * in the middle of the block, we have to write full
346 * words, so round down to an even multiple of 4. */
347 if (fifo >= host->pio_bytes)
348 fifo = host->pio_bytes;
349 else
350 fifo -= fifo & 3;
325 351
326 host->pio_words -= fifo; 352 host->pio_bytes -= fifo;
327 host->pio_count += fifo; 353 host->pio_count += fifo;
328 354
355 fifo = (fifo + 3) >> 2;
329 while (fifo--) 356 while (fifo--)
330 writel(*(host->pio_ptr++), to_ptr); 357 writel(*(host->pio_ptr++), to_ptr);
331 } 358 }
@@ -350,9 +377,9 @@ static void pio_tasklet(unsigned long data)
350 clear_imask(host); 377 clear_imask(host);
351 if (host->pio_active != XFER_NONE) { 378 if (host->pio_active != XFER_NONE) {
352 dbg(host, dbg_err, "unfinished %s " 379 dbg(host, dbg_err, "unfinished %s "
353 "- pio_count:[%u] pio_words:[%u]\n", 380 "- pio_count:[%u] pio_bytes:[%u]\n",
354 (host->pio_active == XFER_READ) ? "read" : "write", 381 (host->pio_active == XFER_READ) ? "read" : "write",
355 host->pio_count, host->pio_words); 382 host->pio_count, host->pio_bytes);
356 383
357 if (host->mrq->data) 384 if (host->mrq->data)
358 host->mrq->data->error = -EINVAL; 385 host->mrq->data->error = -EINVAL;
@@ -813,11 +840,10 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
813 /* We cannot deal with unaligned blocks with more than 840 /* We cannot deal with unaligned blocks with more than
814 * one block being transfered. */ 841 * one block being transfered. */
815 842
816 if (data->blocks > 1) 843 if (data->blocks > 1) {
844 pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);
817 return -EINVAL; 845 return -EINVAL;
818 846 }
819 /* No support yet for non-word block transfers. */
820 return -EINVAL;
821 } 847 }
822 848
823 while (readl(host->base + S3C2410_SDIDSTA) & 849 while (readl(host->base + S3C2410_SDIDSTA) &
@@ -897,7 +923,7 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
897 BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); 923 BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
898 924
899 host->pio_sgptr = 0; 925 host->pio_sgptr = 0;
900 host->pio_words = 0; 926 host->pio_bytes = 0;
901 host->pio_count = 0; 927 host->pio_count = 0;
902 host->pio_active = rw ? XFER_WRITE : XFER_READ; 928 host->pio_active = rw ? XFER_WRITE : XFER_READ;
903 929
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
index 7e39109587f9..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