Re: SD card multiblock read/writes ??

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Aug 25, 2006 at 05:10:56PM -0700, Jeff Hane wrote:
> On Fri, 2006-08-25 at 15:04 -0700, David Brownell wrote:
> > On Friday 25 August 2006 1:24 pm, Jeff Hane wrote:
> > 
> > >   So am curious to know why multiblock writes are not enable.
> > 
> > ISTR because one of the early MMC implementations, for pxa250,
> > has execrable fault reporting which prevents reporting how
> > many blocks were actually transferred after a fault.
> > 
> > You didn't say what platform you're using, but I thought some
> > of them _do_ support multiblock writes.
> 
>  It's an in-house board using a at91rm2000.  However, the current code
> that restricts multiblock writes is in mmc_block.c which is common to
> all platforms.  Thats why I wanted to know if anybody has made changes
> to make it work for their board and if there were any negative side-
> effects.

You might find this patch interesting.  However, you must only ever
set MMC_CAP_MULTI_WRITE if your host driver returns the number of
bytes successfully transferred to the card prior to a bus error
occuring during the data phase.

Failure to observe that restriction will result in data corruption
on bus errors.

Signed-off-by: Russell King <rmk+kernel@xxxxxxxxxxxxxxxx>

diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index bc69c0b..b375536 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -31,6 +31,7 @@ #include <linux/mutex.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/protocol.h>
+#include <linux/mmc/host.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -164,6 +165,7 @@ static int mmc_blk_issue_rq(struct mmc_q
 	do {
 		struct mmc_blk_request brq;
 		struct mmc_command cmd;
+		u32 readcmd, writecmd;
 
 		memset(&brq, 0, sizeof(struct mmc_blk_request));
 		brq.mrq.cmd = &brq.cmd;
@@ -179,13 +181,31 @@ static int mmc_blk_issue_rq(struct mmc_q
 		brq.stop.arg = 0;
 		brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
 
+		/*
+		 * If the host doesn't support multiple block writes, force
+		 * block writes to single block.
+		 */
+		if (rq_data_dir(req) != READ &&
+		    !(card->host->caps & MMC_CAP_MULTI_WRITE))
+			brq.data.blocks = 1;
+
+		if (brq.data.blocks > 1) {
+			brq.data.flags |= MMC_DATA_MULTI;
+			brq.mrq.stop = &brq.stop;
+			readcmd = MMC_READ_MULTIPLE_BLOCK;
+			writecmd = MMC_WRITE_MULTIPLE_BLOCK;
+		} else {
+			brq.mrq.stop = NULL;
+			readcmd = MMC_READ_SINGLE_BLOCK;
+			writecmd = MMC_WRITE_BLOCK;
+		}
+
 		if (rq_data_dir(req) == READ) {
-			brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
+			brq.cmd.opcode = readcmd;
 			brq.data.flags |= MMC_DATA_READ;
 		} else {
-			brq.cmd.opcode = MMC_WRITE_BLOCK;
+			brq.cmd.opcode = writecmd;
 			brq.data.flags |= MMC_DATA_WRITE;
-			brq.data.blocks = 1;
 
 			/*
 			 * Scale up the timeout by the r2w factor
@@ -194,13 +214,6 @@ static int mmc_blk_issue_rq(struct mmc_q
 			brq.data.timeout_clks <<= card->csd.r2w_factor;
 		}
 
-		if (brq.data.blocks > 1) {
-			brq.data.flags |= MMC_DATA_MULTI;
-			brq.mrq.stop = &brq.stop;
-		} else {
-			brq.mrq.stop = NULL;
-		}
-
 		brq.data.sg = mq->sg;
 		brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);
 
diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c
index 8419489..2256d64 100644
--- a/drivers/mmc/mmci.c
+++ b/drivers/mmc/mmci.c
@@ -509,6 +509,7 @@ static int mmci_probe(struct amba_device
 	mmc->f_min = (host->mclk + 511) / 512;
 	mmc->f_max = min(host->mclk, fmax);
 	mmc->ocr_avail = plat->ocr_mask;
+	mmc->caps = MMC_CAP_MULTI_WRITE;
 
 	/*
 	 * We can do SGIO
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index c1f021e..fd909ca 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -85,6 +85,7 @@ struct mmc_host {
 	unsigned long		caps;		/* Host capabilities */
 
 #define MMC_CAP_4_BIT_DATA	(1 << 0)	/* Can the host do 4 bit transfers */
+#define MMC_CAP_MULTI_WRITE	(1 << 1)	/* Can do multiple block writes */
 
 	/* host specific block data */
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */

-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ:        http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette:  http://www.arm.linux.org.uk/mailinglists/etiquette.php

[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [CentOS ARM]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]     [Photos]