From 335eadf2ef6a1122a720aea98e758e5d431da87d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Sep 2005 15:18:50 -0700 Subject: [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 Cc: Russell King Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/mmc.c | 326 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 253 insertions(+), 73 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0a8165974ba7..294961a102ca 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -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); +/** + * 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 @@ -322,48 +394,70 @@ static void mmc_decode_cid(struct mmc_card *card) memset(&card->cid, 0, sizeof(struct mmc_cid)); - /* - * The selection of the format here is guesswork based upon - * information people have sent to date. - */ - switch (card->csd.mmca_vsn) { - case 0: /* MMC v1.? */ - case 1: /* MMC v1.4 */ - card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); - 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.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); - card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); - card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); - card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); - card->cid.serial = UNSTUFF_BITS(resp, 16, 24); - card->cid.month = UNSTUFF_BITS(resp, 12, 4); - card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; - break; - - case 2: /* MMC v2.x ? */ - case 3: /* MMC v3.x ? */ - 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.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); - card->cid.serial = UNSTUFF_BITS(resp, 16, 32); - card->cid.month = UNSTUFF_BITS(resp, 12, 4); - card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; - break; - - default: - printk("%s: card has unknown MMCA version %d\n", - mmc_hostname(card->host), card->csd.mmca_vsn); - mmc_card_set_bad(card); - break; + if (mmc_card_sd(card)) { + /* + * SD doesn't currently have a version field so we will + * 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) { + case 0: /* MMC v1.0 - v1.2 */ + case 1: /* MMC v1.4 */ + card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); + 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.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); + card->cid.serial = UNSTUFF_BITS(resp, 16, 24); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + 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.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.serial = UNSTUFF_BITS(resp, 16, 32); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + default: + printk("%s: card has unknown MMCA version %d\n", + mmc_hostname(card->host), card->csd.mmca_vsn); + mmc_card_set_bad(card); + break; + } } } @@ -376,34 +470,61 @@ static void mmc_decode_csd(struct mmc_card *card) unsigned int e, m, csd_struct; u32 *resp = card->raw_csd; - /* - * We only understand CSD structure v1.1 and v2. - * v2 has extra information in bits 15, 11 and 10. - */ - csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 1 && csd_struct != 2) { - printk("%s: unrecognised CSD structure version %d\n", - mmc_hostname(card->host), csd_struct); - mmc_card_set_bad(card); - return; + 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 v1.2. + * v1.2 has extra information in bits 15, 11 and 10. + */ + csd_struct = UNSTUFF_BITS(resp, 126, 2); + if (csd_struct != 1 && csd_struct != 2) { + printk("%s: unrecognised CSD structure version %d\n", + mmc_hostname(card->host), csd_struct); + mmc_card_set_bad(card); + return; + } - csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); - 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; + csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); + 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); + 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); + 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); + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + } } /* @@ -536,6 +657,34 @@ static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) 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 * times out, it is not an error; there are no further cards @@ -579,13 +728,28 @@ static void mmc_discover_cards(struct mmc_host *host) card->state &= ~MMC_STATE_DEAD; - cmd.opcode = MMC_SET_RELATIVE_ADDR; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1; + if (host->mode == MMC_MODE_SD) { + mmc_card_set_sd(card); - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(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.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(card); + } } } @@ -669,12 +833,25 @@ static void mmc_setup(struct mmc_host *host) int err; u32 ocr; + host->mode = MMC_MODE_MMC; + mmc_power_up(host); mmc_idle_cards(host); 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) - return; + { + err = mmc_send_app_op_cond(host, 0, &ocr); + if (err != MMC_ERR_NONE) + return; + + host->mode = MMC_MODE_SD; + } host->ocr = mmc_select_voltage(host, ocr); @@ -714,7 +891,10 @@ static void mmc_setup(struct mmc_host *host) * all get the idea that they should be ready for CMD2. * (My SanDisk card seems to need this.) */ - mmc_send_op_cond(host, host->ocr, NULL); + 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_discover_cards(host); -- cgit v1.2.2 From a00fc09029f02ca833cf90e5d5625f08c4ac4f51 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Sep 2005 15:18:52 -0700 Subject: [PATCH] sd: read-only switch Support for the read-only switch on SD cards which must be enforced by the host. Signed-off-by: Pierre Ossman Cc: Russell King Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/mmc.c | 39 +++++++++++++++++++++++---------------- drivers/mmc/mmc_block.c | 9 +++++++-- 2 files changed, 30 insertions(+), 18 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 294961a102ca..725c6ad3eb64 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -413,8 +413,7 @@ static void mmc_decode_cid(struct mmc_card *card) card->cid.month = UNSTUFF_BITS(resp, 8, 4); card->cid.year += 2000; /* SD cards year offset */ - } - else { + } else { /* * The selection of the format here is based upon published * specs from sandisk and from what people have reported. @@ -494,8 +493,7 @@ static void mmc_decode_csd(struct mmc_card *card) csd->capacity = (1 + m) << (e + 2); csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); - } - else { + } else { /* * We only understand CSD structure v1.1 and v1.2. * v1.2 has extra information in bits 15, 11 and 10. @@ -738,10 +736,20 @@ static void mmc_discover_cards(struct mmc_host *host) err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) mmc_card_set_dead(card); - else + else { card->rca = cmd.resp[0] >> 16; - } - else { + + if (!host->ops->get_ro) { + printk(KERN_WARNING "%s: host does not " + "support reading read-only " + "switch. assuming write-enable.\n", + mmc_hostname(host)); + } else { + if (host->ops->get_ro(host)) + mmc_card_set_readonly(card); + } + } + } else { cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1; @@ -833,24 +841,23 @@ static void mmc_setup(struct mmc_host *host) int err; u32 ocr; - host->mode = MMC_MODE_MMC; + host->mode = MMC_MODE_SD; mmc_power_up(host); mmc_idle_cards(host); - err = mmc_send_op_cond(host, 0, &ocr); + err = mmc_send_app_op_cond(host, 0, &ocr); /* - * If we fail to detect any cards then try - * searching for SD cards. + * If we fail to detect any SD cards then try + * searching for MMC cards. */ - if (err != MMC_ERR_NONE) - { - err = mmc_send_app_op_cond(host, 0, &ocr); + if (err != MMC_ERR_NONE) { + host->mode = MMC_MODE_MMC; + + err = mmc_send_op_cond(host, 0, &ocr); if (err != MMC_ERR_NONE) return; - - host->mode = MMC_MODE_SD; } host->ocr = mmc_select_voltage(host, ocr); diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index d4eee99c2bf6..fa83f15fdf16 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -95,6 +95,10 @@ static int mmc_blk_open(struct inode *inode, struct file *filp) if (md->usage == 2) check_disk_change(inode->i_bdev); ret = 0; + + if ((filp->f_mode & FMODE_WRITE) && + mmc_card_readonly(md->queue.card)) + ret = -EROFS; } return ret; @@ -403,9 +407,10 @@ static int mmc_blk_probe(struct mmc_card *card) if (err) goto out; - printk(KERN_INFO "%s: %s %s %dKiB\n", + printk(KERN_INFO "%s: %s %s %dKiB %s\n", md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), - (card->csd.capacity << card->csd.read_blkbits) / 1024); + (card->csd.capacity << card->csd.read_blkbits) / 1024, + mmc_card_readonly(card)?"(ro)":""); mmc_set_drvdata(card, md); add_disk(md->disk); -- cgit v1.2.2 From b57c43ad81602589afca3948a5a7121e40026e17 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Sep 2005 15:18:53 -0700 Subject: [PATCH] sd: SCR register Read the SD specific SCR register from the card. Signed-off-by: Pierre Ossman Cc: Russell King Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/mmc.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 725c6ad3eb64..21d4fb3314f8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -246,6 +248,8 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca, EXPORT_SYMBOL(mmc_wait_for_app_cmd); +static int mmc_select_card(struct mmc_host *host, struct mmc_card *card); + /** * __mmc_claim_host - exclusively claim a host * @host: mmc host to claim @@ -278,16 +282,10 @@ int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card) spin_unlock_irqrestore(&host->lock, flags); remove_wait_queue(&host->wq, &wait); - if (card != (void *)-1 && host->card_selected != card) { - struct mmc_command cmd; - - host->card_selected = card; - - cmd.opcode = MMC_SELECT_CARD; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (card != (void *)-1) { + err = mmc_select_card(host, card); + if (err != MMC_ERR_NONE) + return err; } return err; @@ -317,6 +315,29 @@ void mmc_release_host(struct mmc_host *host) EXPORT_SYMBOL(mmc_release_host); +static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) +{ + int err; + struct mmc_command cmd; + + BUG_ON(host->card_busy == NULL); + + if (host->card_selected == card) + return MMC_ERR_NONE; + + host->card_selected = card; + + cmd.opcode = MMC_SELECT_CARD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + return MMC_ERR_NONE; +} + /* * Ensure that no card is selected. */ @@ -525,6 +546,32 @@ static void mmc_decode_csd(struct mmc_card *card) } } +/* + * Given a 64-bit response, decode to our card SCR structure. + */ +static void mmc_decode_scr(struct mmc_card *card) +{ + struct sd_scr *scr = &card->scr; + unsigned int scr_struct; + u32 resp[4]; + + BUG_ON(!mmc_card_sd(card)); + + resp[3] = card->raw_scr[1]; + resp[2] = card->raw_scr[0]; + + scr_struct = UNSTUFF_BITS(resp, 60, 4); + if (scr_struct != 0) { + printk("%s: unrecognised SCR structure version %d\n", + mmc_hostname(card->host), scr_struct); + mmc_card_set_bad(card); + return; + } + + scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); + scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); +} + /* * Locate a MMC card on this MMC host given a raw CID. */ @@ -789,6 +836,79 @@ static void mmc_read_csds(struct mmc_host *host) } } +static void mmc_read_scrs(struct mmc_host *host) +{ + int err; + struct mmc_card *card; + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + + struct scatterlist sg; + + list_for_each_entry(card, &host->cards, node) { + if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) + continue; + if (!mmc_card_sd(card)) + continue; + + err = mmc_select_card(host, card); + if (err != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_APP_CMD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) { + mmc_card_set_dead(card); + continue; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1; + + memset(&data, 0, sizeof(struct mmc_data)); + + data.timeout_ns = card->csd.tacc_ns * 10; + data.timeout_clks = card->csd.tacc_clks * 10; + data.blksz_bits = 3; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, (u8*)card->raw_scr, 8); + + err = mmc_wait_for_req(host, &mrq); + if (err != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + card->raw_scr[0] = ntohl(card->raw_scr[0]); + card->raw_scr[1] = ntohl(card->raw_scr[1]); + + mmc_decode_scr(card); + } + + mmc_deselect_cards(host); +} + static unsigned int mmc_calculate_clock(struct mmc_host *host) { struct mmc_card *card; @@ -912,6 +1032,9 @@ static void mmc_setup(struct mmc_host *host) host->ops->set_ios(host, &host->ios); mmc_read_csds(host); + + if (host->mode == MMC_MODE_SD) + mmc_read_scrs(host); } -- cgit v1.2.2 From a9c4342beb4cd28b3a05c3401195e2536c37c150 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Sep 2005 15:18:54 -0700 Subject: [PATCH] sd: SCR in sysfs Export the SCR register through sysfs. Signed-off-by: Pierre Ossman Cc: Russell King Cc: David Brownell Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/mmc_sysfs.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index ad8949810fc5..9e23ad3ae27b 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -34,6 +34,7 @@ MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], card->raw_cid[2], card->raw_cid[3]); MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], card->raw_csd[2], card->raw_csd[3]); +MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); @@ -47,6 +48,7 @@ MMC_ATTR(serial, "0x%08x\n", card->cid.serial); static struct device_attribute mmc_dev_attrs[] = { MMC_ATTR_RO(cid), MMC_ATTR_RO(csd), + MMC_ATTR_RO(scr), MMC_ATTR_RO(date), MMC_ATTR_RO(fwrev), MMC_ATTR_RO(hwrev), -- cgit v1.2.2 From f218278a456b3c272b480443c89004c3d2a49f18 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Sep 2005 15:18:55 -0700 Subject: [PATCH] sd: SD 4-bit bus Infrastructure for 4-bit bus transfers with SD cards. Signed-off-by: Pierre Ossman Cc: Russell King Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/mmc.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 21d4fb3314f8..6414f071a2a4 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -335,6 +335,40 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) if (err != MMC_ERR_NONE) return err; + /* + * Default bus width is 1 bit. + */ + host->ios.bus_width = MMC_BUS_WIDTH_1; + + /* + * We can only change the bus width of the selected + * card so therefore we have to put the handling + * here. + */ + if (host->caps & MMC_CAP_4_BIT_DATA) { + /* + * The card is in 1 bit mode by default so + * we only need to change if it supports the + * wider version. + */ + if (mmc_card_sd(card) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + struct mmc_command cmd; + cmd.opcode = SD_APP_SET_BUS_WIDTH; + cmd.arg = SD_BUS_WIDTH_4; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_app_cmd(host, card->rca, &cmd, + CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + host->ios.bus_width = MMC_BUS_WIDTH_4; + } + } + + host->ops->set_ios(host, &host->ios); + return MMC_ERR_NONE; } @@ -653,6 +687,7 @@ static void mmc_power_up(struct mmc_host *host) host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_UP; + host->ios.bus_width = MMC_BUS_WIDTH_1; host->ops->set_ios(host, &host->ios); mmc_delay(1); @@ -671,6 +706,7 @@ static void mmc_power_off(struct mmc_host *host) host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_OFF; + host->ios.bus_width = MMC_BUS_WIDTH_1; host->ops->set_ios(host, &host->ios); } -- cgit v1.2.2 From 5b4fd9aef778e223968dfab1b90f905b3f2bd23d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Sep 2005 15:18:56 -0700 Subject: [PATCH] sd: SD copyright notice Credit where credit is due. Signed-off-by: Pierre Ossman Cc: Russell King Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/mmc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 6414f071a2a4..0a117c61cd18 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2,6 +2,8 @@ * linux/drivers/mmc/mmc.c * * Copyright (C) 2003-2004 Russell King, All Rights Reserved. + * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. + * SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as -- cgit v1.2.2 From e619524fe5f5b0c13db34ed0f6320d2dcccf6e8d Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 6 Sep 2005 15:18:56 -0700 Subject: [PATCH] Add write protection switch handling to the PXA MMC driver Add a write protection switch handling code to the PXA MMC driver so that platform specific code can provide it if available. Signed-off-by: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/pxamci.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index b78beb1b0159..e99a53b09e32 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -362,6 +362,16 @@ static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq) pxamci_start_cmd(host, mrq->cmd, cmdat); } +static int pxamci_get_ro(struct mmc_host *mmc) +{ + struct pxamci_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->get_ro) + return host->pdata->get_ro(mmc->dev); + /* Host doesn't support read only detection so assume writeable */ + return 0; +} + static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct pxamci_host *host = mmc_priv(mmc); @@ -401,6 +411,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static struct mmc_host_ops pxamci_ops = { .request = pxamci_request, + .get_ro = pxamci_get_ro, .set_ios = pxamci_set_ios, }; -- cgit v1.2.2 From 65ae2118e84616680dce37b951ffc366dcce7cf0 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Sep 2005 15:18:57 -0700 Subject: [PATCH] mmc: wbsd Secure Digital support Add support for Secure Digital specific features in the wbsd driver. Adds support for read-only switch and wide bus transfers. Signed-off-by: Pierre Ossman Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/wbsd.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++------ drivers/mmc/wbsd.h | 3 +++ 2 files changed, 57 insertions(+), 6 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 08ae22aed9e8..dec01d38c782 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -720,11 +720,28 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) * calculate CRC. * * Space for CRC must be included in the size. + * Two bytes are needed for each data line. */ - blksize = (1 << data->blksz_bits) + 2; + if (host->bus_width == MMC_BUS_WIDTH_1) + { + blksize = (1 << data->blksz_bits) + 2; + + wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); + wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); + } + else if (host->bus_width == MMC_BUS_WIDTH_4) + { + blksize = (1 << data->blksz_bits) + 2 * 4; - wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); - wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); + wbsd_write_index(host, WBSD_IDX_PBSMSB, ((blksize >> 4) & 0xF0) + | WBSD_DATA_WIDTH); + wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); + } + else + { + data->error = MMC_ERR_INVALID; + return; + } /* * Clear the FIFO. This is needed even for DMA @@ -960,9 +977,9 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) struct wbsd_host* host = mmc_priv(mmc); u8 clk, setup, pwr; - DBGF("clock %uHz busmode %u powermode %u cs %u Vdd %u\n", - ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, - ios->vdd); + DBGF("clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n", + ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, + ios->vdd, ios->bus_width); spin_lock_bh(&host->lock); @@ -1010,6 +1027,7 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) setup = wbsd_read_index(host, WBSD_IDX_SETUP); if (ios->chip_select == MMC_CS_HIGH) { + BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1); setup |= WBSD_DAT3_H; host->flags |= WBSD_FIGNORE_DETECT; } @@ -1025,12 +1043,41 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) } wbsd_write_index(host, WBSD_IDX_SETUP, setup); + /* + * Store bus width for later. Will be used when + * setting up the data transfer. + */ + host->bus_width = ios->bus_width; + spin_unlock_bh(&host->lock); } +static int wbsd_get_ro(struct mmc_host* mmc) +{ + struct wbsd_host* host = mmc_priv(mmc); + u8 csr; + + spin_lock_bh(&host->lock); + + csr = inb(host->base + WBSD_CSR); + csr |= WBSD_MSLED; + outb(csr, host->base + WBSD_CSR); + + mdelay(1); + + csr = inb(host->base + WBSD_CSR); + csr &= ~WBSD_MSLED; + outb(csr, host->base + WBSD_CSR); + + spin_unlock_bh(&host->lock); + + return csr & WBSD_WRPT; +} + static struct mmc_host_ops wbsd_ops = { .request = wbsd_request, .set_ios = wbsd_set_ios, + .get_ro = wbsd_get_ro, }; /*****************************************************************************\ @@ -1355,6 +1402,7 @@ static int __devinit wbsd_alloc_mmc(struct device* dev) mmc->f_min = 375000; mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA; spin_lock_init(&host->lock); diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h index 8af43549f5d5..9005b5241b3c 100644 --- a/drivers/mmc/wbsd.h +++ b/drivers/mmc/wbsd.h @@ -106,6 +106,8 @@ #define WBSD_CLK_16M 0x02 #define WBSD_CLK_24M 0x03 +#define WBSD_DATA_WIDTH 0x01 + #define WBSD_DAT3_H 0x08 #define WBSD_FIFO_RESET 0x04 #define WBSD_SOFT_RESET 0x02 @@ -164,6 +166,7 @@ struct wbsd_host int firsterr; /* See fifo functions */ u8 clk; /* Current clock speed */ + unsigned char bus_width; /* Current bus width */ int config; /* Config port */ u8 unlock_code; /* Code to unlock config */ -- cgit v1.2.2 From 4bc20a8d497e137c0a9ee9a3bacce3dd4a2b6993 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 6 Sep 2005 15:18:58 -0700 Subject: [PATCH] mmc: conditional scr sysfs entry Only show the scr file in sysfs for SD cards. Previously this was present for all cards but had a contents of 0 for MMC cards. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/mmc_sysfs.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index 9e23ad3ae27b..3f4a66ca9555 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -48,7 +48,6 @@ MMC_ATTR(serial, "0x%08x\n", card->cid.serial); static struct device_attribute mmc_dev_attrs[] = { MMC_ATTR_RO(cid), MMC_ATTR_RO(csd), - MMC_ATTR_RO(scr), MMC_ATTR_RO(date), MMC_ATTR_RO(fwrev), MMC_ATTR_RO(hwrev), @@ -59,6 +58,8 @@ static struct device_attribute mmc_dev_attrs[] = { __ATTR_NULL }; +static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr); + static void mmc_release_card(struct device *dev) { @@ -209,10 +210,20 @@ void mmc_init_card(struct mmc_card *card, struct mmc_host *host) */ int mmc_register_card(struct mmc_card *card) { + int ret; + snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), "%s:%04x", mmc_hostname(card->host), card->rca); - return device_add(&card->dev); + ret = device_add(&card->dev); + if (ret == 0) { + if (mmc_card_sd(card)) { + ret = device_create_file(&card->dev, &mmc_dev_attr_scr); + if (ret) + device_del(&card->dev); + } + } + return ret; } /* @@ -221,8 +232,12 @@ int mmc_register_card(struct mmc_card *card) */ void mmc_remove_card(struct mmc_card *card) { - if (mmc_card_present(card)) + if (mmc_card_present(card)) { + if (mmc_card_sd(card)) + device_remove_file(&card->dev, &mmc_dev_attr_scr); + device_del(&card->dev); + } put_device(&card->dev); } -- cgit v1.2.2