aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorAlex Dubov <oakad@yahoo.com>2007-04-13 13:04:38 -0400
committerPierre Ossman <drzeus@drzeus.cx>2007-05-01 07:04:12 -0400
commit14d836e7499c53a1f6a65086c3d11600e871a971 (patch)
tree37d1cb767422bd498a13654ecabd6f891b27b0e3 /drivers/mmc
parentdc87c3985e9b442c60994308a96f887579addc39 (diff)
mmc: cull sg list to match mmc request size
mmc layer may introduce additional (compared to block layer) limits on request size. Culling of the sg list to match adjusted request size simplifies the handling of such cases in the low level driver, allowing it to skip block count checks while processing sg entries. (fixes for wbsd and sdhci by Pierre Ossman) Signed-off-by: Alex Dubov <oakad@yahoo.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/mmc_block.c16
-rw-r--r--drivers/mmc/sdhci.c24
-rw-r--r--drivers/mmc/sdhci.h4
-rw-r--r--drivers/mmc/wbsd.c131
-rw-r--r--drivers/mmc/wbsd.h4
5 files changed, 50 insertions, 129 deletions
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index 86439a0bb271..95b0da6abe87 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -223,7 +223,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
223 struct mmc_blk_data *md = mq->data; 223 struct mmc_blk_data *md = mq->data;
224 struct mmc_card *card = md->queue.card; 224 struct mmc_card *card = md->queue.card;
225 struct mmc_blk_request brq; 225 struct mmc_blk_request brq;
226 int ret = 1; 226 int ret = 1, sg_pos, data_size;
227 227
228 if (mmc_card_claim_host(card)) 228 if (mmc_card_claim_host(card))
229 goto flush_queue; 229 goto flush_queue;
@@ -283,6 +283,20 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
283 brq.data.sg = mq->sg; 283 brq.data.sg = mq->sg;
284 brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); 284 brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);
285 285
286 if (brq.data.blocks !=
287 (req->nr_sectors >> (md->block_bits - 9))) {
288 data_size = brq.data.blocks * brq.data.blksz;
289 for (sg_pos = 0; sg_pos < brq.data.sg_len; sg_pos++) {
290 data_size -= mq->sg[sg_pos].length;
291 if (data_size <= 0) {
292 mq->sg[sg_pos].length += data_size;
293 sg_pos++;
294 break;
295 }
296 }
297 brq.data.sg_len = sg_pos;
298 }
299
286 mmc_wait_for_req(card->host, &brq.mrq); 300 mmc_wait_for_req(card->host, &brq.mrq);
287 if (brq.cmd.error) { 301 if (brq.cmd.error) {
288 printk(KERN_ERR "%s: error %d sending read/write command\n", 302 printk(KERN_ERR "%s: error %d sending read/write command\n",
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index d749f08601b8..587dccf95f8e 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver 2 * linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver
3 * 3 *
4 * Copyright (C) 2005-2006 Pierre Ossman, All Rights Reserved. 4 * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 7 * it under the terms of the GNU General Public License as published by
@@ -247,14 +247,13 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
247 chunk_remain = min(blksize, 4); 247 chunk_remain = min(blksize, 4);
248 } 248 }
249 249
250 size = min(host->size, host->remain); 250 size = min(host->remain, chunk_remain);
251 size = min(size, chunk_remain);
252 251
253 chunk_remain -= size; 252 chunk_remain -= size;
254 blksize -= size; 253 blksize -= size;
255 host->offset += size; 254 host->offset += size;
256 host->remain -= size; 255 host->remain -= size;
257 host->size -= size; 256
258 while (size) { 257 while (size) {
259 *buffer = data & 0xFF; 258 *buffer = data & 0xFF;
260 buffer++; 259 buffer++;
@@ -289,14 +288,13 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
289 buffer = sdhci_sg_to_buffer(host) + host->offset; 288 buffer = sdhci_sg_to_buffer(host) + host->offset;
290 289
291 while (blksize) { 290 while (blksize) {
292 size = min(host->size, host->remain); 291 size = min(host->remain, chunk_remain);
293 size = min(size, chunk_remain);
294 292
295 chunk_remain -= size; 293 chunk_remain -= size;
296 blksize -= size; 294 blksize -= size;
297 host->offset += size; 295 host->offset += size;
298 host->remain -= size; 296 host->remain -= size;
299 host->size -= size; 297
300 while (size) { 298 while (size) {
301 data >>= 8; 299 data >>= 8;
302 data |= (u32)*buffer << 24; 300 data |= (u32)*buffer << 24;
@@ -325,7 +323,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
325 323
326 BUG_ON(!host->data); 324 BUG_ON(!host->data);
327 325
328 if (host->size == 0) 326 if (host->num_sg == 0)
329 return; 327 return;
330 328
331 if (host->data->flags & MMC_DATA_READ) 329 if (host->data->flags & MMC_DATA_READ)
@@ -339,10 +337,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
339 else 337 else
340 sdhci_write_block_pio(host); 338 sdhci_write_block_pio(host);
341 339
342 if (host->size == 0) 340 if (host->num_sg == 0)
343 break; 341 break;
344
345 BUG_ON(host->num_sg == 0);
346 } 342 }
347 343
348 DBG("PIO transfer complete.\n"); 344 DBG("PIO transfer complete.\n");
@@ -408,8 +404,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
408 404
409 writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS); 405 writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS);
410 } else { 406 } else {
411 host->size = data->blksz * data->blocks;
412
413 host->cur_sg = data->sg; 407 host->cur_sg = data->sg;
414 host->num_sg = data->sg_len; 408 host->num_sg = data->sg_len;
415 409
@@ -473,10 +467,6 @@ static void sdhci_finish_data(struct sdhci_host *host)
473 "though there were blocks left.\n", 467 "though there were blocks left.\n",
474 mmc_hostname(host->mmc)); 468 mmc_hostname(host->mmc));
475 data->error = MMC_ERR_FAILED; 469 data->error = MMC_ERR_FAILED;
476 } else if (host->size != 0) {
477 printk(KERN_ERR "%s: %d bytes were left untransferred.\n",
478 mmc_hostname(host->mmc), host->size);
479 data->error = MMC_ERR_FAILED;
480 } 470 }
481 471
482 DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered); 472 DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered);
diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h
index e324f0a623dc..7400f4bc114f 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver 2 * linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver
3 * 3 *
4 * Copyright (C) 2005 Pierre Ossman, All Rights Reserved. 4 * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 7 * it under the terms of the GNU General Public License as published by
@@ -187,8 +187,6 @@ struct sdhci_host {
187 int offset; /* Offset into current sg */ 187 int offset; /* Offset into current sg */
188 int remain; /* Bytes left in current */ 188 int remain; /* Bytes left in current */
189 189
190 int size; /* Remaining bytes in transfer */
191
192 char slot_descr[20]; /* Name for reservations */ 190 char slot_descr[20]; /* Name for reservations */
193 191
194 int irq; /* Device IRQ */ 192 int irq; /* Device IRQ */
diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c
index 05ccfc43168f..7a3e32ec46b8 100644
--- a/drivers/mmc/wbsd.c
+++ b/drivers/mmc/wbsd.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver 2 * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
3 * 3 *
4 * Copyright (C) 2004-2006 Pierre Ossman, All Rights Reserved. 4 * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved.
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 7 * it under the terms of the GNU General Public License as published by
@@ -278,90 +278,36 @@ static inline char *wbsd_sg_to_buffer(struct wbsd_host *host)
278 278
279static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) 279static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
280{ 280{
281 unsigned int len, i, size; 281 unsigned int len, i;
282 struct scatterlist *sg; 282 struct scatterlist *sg;
283 char *dmabuf = host->dma_buffer; 283 char *dmabuf = host->dma_buffer;
284 char *sgbuf; 284 char *sgbuf;
285 285
286 size = host->size;
287
288 sg = data->sg; 286 sg = data->sg;
289 len = data->sg_len; 287 len = data->sg_len;
290 288
291 /*
292 * Just loop through all entries. Size might not
293 * be the entire list though so make sure that
294 * we do not transfer too much.
295 */
296 for (i = 0; i < len; i++) { 289 for (i = 0; i < len; i++) {
297 sgbuf = page_address(sg[i].page) + sg[i].offset; 290 sgbuf = page_address(sg[i].page) + sg[i].offset;
298 if (size < sg[i].length) 291 memcpy(dmabuf, sgbuf, sg[i].length);
299 memcpy(dmabuf, sgbuf, size);
300 else
301 memcpy(dmabuf, sgbuf, sg[i].length);
302 dmabuf += sg[i].length; 292 dmabuf += sg[i].length;
303
304 if (size < sg[i].length)
305 size = 0;
306 else
307 size -= sg[i].length;
308
309 if (size == 0)
310 break;
311 } 293 }
312
313 /*
314 * Check that we didn't get a request to transfer
315 * more data than can fit into the SG list.
316 */
317
318 BUG_ON(size != 0);
319
320 host->size -= size;
321} 294}
322 295
323static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) 296static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
324{ 297{
325 unsigned int len, i, size; 298 unsigned int len, i;
326 struct scatterlist *sg; 299 struct scatterlist *sg;
327 char *dmabuf = host->dma_buffer; 300 char *dmabuf = host->dma_buffer;
328 char *sgbuf; 301 char *sgbuf;
329 302
330 size = host->size;
331
332 sg = data->sg; 303 sg = data->sg;
333 len = data->sg_len; 304 len = data->sg_len;
334 305
335 /*
336 * Just loop through all entries. Size might not
337 * be the entire list though so make sure that
338 * we do not transfer too much.
339 */
340 for (i = 0; i < len; i++) { 306 for (i = 0; i < len; i++) {
341 sgbuf = page_address(sg[i].page) + sg[i].offset; 307 sgbuf = page_address(sg[i].page) + sg[i].offset;
342 if (size < sg[i].length) 308 memcpy(sgbuf, dmabuf, sg[i].length);
343 memcpy(sgbuf, dmabuf, size);
344 else
345 memcpy(sgbuf, dmabuf, sg[i].length);
346 dmabuf += sg[i].length; 309 dmabuf += sg[i].length;
347
348 if (size < sg[i].length)
349 size = 0;
350 else
351 size -= sg[i].length;
352
353 if (size == 0)
354 break;
355 } 310 }
356
357 /*
358 * Check that we didn't get a request to transfer
359 * more data than can fit into the SG list.
360 */
361
362 BUG_ON(size != 0);
363
364 host->size -= size;
365} 311}
366 312
367/* 313/*
@@ -484,7 +430,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
484 /* 430 /*
485 * Handle excessive data. 431 * Handle excessive data.
486 */ 432 */
487 if (data->bytes_xfered == host->size) 433 if (host->num_sg == 0)
488 return; 434 return;
489 435
490 buffer = wbsd_sg_to_buffer(host) + host->offset; 436 buffer = wbsd_sg_to_buffer(host) + host->offset;
@@ -514,31 +460,14 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
514 data->bytes_xfered++; 460 data->bytes_xfered++;
515 461
516 /* 462 /*
517 * Transfer done?
518 */
519 if (data->bytes_xfered == host->size)
520 return;
521
522 /*
523 * End of scatter list entry? 463 * End of scatter list entry?
524 */ 464 */
525 if (host->remain == 0) { 465 if (host->remain == 0) {
526 /* 466 /*
527 * Get next entry. Check if last. 467 * Get next entry. Check if last.
528 */ 468 */
529 if (!wbsd_next_sg(host)) { 469 if (!wbsd_next_sg(host))
530 /*
531 * We should never reach this point.
532 * It means that we're trying to
533 * transfer more blocks than can fit
534 * into the scatter list.
535 */
536 BUG_ON(1);
537
538 host->size = data->bytes_xfered;
539
540 return; 470 return;
541 }
542 471
543 buffer = wbsd_sg_to_buffer(host); 472 buffer = wbsd_sg_to_buffer(host);
544 } 473 }
@@ -550,7 +479,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
550 * hardware problem. The chip doesn't trigger 479 * hardware problem. The chip doesn't trigger
551 * FIFO threshold interrupts properly. 480 * FIFO threshold interrupts properly.
552 */ 481 */
553 if ((host->size - data->bytes_xfered) < 16) 482 if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
554 tasklet_schedule(&host->fifo_tasklet); 483 tasklet_schedule(&host->fifo_tasklet);
555} 484}
556 485
@@ -564,7 +493,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
564 * Check that we aren't being called after the 493 * Check that we aren't being called after the
565 * entire buffer has been transfered. 494 * entire buffer has been transfered.
566 */ 495 */
567 if (data->bytes_xfered == host->size) 496 if (host->num_sg == 0)
568 return; 497 return;
569 498
570 buffer = wbsd_sg_to_buffer(host) + host->offset; 499 buffer = wbsd_sg_to_buffer(host) + host->offset;
@@ -594,31 +523,14 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
594 data->bytes_xfered++; 523 data->bytes_xfered++;
595 524
596 /* 525 /*
597 * Transfer done?
598 */
599 if (data->bytes_xfered == host->size)
600 return;
601
602 /*
603 * End of scatter list entry? 526 * End of scatter list entry?
604 */ 527 */
605 if (host->remain == 0) { 528 if (host->remain == 0) {
606 /* 529 /*
607 * Get next entry. Check if last. 530 * Get next entry. Check if last.
608 */ 531 */
609 if (!wbsd_next_sg(host)) { 532 if (!wbsd_next_sg(host))
610 /*
611 * We should never reach this point.
612 * It means that we're trying to
613 * transfer more blocks than can fit
614 * into the scatter list.
615 */
616 BUG_ON(1);
617
618 host->size = data->bytes_xfered;
619
620 return; 533 return;
621 }
622 534
623 buffer = wbsd_sg_to_buffer(host); 535 buffer = wbsd_sg_to_buffer(host);
624 } 536 }
@@ -638,6 +550,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
638 u16 blksize; 550 u16 blksize;
639 u8 setup; 551 u8 setup;
640 unsigned long dmaflags; 552 unsigned long dmaflags;
553 unsigned int size;
641 554
642 DBGF("blksz %04x blks %04x flags %08x\n", 555 DBGF("blksz %04x blks %04x flags %08x\n",
643 data->blksz, data->blocks, data->flags); 556 data->blksz, data->blocks, data->flags);
@@ -647,7 +560,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
647 /* 560 /*
648 * Calculate size. 561 * Calculate size.
649 */ 562 */
650 host->size = data->blocks * data->blksz; 563 size = data->blocks * data->blksz;
651 564
652 /* 565 /*
653 * Check timeout values for overflow. 566 * Check timeout values for overflow.
@@ -705,8 +618,8 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
705 /* 618 /*
706 * The buffer for DMA is only 64 kB. 619 * The buffer for DMA is only 64 kB.
707 */ 620 */
708 BUG_ON(host->size > 0x10000); 621 BUG_ON(size > 0x10000);
709 if (host->size > 0x10000) { 622 if (size > 0x10000) {
710 data->error = MMC_ERR_INVALID; 623 data->error = MMC_ERR_INVALID;
711 return; 624 return;
712 } 625 }
@@ -729,7 +642,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
729 else 642 else
730 set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); 643 set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
731 set_dma_addr(host->dma, host->dma_addr); 644 set_dma_addr(host->dma, host->dma_addr);
732 set_dma_count(host->dma, host->size); 645 set_dma_count(host->dma, size);
733 646
734 enable_dma(host->dma); 647 enable_dma(host->dma);
735 release_dma_lock(dmaflags); 648 release_dma_lock(dmaflags);
@@ -812,6 +725,10 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
812 count = get_dma_residue(host->dma); 725 count = get_dma_residue(host->dma);
813 release_dma_lock(dmaflags); 726 release_dma_lock(dmaflags);
814 727
728 data->bytes_xfered = host->mrq->data->blocks *
729 host->mrq->data->blksz - count;
730 data->bytes_xfered -= data->bytes_xfered % data->blksz;
731
815 /* 732 /*
816 * Any leftover data? 733 * Any leftover data?
817 */ 734 */
@@ -820,7 +737,8 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
820 "%d bytes left.\n", 737 "%d bytes left.\n",
821 mmc_hostname(host->mmc), count); 738 mmc_hostname(host->mmc), count);
822 739
823 data->error = MMC_ERR_FAILED; 740 if (data->error == MMC_ERR_NONE)
741 data->error = MMC_ERR_FAILED;
824 } else { 742 } else {
825 /* 743 /*
826 * Transfer data from DMA buffer to 744 * Transfer data from DMA buffer to
@@ -828,8 +746,11 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
828 */ 746 */
829 if (data->flags & MMC_DATA_READ) 747 if (data->flags & MMC_DATA_READ)
830 wbsd_dma_to_sg(host, data); 748 wbsd_dma_to_sg(host, data);
749 }
831 750
832 data->bytes_xfered = host->size; 751 if (data->error != MMC_ERR_NONE) {
752 if (data->bytes_xfered)
753 data->bytes_xfered -= data->blksz;
833 } 754 }
834 } 755 }
835 756
@@ -1167,7 +1088,7 @@ static void wbsd_tasklet_fifo(unsigned long param)
1167 /* 1088 /*
1168 * Done? 1089 * Done?
1169 */ 1090 */
1170 if (host->size == data->bytes_xfered) { 1091 if (host->num_sg == 0) {
1171 wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); 1092 wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
1172 tasklet_schedule(&host->finish_tasklet); 1093 tasklet_schedule(&host->finish_tasklet);
1173 } 1094 }
diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h
index d06718b0e2ab..6fb4fa423219 100644
--- a/drivers/mmc/wbsd.h
+++ b/drivers/mmc/wbsd.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver 2 * linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver
3 * 3 *
4 * Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved. 4 * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved.
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 7 * it under the terms of the GNU General Public License as published by
@@ -158,8 +158,6 @@ struct wbsd_host
158 unsigned int offset; /* Offset into current entry */ 158 unsigned int offset; /* Offset into current entry */
159 unsigned int remain; /* Data left in curren entry */ 159 unsigned int remain; /* Data left in curren entry */
160 160
161 int size; /* Total size of transfer */
162
163 char* dma_buffer; /* ISA DMA buffer */ 161 char* dma_buffer; /* ISA DMA buffer */
164 dma_addr_t dma_addr; /* Physical address for same */ 162 dma_addr_t dma_addr; /* Physical address for same */
165 163