[PATCH] sd: initialize SD cards
Support for the Secure Digital protocol in the MMC layer. A summary of the legal issues surrounding SD cards, as understood by yours truly: Members of the Secure Digital Association, hereafter SDA, are required to sign a NDA[1] before given access to any specifications. It has been speculated that including an SD implementation would forbid these members to redistribute Linux. This is the basic problem with SD support so it is unclear if it even is a problem since it has no effect on those of us that aren't members. The SDA doesn't seem to enforce these rules though since the patches included here are based on documentation made public by some of the members. The most complete specs[2] are actually released by Sandisk, one of the founding companies of the SDA. Because of this the NDA is considered a non-issue by most involved in the discussions concerning these patches. It might be that the SDA is only interested in protecting the so called "secure" bits of SD, which so far hasn't been found in any public spec. (The card is split into two sections, one "normal" and one "secure" which has an access scheme similar to TPM:s). (As a side note, Microsoft is working to make things easier for us since they want to be able to include the source code for a SD driver in one of their development kits. HP is making sure that the new NDA will allow a Linux implementation. So far only the SDIO specs have been opened up[3]. More will hopefully follow.) [1] http://www.sdcard.org/membership/images/ippolicy.pdf [2] http://www.sandisk.com/pdf/oem/ProdManualSDCardv1.9.pdf [3] http://www.sdcard.org/sdio/Simplified%20SDIO%20Card%20Specification.pdf This patch contains the central parts of the SD support. If no MMC cards are found on a bus then the MMC layer proceeds looking for SD cards. Helper functions are extended to handle the special needs of SD cards. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> Cc: Russell King <rmk@arm.linux.org.uk> Cc: David Brownell <david-b@pacbell.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
328b922786
commit
335eadf2ef
|
@ -172,7 +172,79 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_wait_for_cmd);
|
EXPORT_SYMBOL(mmc_wait_for_cmd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mmc_wait_for_app_cmd - start an application command and wait for
|
||||||
|
completion
|
||||||
|
* @host: MMC host to start command
|
||||||
|
* @rca: RCA to send MMC_APP_CMD to
|
||||||
|
* @cmd: MMC command to start
|
||||||
|
* @retries: maximum number of retries
|
||||||
|
*
|
||||||
|
* Sends a MMC_APP_CMD, checks the card response, sends the command
|
||||||
|
* in the parameter and waits for it to complete. Return any error
|
||||||
|
* that occurred while the command was executing. Do not attempt to
|
||||||
|
* parse the response.
|
||||||
|
*/
|
||||||
|
int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca,
|
||||||
|
struct mmc_command *cmd, int retries)
|
||||||
|
{
|
||||||
|
struct mmc_request mrq;
|
||||||
|
struct mmc_command appcmd;
|
||||||
|
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
BUG_ON(host->card_busy == NULL);
|
||||||
|
BUG_ON(retries < 0);
|
||||||
|
|
||||||
|
err = MMC_ERR_INVALID;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have to resend MMC_APP_CMD for each attempt so
|
||||||
|
* we cannot use the retries field in mmc_command.
|
||||||
|
*/
|
||||||
|
for (i = 0;i <= retries;i++) {
|
||||||
|
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||||
|
|
||||||
|
appcmd.opcode = MMC_APP_CMD;
|
||||||
|
appcmd.arg = rca << 16;
|
||||||
|
appcmd.flags = MMC_RSP_R1;
|
||||||
|
appcmd.retries = 0;
|
||||||
|
memset(appcmd.resp, 0, sizeof(appcmd.resp));
|
||||||
|
appcmd.data = NULL;
|
||||||
|
|
||||||
|
mrq.cmd = &appcmd;
|
||||||
|
appcmd.data = NULL;
|
||||||
|
|
||||||
|
mmc_wait_for_req(host, &mrq);
|
||||||
|
|
||||||
|
if (appcmd.error) {
|
||||||
|
err = appcmd.error;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that card supported application commands */
|
||||||
|
if (!(appcmd.resp[0] & R1_APP_CMD))
|
||||||
|
return MMC_ERR_FAILED;
|
||||||
|
|
||||||
|
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||||
|
|
||||||
|
memset(cmd->resp, 0, sizeof(cmd->resp));
|
||||||
|
cmd->retries = 0;
|
||||||
|
|
||||||
|
mrq.cmd = cmd;
|
||||||
|
cmd->data = NULL;
|
||||||
|
|
||||||
|
mmc_wait_for_req(host, &mrq);
|
||||||
|
|
||||||
|
err = cmd->error;
|
||||||
|
if (cmd->error == MMC_ERR_NONE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(mmc_wait_for_app_cmd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __mmc_claim_host - exclusively claim a host
|
* __mmc_claim_host - exclusively claim a host
|
||||||
|
@ -322,12 +394,33 @@ static void mmc_decode_cid(struct mmc_card *card)
|
||||||
|
|
||||||
memset(&card->cid, 0, sizeof(struct mmc_cid));
|
memset(&card->cid, 0, sizeof(struct mmc_cid));
|
||||||
|
|
||||||
|
if (mmc_card_sd(card)) {
|
||||||
/*
|
/*
|
||||||
* The selection of the format here is guesswork based upon
|
* SD doesn't currently have a version field so we will
|
||||||
* information people have sent to date.
|
* have to assume we can parse this.
|
||||||
|
*/
|
||||||
|
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
|
||||||
|
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
|
||||||
|
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
|
||||||
|
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
|
||||||
|
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
|
||||||
|
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
|
||||||
|
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
|
||||||
|
card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4);
|
||||||
|
card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4);
|
||||||
|
card->cid.serial = UNSTUFF_BITS(resp, 24, 32);
|
||||||
|
card->cid.year = UNSTUFF_BITS(resp, 12, 8);
|
||||||
|
card->cid.month = UNSTUFF_BITS(resp, 8, 4);
|
||||||
|
|
||||||
|
card->cid.year += 2000; /* SD cards year offset */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
* The selection of the format here is based upon published
|
||||||
|
* specs from sandisk and from what people have reported.
|
||||||
*/
|
*/
|
||||||
switch (card->csd.mmca_vsn) {
|
switch (card->csd.mmca_vsn) {
|
||||||
case 0: /* MMC v1.? */
|
case 0: /* MMC v1.0 - v1.2 */
|
||||||
case 1: /* MMC v1.4 */
|
case 1: /* MMC v1.4 */
|
||||||
card->cid.manfid = UNSTUFF_BITS(resp, 104, 24);
|
card->cid.manfid = UNSTUFF_BITS(resp, 104, 24);
|
||||||
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
|
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
|
||||||
|
@ -344,8 +437,8 @@ static void mmc_decode_cid(struct mmc_card *card)
|
||||||
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
|
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: /* MMC v2.x ? */
|
case 2: /* MMC v2.0 - v2.2 */
|
||||||
case 3: /* MMC v3.x ? */
|
case 3: /* MMC v3.1 - v3.3 */
|
||||||
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
|
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
|
||||||
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
|
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
|
||||||
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
|
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
|
||||||
|
@ -366,6 +459,7 @@ static void mmc_decode_cid(struct mmc_card *card)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a 128-bit response, decode to our card CSD structure.
|
* Given a 128-bit response, decode to our card CSD structure.
|
||||||
|
@ -376,9 +470,35 @@ static void mmc_decode_csd(struct mmc_card *card)
|
||||||
unsigned int e, m, csd_struct;
|
unsigned int e, m, csd_struct;
|
||||||
u32 *resp = card->raw_csd;
|
u32 *resp = card->raw_csd;
|
||||||
|
|
||||||
|
if (mmc_card_sd(card)) {
|
||||||
|
csd_struct = UNSTUFF_BITS(resp, 126, 2);
|
||||||
|
if (csd_struct != 0) {
|
||||||
|
printk("%s: unrecognised CSD structure version %d\n",
|
||||||
|
mmc_hostname(card->host), csd_struct);
|
||||||
|
mmc_card_set_bad(card);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = UNSTUFF_BITS(resp, 115, 4);
|
||||||
|
e = UNSTUFF_BITS(resp, 112, 3);
|
||||||
|
csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
|
||||||
|
csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100;
|
||||||
|
|
||||||
|
m = UNSTUFF_BITS(resp, 99, 4);
|
||||||
|
e = UNSTUFF_BITS(resp, 96, 3);
|
||||||
|
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);
|
||||||
|
|
||||||
|
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
/*
|
/*
|
||||||
* We only understand CSD structure v1.1 and v2.
|
* We only understand CSD structure v1.1 and v1.2.
|
||||||
* v2 has extra information in bits 15, 11 and 10.
|
* v1.2 has extra information in bits 15, 11 and 10.
|
||||||
*/
|
*/
|
||||||
csd_struct = UNSTUFF_BITS(resp, 126, 2);
|
csd_struct = UNSTUFF_BITS(resp, 126, 2);
|
||||||
if (csd_struct != 1 && csd_struct != 2) {
|
if (csd_struct != 1 && csd_struct != 2) {
|
||||||
|
@ -405,6 +525,7 @@ static void mmc_decode_csd(struct mmc_card *card)
|
||||||
|
|
||||||
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
|
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Locate a MMC card on this MMC host given a raw CID.
|
* Locate a MMC card on this MMC host given a raw CID.
|
||||||
|
@ -536,6 +657,34 @@ static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||||
|
{
|
||||||
|
struct mmc_command cmd;
|
||||||
|
int i, err = 0;
|
||||||
|
|
||||||
|
cmd.opcode = SD_APP_OP_COND;
|
||||||
|
cmd.arg = ocr;
|
||||||
|
cmd.flags = MMC_RSP_R3;
|
||||||
|
|
||||||
|
for (i = 100; i; i--) {
|
||||||
|
err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES);
|
||||||
|
if (err != MMC_ERR_NONE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
err = MMC_ERR_TIMEOUT;
|
||||||
|
|
||||||
|
mmc_delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rocr)
|
||||||
|
*rocr = cmd.resp[0];
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Discover cards by requesting their CID. If this command
|
* Discover cards by requesting their CID. If this command
|
||||||
* times out, it is not an error; there are no further cards
|
* times out, it is not an error; there are no further cards
|
||||||
|
@ -579,6 +728,20 @@ static void mmc_discover_cards(struct mmc_host *host)
|
||||||
|
|
||||||
card->state &= ~MMC_STATE_DEAD;
|
card->state &= ~MMC_STATE_DEAD;
|
||||||
|
|
||||||
|
if (host->mode == MMC_MODE_SD) {
|
||||||
|
mmc_card_set_sd(card);
|
||||||
|
|
||||||
|
cmd.opcode = SD_SEND_RELATIVE_ADDR;
|
||||||
|
cmd.arg = 0;
|
||||||
|
cmd.flags = MMC_RSP_R1;
|
||||||
|
|
||||||
|
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
|
||||||
|
if (err != MMC_ERR_NONE)
|
||||||
|
mmc_card_set_dead(card);
|
||||||
|
else
|
||||||
|
card->rca = cmd.resp[0] >> 16;
|
||||||
|
}
|
||||||
|
else {
|
||||||
cmd.opcode = MMC_SET_RELATIVE_ADDR;
|
cmd.opcode = MMC_SET_RELATIVE_ADDR;
|
||||||
cmd.arg = card->rca << 16;
|
cmd.arg = card->rca << 16;
|
||||||
cmd.flags = MMC_RSP_R1;
|
cmd.flags = MMC_RSP_R1;
|
||||||
|
@ -588,6 +751,7 @@ static void mmc_discover_cards(struct mmc_host *host)
|
||||||
mmc_card_set_dead(card);
|
mmc_card_set_dead(card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mmc_read_csds(struct mmc_host *host)
|
static void mmc_read_csds(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
|
@ -669,13 +833,26 @@ static void mmc_setup(struct mmc_host *host)
|
||||||
int err;
|
int err;
|
||||||
u32 ocr;
|
u32 ocr;
|
||||||
|
|
||||||
|
host->mode = MMC_MODE_MMC;
|
||||||
|
|
||||||
mmc_power_up(host);
|
mmc_power_up(host);
|
||||||
mmc_idle_cards(host);
|
mmc_idle_cards(host);
|
||||||
|
|
||||||
err = mmc_send_op_cond(host, 0, &ocr);
|
err = mmc_send_op_cond(host, 0, &ocr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we fail to detect any cards then try
|
||||||
|
* searching for SD cards.
|
||||||
|
*/
|
||||||
|
if (err != MMC_ERR_NONE)
|
||||||
|
{
|
||||||
|
err = mmc_send_app_op_cond(host, 0, &ocr);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err != MMC_ERR_NONE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
host->mode = MMC_MODE_SD;
|
||||||
|
}
|
||||||
|
|
||||||
host->ocr = mmc_select_voltage(host, ocr);
|
host->ocr = mmc_select_voltage(host, ocr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -714,6 +891,9 @@ static void mmc_setup(struct mmc_host *host)
|
||||||
* all get the idea that they should be ready for CMD2.
|
* all get the idea that they should be ready for CMD2.
|
||||||
* (My SanDisk card seems to need this.)
|
* (My SanDisk card seems to need this.)
|
||||||
*/
|
*/
|
||||||
|
if (host->mode == MMC_MODE_SD)
|
||||||
|
mmc_send_app_op_cond(host, host->ocr, NULL);
|
||||||
|
else
|
||||||
mmc_send_op_cond(host, host->ocr, NULL);
|
mmc_send_op_cond(host, host->ocr, NULL);
|
||||||
|
|
||||||
mmc_discover_cards(host);
|
mmc_discover_cards(host);
|
||||||
|
|
|
@ -47,6 +47,7 @@ struct mmc_card {
|
||||||
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
|
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
|
||||||
#define MMC_STATE_DEAD (1<<1) /* device no longer in stack */
|
#define MMC_STATE_DEAD (1<<1) /* device no longer in stack */
|
||||||
#define MMC_STATE_BAD (1<<2) /* unrecognised device */
|
#define MMC_STATE_BAD (1<<2) /* unrecognised device */
|
||||||
|
#define MMC_STATE_SDCARD (1<<3) /* is an SD card */
|
||||||
u32 raw_cid[4]; /* raw card CID */
|
u32 raw_cid[4]; /* raw card CID */
|
||||||
u32 raw_csd[4]; /* raw card CSD */
|
u32 raw_csd[4]; /* raw card CSD */
|
||||||
struct mmc_cid cid; /* card identification */
|
struct mmc_cid cid; /* card identification */
|
||||||
|
@ -56,10 +57,12 @@ struct mmc_card {
|
||||||
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
|
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
|
||||||
#define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD)
|
#define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD)
|
||||||
#define mmc_card_bad(c) ((c)->state & MMC_STATE_BAD)
|
#define mmc_card_bad(c) ((c)->state & MMC_STATE_BAD)
|
||||||
|
#define mmc_card_sd(c) ((c)->state & MMC_STATE_SDCARD)
|
||||||
|
|
||||||
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
|
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
|
||||||
#define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD)
|
#define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD)
|
||||||
#define mmc_card_set_bad(c) ((c)->state |= MMC_STATE_BAD)
|
#define mmc_card_set_bad(c) ((c)->state |= MMC_STATE_BAD)
|
||||||
|
#define mmc_card_set_sd(c) ((c)->state |= MMC_STATE_SDCARD)
|
||||||
|
|
||||||
#define mmc_card_name(c) ((c)->cid.prod_name)
|
#define mmc_card_name(c) ((c)->cid.prod_name)
|
||||||
#define mmc_card_id(c) ((c)->dev.bus_id)
|
#define mmc_card_id(c) ((c)->dev.bus_id)
|
||||||
|
|
|
@ -87,6 +87,10 @@ struct mmc_host {
|
||||||
struct mmc_ios ios; /* current io bus settings */
|
struct mmc_ios ios; /* current io bus settings */
|
||||||
u32 ocr; /* the current OCR setting */
|
u32 ocr; /* the current OCR setting */
|
||||||
|
|
||||||
|
unsigned int mode; /* current card mode of host */
|
||||||
|
#define MMC_MODE_MMC 0
|
||||||
|
#define MMC_MODE_SD 1
|
||||||
|
|
||||||
struct list_head cards; /* devices attached to this host */
|
struct list_head cards; /* devices attached to this host */
|
||||||
|
|
||||||
wait_queue_head_t wq;
|
wait_queue_head_t wq;
|
||||||
|
|
|
@ -88,6 +88,8 @@ struct mmc_card;
|
||||||
|
|
||||||
extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
|
extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
|
||||||
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
|
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
|
||||||
|
extern int mmc_wait_for_app_cmd(struct mmc_host *, unsigned int,
|
||||||
|
struct mmc_command *, int);
|
||||||
|
|
||||||
extern int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card);
|
extern int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue