X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=drivers%2Fmmc%2Fmmc.c;h=da528390acf87293292237abddf2bf5cf66b600c;hb=f24e09754bcb4d367664ea635c0c4ccde6ec4df5;hp=294961a102cac4ccd32a8d5bc3f78e5ec43dbcba;hpb=335eadf2ef6a1122a720aea98e758e5d431da87d;p=powerpc.git diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 294961a102..da528390ac 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 @@ -16,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -246,6 +250,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 +284,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 +317,63 @@ 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; + + /* + * 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; +} + /* * Ensure that no card is selected. */ @@ -413,8 +470,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 +550,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. @@ -527,6 +582,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. */ @@ -608,6 +689,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); @@ -626,6 +708,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); } @@ -738,10 +821,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; @@ -781,6 +874,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; @@ -833,24 +999,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); @@ -905,19 +1070,26 @@ 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); } /** * mmc_detect_change - process change of state on a MMC socket * @host: host which changed state. + * @delay: optional delay to wait before detection (jiffies) * * All we know is that card(s) have been inserted or removed * from the socket(s). We don't know which socket or cards. */ -void mmc_detect_change(struct mmc_host *host) +void mmc_detect_change(struct mmc_host *host, unsigned long delay) { - schedule_work(&host->detect); + if (delay) + schedule_delayed_work(&host->detect, delay); + else + schedule_work(&host->detect); } EXPORT_SYMBOL(mmc_detect_change); @@ -1021,7 +1193,7 @@ int mmc_add_host(struct mmc_host *host) ret = mmc_add_host_sysfs(host); if (ret == 0) { mmc_power_off(host); - mmc_detect_change(host); + mmc_detect_change(host, 0); } return ret; @@ -1091,7 +1263,7 @@ EXPORT_SYMBOL(mmc_suspend_host); */ int mmc_resume_host(struct mmc_host *host) { - mmc_detect_change(host); + mmc_rescan(host); return 0; }