[U-Boot] [PATCH RFC] mmc: Add multi-block read support to the generic mmc driver

Steve Sakoman steve at sakoman.com
Thu Oct 28 19:17:51 CEST 2010


From: Alagu Sankar <alagusankar at embwise.com>

This patch adds multi-block read support for the generic MMC
driver. Large reads are broken into chunks of 65535 blocks to
ensure that the code works with controllers having a 16 bit block counter.

This patch results in a significant performance improvement.

Time to read a 45 MB file went from 36 seconds to 9 seconds on Overo

Signed-off-by: Steve Sakoman <steve.sakoman at linaro.org>
Tested-by: Steve Sakoman <steve.sakoman at linaro.org>
---

This is based on Alagu's original patch, but heavily modified to reflect
recent changes in the driver and to remove the CONFIG option as
requested in the earlier discussion of the patch:

http://www.mail-archive.com/u-boot@lists.denx.de/msg32319.html

It also incorporates feedback on my previously submitted RFC multi-block
read patch. I am leaving authorship with Alagu since he did the original
work, but will leave it up to him to add his "Signed-off-by"
---

diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 00fe867..6805b33 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -154,110 +154,78 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
 	return blkcnt;
 }
 
-int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum)
+int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)
 {
 	struct mmc_cmd cmd;
 	struct mmc_data data;
 
-	cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
+	if (blkcnt > 1)
+		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+	else
+		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
 
 	if (mmc->high_capacity)
-		cmd.cmdarg = blocknum;
+		cmd.cmdarg = start;
 	else
-		cmd.cmdarg = blocknum * mmc->read_bl_len;
+		cmd.cmdarg = start * mmc->read_bl_len;
 
 	cmd.resp_type = MMC_RSP_R1;
 	cmd.flags = 0;
 
 	data.dest = dst;
-	data.blocks = 1;
+	data.blocks = blkcnt;
 	data.blocksize = mmc->read_bl_len;
 	data.flags = MMC_DATA_READ;
 
-	return mmc_send_cmd(mmc, &cmd, &data);
-}
-
-int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size)
-{
-	char *buffer;
-	int i;
-	int blklen = mmc->read_bl_len;
-	int startblock = lldiv(src, mmc->read_bl_len);
-	int endblock = lldiv(src + size - 1, mmc->read_bl_len);
-	int err = 0;
-
-	/* Make a buffer big enough to hold all the blocks we might read */
-	buffer = malloc(blklen);
-
-	if (!buffer) {
-		printf("Could not allocate buffer for MMC read!\n");
-		return -1;
-	}
-
-	/* We always do full block reads from the card */
-	err = mmc_set_blocklen(mmc, mmc->read_bl_len);
-
-	if (err)
-		goto free_buffer;
-
-	for (i = startblock; i <= endblock; i++) {
-		int segment_size;
-		int offset;
-
-		err = mmc_read_block(mmc, buffer, i);
-
-		if (err)
-			goto free_buffer;
-
-		/*
-		 * The first block may not be aligned, so we
-		 * copy from the desired point in the block
-		 */
-		offset = (src & (blklen - 1));
-		segment_size = MIN(blklen - offset, size);
-
-		memcpy(dst, buffer + offset, segment_size);
+	if (mmc_send_cmd(mmc, &cmd, &data))
+		return 0;
 
-		dst += segment_size;
-		src += segment_size;
-		size -= segment_size;
+	if (blkcnt > 1) {
+		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+		cmd.cmdarg = 0;
+		cmd.resp_type = MMC_RSP_R1b;
+		cmd.flags = 0;
+		if (mmc_send_cmd(mmc, &cmd, NULL)) {
+			printf("mmc fail to send stop cmd\n");
+			return 0;
+		}
 	}
 
-free_buffer:
-	free(buffer);
-
-	return err;
+	return blkcnt;
 }
 
 static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
 {
-	int err;
-	int i;
-	struct mmc *mmc = find_mmc_device(dev_num);
+	lbaint_t cur, blocks_todo = blkcnt;
+
+	if (blkcnt == 0)
+		return 0;
 
+	struct mmc *mmc = find_mmc_device(dev_num);
 	if (!mmc)
 		return 0;
 
 	if ((start + blkcnt) > mmc->block_dev.lba) {
-		printf("MMC: block number 0x%lx exceeds max(0x%lx)",
+		printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
 			start + blkcnt, mmc->block_dev.lba);
 		return 0;
 	}
-	/* We always do full block reads from the card */
-	err = mmc_set_blocklen(mmc, mmc->read_bl_len);
 
-	if (err) {
+	if (mmc_set_blocklen(mmc, mmc->read_bl_len))
 		return 0;
-	}
 
-	for (i = start; i < start + blkcnt; i++, dst += mmc->read_bl_len) {
-		err = mmc_read_block(mmc, dst, i);
-
-		if (err) {
-			printf("block read failed: %d\n", err);
-			return i - start;
-		}
-	}
+	do {
+		/*
+		 * The 65535 constraint comes from some hardware has
+		 * only 16 bit width block number counter
+		 */
+		cur = (blocks_todo > 65535) ? 65535 : blocks_todo;
+		if(mmc_read_blocks(mmc, dst, start, cur) != cur)
+			return 0;
+		blocks_todo -= cur;
+		start += cur;
+		dst += cur * mmc->read_bl_len;
+	} while (blocks_todo > 0);
 
 	return blkcnt;
 }



More information about the U-Boot mailing list