Hi all,
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.
Addendum: Similar spec changes were introduced by the MMC Association in version 4.2
to switch to block addressing. We do not have access to the 4.2 spec or even a change
summary as we did for 3 -> 4. My guess as to what's changed looks something like the
following:
i) A new field is added to the ext_csd to store the capacity that cannot be described
using existing csd fields.
ii) A new field is added to the ext_csd to indicate the need for block addressing.
iii) A new write-only field is added to the ext_csd which has to be toggled to tell the
card that the host supports block-addressing. But perhaps compatibility requires that
a new command be used like for SDHC...
If someone has access to the spec and can shed some light on the subject in a legal way,
please let us know. Of course, I don't think there are any MMC HC cards on the market
yet, but one was announced last month.
--phil
Signed-off-by: Philipl Langdale <[email protected]>
---
drivers/mmc/mmc.c | 40 +++++++++++++++++++++++++++++++++++-----
drivers/mmc/mmc_block.c | 4 +++-
include/linux/mmc/card.h | 3 +++
include/linux/mmc/mmc.h | 1 +
include/linux/mmc/protocol.h | 13 ++++++++++++-
5 files changed, 54 insertions(+), 7 deletions(-)
diff -urN linux-2.6.19/drivers/mmc/mmc_block.c linux-2.6.19-sdhc/drivers/mmc/mmc_block.c
--- linux-2.6.19/drivers/mmc/mmc_block.c 2006-12-21 20:29:49.000000000 -0800
+++ linux-2.6.19-sdhc/drivers/mmc/mmc_block.c 2006-12-22 08:42:56.000000000 -0800
@@ -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);
diff -urN linux-2.6.19/drivers/mmc/mmc.c linux-2.6.19-sdhc/drivers/mmc/mmc.c
--- linux-2.6.19/drivers/mmc/mmc.c 2006-12-21 20:29:49.000000000 -0800
+++ linux-2.6.19-sdhc/drivers/mmc/mmc.c 2006-12-22 09:57:41.000000000 -0800
@@ -289,7 +289,10 @@
else
limit_us = 100000;
- if (timeout_us > limit_us) {
+ /*
+ * SDHC cards always use these fixed values.
+ */
+ if (timeout_us > limit_us || mmc_card_blockaddr(card)) {
data->timeout_ns = limit_us * 1000;
data->timeout_clks = 0;
}
@@ -588,12 +591,15 @@
if (mmc_card_sd(card)) {
csd_struct = UNSTUFF_BITS(resp, 126, 2);
- if (csd_struct != 0) {
+ if (csd_struct != 0 && csd_struct != 1) {
printk("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd_struct);
mmc_card_set_bad(card);
return;
}
+ if (csd_struct == 1) {
+ mmc_card_set_blockaddr(card);
+ }
m = UNSTUFF_BITS(resp, 115, 4);
e = UNSTUFF_BITS(resp, 112, 3);
@@ -605,9 +611,14 @@
csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
- e = UNSTUFF_BITS(resp, 47, 3);
- m = UNSTUFF_BITS(resp, 62, 12);
- csd->capacity = (1 + m) << (e + 2);
+ if (csd_struct == 0) {
+ e = UNSTUFF_BITS(resp, 47, 3);
+ m = UNSTUFF_BITS(resp, 62, 12);
+ csd->capacity = (1 + m) << (e + 2);
+ } else if (csd_struct == 1) {
+ m = UNSTUFF_BITS(resp, 48, 22);
+ csd->capacity = (1 + m) << 10;
+ }
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
@@ -824,6 +835,25 @@
{
struct mmc_command cmd;
int i, err = 0;
+ static const u8 test_pattern = 0xAA;
+
+ /*
+ * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
+ * before SD_APP_OP_COND. This command will harmlessly fail for
+ * SD 1.0 and MMC cards (fortunately including MMC 4 cards).
+ */
+ cmd.opcode = SD_SEND_IF_COND;
+ cmd.arg = ((host->ocr_avail & 0xFF8000) != 0) << 8 | test_pattern;
+ cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err == MMC_ERR_NONE) {
+ if ((cmd.resp[0] & 0xFF) != test_pattern) {
+ return MMC_ERR_FAILED;
+ } else if (ocr != 0) {
+ ocr |= 1 << 30;
+ }
+ }
cmd.opcode = SD_APP_OP_COND;
cmd.arg = ocr;
diff -urN linux-2.6.19/include/linux/mmc/card.h linux-2.6.19-sdhc/include/linux/mmc/card.h
--- linux-2.6.19/include/linux/mmc/card.h 2006-12-21 20:29:49.000000000 -0800
+++ linux-2.6.19-sdhc/include/linux/mmc/card.h 2006-12-22 08:42:56.000000000 -0800
@@ -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)
diff -urN linux-2.6.19/include/linux/mmc/mmc.h linux-2.6.19-sdhc/include/linux/mmc/mmc.h
--- linux-2.6.19/include/linux/mmc/mmc.h 2006-11-29 13:57:37.000000000 -0800
+++ linux-2.6.19-sdhc/include/linux/mmc/mmc.h 2006-12-22 08:42:56.000000000 -0800
@@ -43,6 +43,7 @@
#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_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC)
#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
diff -urN linux-2.6.19/include/linux/mmc/protocol.h linux-2.6.19-sdhc/include/linux/mmc/protocol.h
--- linux-2.6.19/include/linux/mmc/protocol.h 2006-12-21 20:29:49.000000000 -0800
+++ linux-2.6.19-sdhc/include/linux/mmc/protocol.h 2006-12-22 08:42:56.000000000 -0800
@@ -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
Philip Langdale wrote:
> Hi all,
>
> 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:
So I sent that with the wrong From: address (damn you thunderbird!). Please
reply to my personal address (this one) and not my work one. :-)
--phil
Philip Langdale wrote:
> Hi all,
>
>
>
*snip*
> Signed-off-by: Philipl Langdale <[email protected]>
>
When you have a commit message larger than the patch, you know there is
something wrong. ;)
Please skip the part about MMC at least.
> @@ -588,12 +591,15 @@
>
> if (mmc_card_sd(card)) {
> csd_struct = UNSTUFF_BITS(resp, 126, 2);
> - if (csd_struct != 0) {
> + if (csd_struct != 0 && csd_struct != 1) {
> printk("%s: unrecognised CSD structure version %d\n",
> mmc_hostname(card->host), csd_struct);
> mmc_card_set_bad(card);
> return;
> }
> + if (csd_struct == 1) {
> + mmc_card_set_blockaddr(card);
> + }
>
> m = UNSTUFF_BITS(resp, 115, 4);
> e = UNSTUFF_BITS(resp, 112, 3);
>
Actually, the way the spec describes version 2.0 of the CSD leaves my
stomach a bit upset. I think we will find cards that put bogus values in
the fields, expecting most hosts to use the well defined values. So I'd
prefer a switch statement here, where csd_struct == 1 results in hard
coded values for many fields.
> @@ -824,6 +835,25 @@
> {
> struct mmc_command cmd;
> int i, err = 0;
> + static const u8 test_pattern = 0xAA;
> +
> + /*
> + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
> + * before SD_APP_OP_COND. This command will harmlessly fail for
> + * SD 1.0 and MMC cards (fortunately including MMC 4 cards).
> + */
> + cmd.opcode = SD_SEND_IF_COND;
> + cmd.arg = ((host->ocr_avail & 0xFF8000) != 0) << 8 | test_pattern;
> + cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
> +
> + err = mmc_wait_for_cmd(host, &cmd, 0);
> + if (err == MMC_ERR_NONE) {
> + if ((cmd.resp[0] & 0xFF) != test_pattern) {
> + return MMC_ERR_FAILED;
> + } else if (ocr != 0) {
> + ocr |= 1 << 30;
> + }
> + }
>
> cmd.opcode = SD_APP_OP_COND;
> cmd.arg = ocr;
>
Put this in mmc_setup() with the rest of the initialisation. Hiding in
here just confuses things.
Also, please add a comment about why you manipulate the ocr.
> @@ -43,6 +43,7 @@
> #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_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC)
>
> #define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
>
>
Wrong. R7 is defined as MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE.
(So is R6 btw, wonder why that is...)
Rgds
--
-- Pierre Ossman
Linux kernel, MMC maintainer http://www.kernel.org
PulseAudio, core developer http://pulseaudio.org
rdesktop, core developer http://www.rdesktop.org
I will post the updated diff as a separate follow up.
Pierre Ossman wrote:
>
> When you have a commit message larger than the patch, you know there is
> something wrong. ;)
>
> Please skip the part about MMC at least.
Heh. I forget that you don't want to manually alter the email. Will do.
>
> Actually, the way the spec describes version 2.0 of the CSD leaves my
> stomach a bit upset. I think we will find cards that put bogus values in
> the fields, expecting most hosts to use the well defined values. So I'd
> prefer a switch statement here, where csd_struct == 1 results in hard
> coded values for many fields.
I'm less pessimistic than you about this, but I have no problems with
hard-coding.
>> + cmd.opcode = SD_SEND_IF_COND;
>>
>
> Put this in mmc_setup() with the rest of the initialisation. Hiding in
> here just confuses things.
Done. I thought we needed it before both SD_APP_SEND_OP_COND calls but
it's only needed before the second one so I've moved it inline into
mmc_setup.
> Also, please add a comment about why you manipulate the ocr.
Done.
>
> Wrong. R7 is defined as MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE.
>
> (So is R6 btw, wonder why that is...)
Ah - I knew they were both the same so I made R7 match R6. :-)
I've added OPCODE to both.
Thanks,
--phil
Hi!
> > Hi all,
> >
> > 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:
>
> So I sent that with the wrong From: address (damn you thunderbird!). Please
> reply to my personal address (this one) and not my work one. :-)
Would you describe what SDHC is? I know SD flash cards, and IIRC SDIO
cards exist, with functionality such as bluetooth...? But SDHC?
--
Thanks for all the (sleeping) penguins.
Pavel Machek wrote:
> Would you describe what SDHC is? I know SD flash cards, and IIRC SDIO
> cards exist, with functionality such as bluetooth...? But SDHC?
>
>
SDHC is short for "Secure Digital High Capacity". It's simply SD flash
cards than conform to a new version of the protocol, a version where
addressing is done on a sector (512 byte) basis instead of bytes.
Rgds
--
-- Pierre Ossman
Linux kernel, MMC maintainer http://www.kernel.org
PulseAudio, core developer http://pulseaudio.org
rdesktop, core developer http://www.rdesktop.org
Philip Langdale wrote:
> Heh. I forget that you don't want to manually alter the email. Will do.
>
>
I'm rather reluctant to change anything once someone has put a
signed-off-by line to it. Different people have different limits as to
what they regard a trivial modification.
> Done. I thought we needed it before both SD_APP_SEND_OP_COND calls but
> it's only needed before the second one so I've moved it inline into
> mmc_setup.
>
>
The spec says we need it at both (even though it might not be so in
practice). We should follow the spec first and foremost.
Rgds
--
-- Pierre Ossman
Linux kernel, MMC maintainer http://www.kernel.org
PulseAudio, core developer http://pulseaudio.org
rdesktop, core developer http://www.rdesktop.org