<a href="http://www.archivesat.com/Linux_kernel_discussion/thread2328642.htm" target="_blank">mmc: Add support for SDHC cards</a>
2006-12-22 18:24:00
Hi all,<br /><br />Thanks to the generous donation of an SDHC card by John Gilmore, and the<br />surprisingly enlightened decision by the SD Card Association to publish<br />useful specs, I've been able to bash out support for SDHC.<br />The changes are not too profound:<br /><br /><strong><font color="#0000ff">i)</font></strong> Add a card flag indicating the card uses block level addressing and check<br />it in the block driver. As we never took advantage of byte-level addressing,<br />this simply involves skipping the block -> byte translation when sending commands.<br /><br /><strong><font color="#0000ff">ii)</font></strong> The layout of the CSD is changed - a set of fields are discarded to make space<br />for a larger C_SIZE. We did not reference any of the discarded fields except those<br />related to the C_SIZE.<br /><br /><strong><font color="#0000ff">iii)</font></strong> Read and write timeouts are fixed values and not calculated from CSD values.<br /><br /><strong><font color="#0000ff">iv)</font></strong> Before invoking SEND_APP_OP_COND, we must invoke the new SEND_IF_COND to inform<br />the card we support SDHC.
I've done some basic read and write tests and everything seems to work fine but one<br />should obviously use caution in case it eats your data.<br /><br />Addendum: Similar spec changes were introduced by the MMC Association in version 4.2<br />to switch to block addressing. We do not have access to the 4.2 spec or even a change<br />summary as we did for 3 -> 4. My guess as to what's changed looks something like the<br />following:<br /><br />i) A new field is added to the ext_csd to store the capacity that cannot be described<br />using existing csd fields.<br /><br />ii) A new field is added to the ext_csd to indicate the need for block addressing.<br /><br />iii) A new write-only field is added to the ext_csd which has to be toggled to tell the<br />card that the host supports block-addressing. But perhaps compatibility requires that<br />a new command be used like for SDHC...<br /><br />If someone has access to the spec and can shed some light on the subject in a legal way,<br />please let us know. Of course, I don't think there are any MMC HC cards on the market<br />yet, but one was announced last month.<br /><br />--phil<br /><br />Signed-off-by: Philipl Langdale <philipl<img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" />overt.org><br />---<br /><br /> drivers/mmc/mmc.c | 40<br />+++++++++++++++++++++++++++++++++++-----<br /> drivers/mmc/mmc_block.c | 4 +++-<br /> include/linux/mmc/card.h | 3 +++<br /> include/linux/mmc/mmc.h | 1 +<br /> include/linux/mmc/protocol.h | 13 ++++++++++++-<br /> 5 files changed, 54 insertions(+), 7 deletions(-)<br /><br />diff -urN linux-2.6.19/drivers/mmc/mmc_block.c<br />linux-2.6.19-sdhc/drivers/mmc/mmc_block.c<br />--- linux-2.6.19/drivers/mmc/mmc_block.c 2006-12-21<br />20:29:49.000000000 -0800<br />+++ linux-2.6.19-sdhc/drivers/mmc/mmc_block.c 2006-12-22<br />08:42:56.000000000 -0800<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -237,7<br />+237,9 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> brq.mrq.cmd = &brq.cmd;<br /> brq.mrq.data = &brq.data;<br /><br />- brq.cmd.arg = req->sector << 9;<br />+ brq.cmd.arg = req->sector;<br />+ if (!mmc_card_blockaddr(card))<br />+ brq.cmd.arg <<= 9;<br /> brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;<br /> brq.data.blksz = 1 << md->block_bits;<br /> brq.data.blocks = req->nr_sectors >><br />(md->block_bits - 9);<br />diff -urN linux-2.6.19/drivers/mmc/mmc.c<br />linux-2.6.19-sdhc/drivers/mmc/mmc.c<br />--- linux-2.6.19/drivers/mmc/mmc.c 2006-12-21<br />20:29:49.000000000 -0800<br />+++ linux-2.6.19-sdhc/drivers/mmc/mmc.c 2006-12-22<br />09:57:41.000000000 -0800<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -289,7<br />+289,10 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> else<br /> limit_us = 100000;<br /><br />- if (timeout_us > limit_us) {<br />+ /*<br />+ * SDHC cards always use these fixed values.<br />+ */<br />+ if (timeout_us > limit_us || mmc_card_blockaddr(card))<br />{<br /> data->timeout_ns = limit_us * 1000;<br /> data->timeout_clks = 0;<br /> }<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -588,12<br />+591,15 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /><br /> if (mmc_card_sd(card)) {<br /> csd_struct = UNSTUFF_BITS(resp, 126, 2);<br />- if (csd_struct != 0) {<br />+ if (csd_struct != 0 && csd_struct != 1) {<br /> printk("%s: unrecognised CSD structure version<br />%dn",<br /> mmc_hostname(card->host), csd_struct);<br /> mmc_card_set_bad(card);<br /> return;<br /> }<br />+ if (csd_struct == 1) {<br />+ mmc_card_set_blockaddr(card);<br />+ }<br /><br /> m = UNSTUFF_BITS(resp, 115, 4);<br /> e = UNSTUFF_BITS(resp, 112, 3);<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -605,9<br />+611,14 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> csd->max_dtr = tran_exp[e] * tran_mant[m];<br /> csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);<br /><br />- e = UNSTUFF_BITS(resp, 47, 3);<br />- m = UNSTUFF_BITS(resp, 62, 12);<br />- csd->capacity = (1 + m) << (e + 2);<br />+ if (csd_struct == 0) {<br />+ e = UNSTUFF_BITS(resp, 47, 3);<br />+ m = UNSTUFF_BITS(resp, 62, 12);<br />+ csd->capacity = (1 + m) << (e + 2);<br />+ } else if (csd_struct == 1) {<br />+ m = UNSTUFF_BITS(resp, 48, 22);<br />+ csd->capacity = (1 + m) << 10;<br />+ }<br /><br /> csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);<br /> csd->read_partial = UNSTUFF_BITS(resp, 79, 1);<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -824,6<br />+835,25 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> {<br /> struct mmc_command cmd;<br /> int i, err = 0;<br />+ static const u8 test_pattern = 0xAA;<br />+<br />+ /*<br />+ * To support SD 2.0 cards, we must always invoke<br />SD_SEND_IF_COND<br />+ * before SD_APP_OP_COND. This command will harmlessly<br />fail for<br />+ * SD 1.0 and MMC cards (fortunately including MMC 4<br />cards).<br />+ */<br />+ cmd.opcode = SD_SEND_IF_COND;<br />+ cmd.arg = ((host->ocr_avail & 0xFF8000) != 0)<br /><< 8 | test_pattern;<br />+ cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;<br />+<br />+ err = mmc_wait_for_cmd(host, &cmd, 0);<br />+ if (err == MMC_ERR_NONE) {<br />+ if ((cmd.resp[0] & 0xFF) != test_pattern) {<br />+ return MMC_ERR_FAILED;<br />+ } else if (ocr != 0) {<br />+ ocr |= 1 << 30;<br />+ }<br />+ }<br /><br /> cmd.opcode = SD_APP_OP_COND;<br /> cmd.arg = ocr;<br />diff -urN linux-2.6.19/include/linux/mmc/card.h<br />linux-2.6.19-sdhc/include/linux/mmc/card.h<br />--- linux-2.6.19/include/linux/mmc/card.h 2006-12-21<br />20:29:49.000000000 -0800<br />+++ linux-2.6.19-sdhc/include/linux/mmc/card.h 2006-12-22<br />08:42:56.000000000 -0800<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -71,6<br />+71,7 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> #define MMC_STATE_SDCARD (1<<3) /* is an SD card */<br /> #define MMC_STATE_READONLY (1<<4) /* card is<br />read-only */<br /> #define MMC_STATE_HIGHSPEED (1<<5) /* card is in<br />high speed mode */<br />+#define MMC_STATE_BLOCKADDR (1<<6) /* card uses<br />block-addressing */<br /> u32 raw_cid[4]; /* raw card CID */<br /> u32 raw_csd[4]; /* raw card CSD */<br /> u32 raw_scr[2]; /* raw card SCR */<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -87,6<br />+88,7 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> #define mmc_card_sd(c) ((c)->state &<br />MMC_STATE_SDCARD)<br /> #define mmc_card_readonly(c) ((c)->state &<br />MMC_STATE_READONLY)<br /> #define mmc_card_highspeed(c) ((c)->state &<br />MMC_STATE_HIGHSPEED)<br />+#define mmc_card_blockaddr(c) ((c)->state &<br />MMC_STATE_BLOCKADDR)<br /><br /> #define mmc_card_set_present(c) ((c)->state |=<br />MMC_STATE_PRESENT)<br /> #define mmc_card_set_dead(c) ((c)->state |=<br />MMC_STATE_DEAD)<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -94,6<br />+96,7 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> #define mmc_card_set_sd(c) ((c)->state |=<br />MMC_STATE_SDCARD)<br /> #define mmc_card_set_readonly(c) ((c)->state |=<br />MMC_STATE_READONLY)<br /> #define mmc_card_set_highspeed(c) ((c)->state |=<br />MMC_STATE_HIGHSPEED)<br />+#define mmc_card_set_blockaddr(c) ((c)->state |=<br />MMC_STATE_BLOCKADDR)<br /><br /> #define mmc_card_name(c) ((c)->cid.prod_name)<br /> #define mmc_card_id(c) ((c)->dev.bus_id)<br />diff -urN linux-2.6.19/include/linux/mmc/mmc.h<br />linux-2.6.19-sdhc/include/linux/mmc/mmc.h<br />--- linux-2.6.19/include/linux/mmc/mmc.h 2006-11-29<br />13:57:37.000000000 -0800<br />+++ linux-2.6.19-sdhc/include/linux/mmc/mmc.h 2006-12-22<br />08:42:56.000000000 -0800<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -43,6<br />+43,7 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> #define<br />MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)<br /> #define MMC_RSP_R3 (MMC_RSP_PRESENT)<br /> #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC)<br />+#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC)<br /><br /> #define mmc_resp_type(cmd) ((cmd)->flags &<br />(MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RS<br />P_OPCODE))<br /><br />diff -urN linux-2.6.19/include/linux/mmc/protocol.h<br />linux-2.6.19-sdhc/include/linux/mmc/protocol.h<br />--- linux-2.6.19/include/linux/mmc/protocol.h 2006-12-21<br />20:29:49.000000000 -0800<br />+++<br />linux-2.6.19-sdhc/include/linux/mmc/protocol.h 2006-12-22<br />08:42:56.000000000 -0800<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -79,9<br />+79,12 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> #define MMC_GEN_CMD 56 /* adtc [0] RD/WR <br /> R1 */<br /><br /> /* SD commands type argument <br />response */<br />- /* class 8 */<br />+ /* class 0 */<br /> /* This is basically the same command as for MMC with some<br />quirks. */<br /> #define SD_SEND_RELATIVE_ADDR 3 /* bcr <br /> R6 */<br />+#define SD_SEND_IF_COND 8 /* bcr [11:0] See<br />below R7 */<br />+<br />+ /* class 10 */<br /> #define SD_SWITCH 6 /* adtc [31:0] See<br />below R1 */<br /><br /> /* Application commands */<br /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /> -115,6<br />+118,14 <img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" /><br /> */<br /><br /> /*<br />+ * SD_SEND_IF_COND argument format:<br />+ *<br />+ * [31:12] Reserved (0)<br />+ * [11:8] Host Voltage Supply Flags<br />+ * [7:0] Check Pattern (0xAA)<br />+ */<br />+<br />+/*<br /> MMC status in R1<br /> Type<br /> e : error bit<br />-<br />To unsubscribe from this list: send the line<br />"unsubscribe linux-kernel" in<br />the body of a message to majordomo<img border="0" align="middle" src="http://www.archivesat.com/img/at.gif" />vger.kernel.org<br />More majordomo info at <a href="http://vger.kernel.org/majordomo-info.html">http://vge<br />r.kernel.org/majordomo-info.html</a><br />Please read the FAQ at <a href="http://www.tux.org/lkml/">http://www.tux.org/lkml/</a>
转载请注明:在路上 » mmc: Add support for SDHC cards