Skip to contentSkip to conversation

mmc: Add support for SDHC cards (Take 4)

PATCH In review Philip Langdale 2 participants 2 messages Jan 4, 2007

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 mmc: Add support for SDHC cards (Take 4)

Thanks to the generous donation of an SDHC card by John Gilmore, and the surprisingly enlightened decision by the SD Card Association to publish useful specs, I've been able to bash out support for SDHC. The changes are not too profound:

i) Add a card flag indicating the card uses block level addressing and check it in the block driver. As we never took advantage of byte-level addressing, this simply involves skipping the block -> byte translation when sending commands.

ii) The layout of the CSD is changed - a set of fields are discarded to make space for a larger C_SIZE. We did not reference any of the discarded fields except those related to the C_SIZE.

iii) Read and write timeouts are fixed values and not calculated from CSD values.

iv) Before invoking SEND_APP_OP_COND, we must invoke the new SEND_IF_COND to inform the card we support SDHC.

I've done some basic read and write tests and everything seems to work fine but one should obviously use caution in case it eats your data.

In the process of developing this patch, we realised that the R6 response definition was incorrect and that it should have been identical to R1 (and so should R7). Correcting this mistake revealed problems in a couple of host controller drivers that relied on the response definitions to be unique. As we need a story for R7, I've also fixed up the R6 handling in the affected drivers but I have no idea if it will work in practice as I lack the hardware.

---

 drivers/mmc/imxmmc.c         |   14 +++-
 drivers/mmc/mmc.c            |  138 ++++++++++++++++++++++++++++++++++---------
 drivers/mmc/mmc_block.c      |    8 ++
 drivers/mmc/omap.c           |    2
 drivers/mmc/pxamci.c         |    2
 drivers/mmc/tifm_sd.c        |   14 +++-
 include/linux/mmc/card.h     |    3
 include/linux/mmc/mmc.h      |    3
 include/linux/mmc/protocol.h |   13 +++-
 9 files changed, 157 insertions(+), 40 deletions(-)
drivers/mmc/imxmmc.c renamed from usr/src/linux/drivers/mmc/imxmmc.c +10 -4
@@ -343,7 +343,16 @@
 	switch (mmc_resp_type(cmd)) {
 	case MMC_RSP_R1: /* short CRC, OPCODE */
 	case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */
-		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1;
+		switch (cmd->opcode) {
+		case SD_SEND_RELATIVE_ADDR:
+			cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R6;
+			break;
+		case SD_SEND_IF_COND:
+			/* Assuming R7 should be mapped to R1. */
+		default:
+			cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1;
+			break;
+		}
 		break;
 	case MMC_RSP_R2: /* long 136 bit + CRC */
 		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2;
@@ -351,9 +360,6 @@
 	case MMC_RSP_R3: /* short */
 		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3;
 		break;
-	case MMC_RSP_R6: /* short CRC */
-		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R6;
-		break;
 	default:
 		break;
 	}
drivers/mmc/mmc.c renamed from usr/src/linux/drivers/mmc/mmc.c +111 -27

174 lines not on this page · raw message

drivers/mmc/mmc_block.c renamed from usr/src/linux/drivers/mmc/mmc_block.c +7 -1
@@ -237,7 +237,9 @@
 		brq.mrq.cmd = &brq.cmd;
 		brq.mrq.data = &brq.data;
 
-		brq.cmd.arg = req->sector << 9;
+		brq.cmd.arg = req->sector;
+		if (!mmc_card_blockaddr(card))
+			brq.cmd.arg <<= 9;
 		brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 		brq.data.blksz = 1 << md->block_bits;
 		brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
@@ -494,6 +496,10 @@
 	struct mmc_command cmd;
 	int err;
 
+	/* Block-addressed cards ignore MMC_SET_BLOCKLEN. */
+	if (mmc_card_blockaddr(card))
+		return 0;
+
 	mmc_card_claim_host(card);
 	cmd.opcode = MMC_SET_BLOCKLEN;
 	cmd.arg = 1 << md->block_bits;
drivers/mmc/omap.c renamed from usr/src/linux/drivers/mmc/omap.c +1 -1
@@ -206,7 +206,7 @@
 	/* Our hardware needs to know exact type */
 	switch (RSP_TYPE(mmc_resp_type(cmd))) {
 	case RSP_TYPE(MMC_RSP_R1):
-		/* resp 1, resp 1b */
+		/* resp 1, 1b, 6, 7 */
 		resptype = 1;
 		break;
 	case RSP_TYPE(MMC_RSP_R2):
drivers/mmc/pxamci.c renamed from usr/src/linux/drivers/mmc/pxamci.c +1 -1
@@ -171,7 +171,7 @@
 
 #define RSP_TYPE(x)	((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
 	switch (RSP_TYPE(mmc_resp_type(cmd))) {
-	case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6 */
+	case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */
 		cmdat |= CMDAT_RESP_SHORT;
 		break;
 	case RSP_TYPE(MMC_RSP_R3):
drivers/mmc/tifm_sd.c renamed from usr/src/linux/drivers/mmc/tifm_sd.c +10 -4
@@ -165,7 +165,16 @@
 	case MMC_RSP_R1B:
 		rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through
 	case MMC_RSP_R1:
-		rc |= TIFM_MMCSD_RSP_R1;
+		switch (cmd->opcode) {
+		case SD_SEND_RELATIVE_ADDR:
+			rc |= TIFM_MMCSD_RSP_R6;
+			break;
+		case SD_SEND_IF_COND:
+			/* Assuming R7 should be mapped to R1. */
+		default:
+			rc |= TIFM_MMCSD_RSP_R1;
+			break;
+		}
 		break;
 	case MMC_RSP_R2:
 		rc |= TIFM_MMCSD_RSP_R2;
@@ -173,9 +182,6 @@
 	case MMC_RSP_R3:
 		rc |= TIFM_MMCSD_RSP_R3;
 		break;
-	case MMC_RSP_R6:
-		rc |= TIFM_MMCSD_RSP_R6;
-		break;
 	default:
 		BUG();
 	}
include/linux/mmc/card.h renamed from usr/src/linux/include/linux/mmc/card.h +3 -0
@@ -71,6 +71,7 @@
 #define MMC_STATE_SDCARD	(1<<3)		/* is an SD card */
 #define MMC_STATE_READONLY	(1<<4)		/* card is read-only */
 #define MMC_STATE_HIGHSPEED	(1<<5)		/* card is in high speed mode */
+#define MMC_STATE_BLOCKADDR	(1<<6)		/* card uses block-addressing */
 	u32			raw_cid[4];	/* raw card CID */
 	u32			raw_csd[4];	/* raw card CSD */
 	u32			raw_scr[2];	/* raw card SCR */
@@ -87,6 +88,7 @@
 #define mmc_card_sd(c)		((c)->state & MMC_STATE_SDCARD)
 #define mmc_card_readonly(c)	((c)->state & MMC_STATE_READONLY)
 #define mmc_card_highspeed(c)	((c)->state & MMC_STATE_HIGHSPEED)
+#define mmc_card_blockaddr(c)	((c)->state & MMC_STATE_BLOCKADDR)
 
 #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_dead(c)	((c)->state |= MMC_STATE_DEAD)
@@ -94,6 +96,7 @@
 #define mmc_card_set_sd(c)	((c)->state |= MMC_STATE_SDCARD)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
 #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
+#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
 
 #define mmc_card_name(c)	((c)->cid.prod_name)
 #define mmc_card_id(c)		((c)->dev.bus_id)
include/linux/mmc/mmc.h renamed from usr/src/linux/include/linux/mmc/mmc.h +2 -1
@@ -42,7 +42,8 @@
 #define MMC_RSP_R1B	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
 #define MMC_RSP_R2	(MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
 #define MMC_RSP_R3	(MMC_RSP_PRESENT)
-#define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC)
+#define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 
 #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
 
include/linux/mmc/protocol.h renamed from usr/src/linux/include/linux/mmc/protocol.h +12 -1
@@ -79,9 +79,12 @@
 #define MMC_GEN_CMD              56   /* adtc [0] RD/WR          R1  */
 
 /* SD commands                           type  argument     response */
-  /* class 8 */
+  /* class 0 */
 /* This is basically the same command as for MMC with some quirks. */
 #define SD_SEND_RELATIVE_ADDR     3   /* bcr                     R6  */
+#define SD_SEND_IF_COND           8   /* bcr  [11:0] See below   R7  */
+
+  /* class 10 */
 #define SD_SWITCH                 6   /* adtc [31:0] See below   R1  */
 
   /* Application commands */
@@ -115,6 +118,14 @@
  */
 
 /*
+ * SD_SEND_IF_COND argument format:
+ *
+ *	[31:12] Reserved (0)
+ *	[11:8] Host Voltage Supply Flags
+ *	[7:0] Check Pattern (0xAA)
+ */
+
+/*
   MMC status in R1
   Type
   	e : error bit
#1 lore raw
Quoted 8 lines · Philip Langdale

In the process of developing this patch, we realised that the R6 response definition was incorrect and that it should have been identical to R1 (and so should R7). Correcting this mistake revealed problems in a couple of host controller drivers that relied on the response definitions to be unique. As we need a story for R7, I've also fixed up the R6 handling in the affected drivers but I have no idea if it will work in practice as I lack the hardware.

Do this fix in a separate patch. And cc the relevant maintainers.

drivers/mmc/imxmmc.c line 365

Quoted 13 lines
drivers/mmc/imxmmc.c renamed from usr/src/linux/drivers/mmc/imxmmc.c +0 -3
@@ -351,9 +360,6 @@
 	case MMC_RSP_R3: /* short */
 		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3;
 		break;
-	case MMC_RSP_R6: /* short CRC */
-		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R6;
-		break;
 	default:
 		break;
 	}

I think this chunk suffices. Until proven otherwise, regard R6 and R7 as information the hw does not need to know about. Same thing in tifm_sd.

Rgds

Signature · 5 lines
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org