diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c7d64c0187fb..6c97491543db 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -19,6 +19,8 @@ config MMC_DEBUG This is an option for use by developers; most people should say N here. This enables MMC core and driver debugging. +source "drivers/mmc/core/Kconfig" + source "drivers/mmc/card/Kconfig" source "drivers/mmc/host/Kconfig" diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig new file mode 100644 index 000000000000..94222b9a15ea --- /dev/null +++ b/drivers/mmc/core/Kconfig @@ -0,0 +1,17 @@ +# +# MMC core configuration +# + +config MMC_UNSAFE_RESUME + bool "Allow unsafe resume (DANGEROUS)" + depends on MMC != n + help + If you say Y here, the MMC layer will assume that all cards + stayed in their respective slots during the suspend. The + normal behaviour is to remove them at suspend and + redetecting them at resume. Breaking this assumption will + in most cases result in data corruption. + + This option is usually just for embedded systems which use + a MMC/SD card for rootfs. Most people should say N here. + diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 75333a2461df..72c7cf4a9f9d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -677,14 +677,19 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { - if (host->bus_ops->remove) - host->bus_ops->remove(host); - mmc_detach_bus(host); + if (host->bus_ops->suspend) + host->bus_ops->suspend(host); + if (!host->bus_ops->resume) { + if (host->bus_ops->remove) + host->bus_ops->remove(host); + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } } mmc_bus_put(host); - BUG_ON(host->card); - mmc_power_off(host); return 0; @@ -698,7 +703,19 @@ EXPORT_SYMBOL(mmc_suspend_host); */ int mmc_resume_host(struct mmc_host *host) { - mmc_rescan(&host->detect.work); + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + mmc_power_up(host); + BUG_ON(!host->bus_ops->resume); + host->bus_ops->resume(host); + } + mmc_bus_put(host); + + /* + * We add a slight delay here so that resume can progress + * in parallel. + */ + mmc_detect_change(host, 1); return 0; } diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index fad8edc38099..177264d090ac 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -18,6 +18,8 @@ struct mmc_bus_ops { void (*remove)(struct mmc_host *); void (*detect)(struct mmc_host *); + void (*suspend)(struct mmc_host *); + void (*resume)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 47449e870131..619581e49163 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -229,55 +229,13 @@ static int mmc_read_ext_csd(struct mmc_card *card) } /* - * Host is being removed. Free up the current card. + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. */ -static void mmc_remove(struct mmc_host *host) -{ - BUG_ON(!host); - BUG_ON(!host->card); - - mmc_remove_card(host->card); - host->card = NULL; -} - -/* - * Card detection callback from host. - */ -static void mmc_detect(struct mmc_host *host) -{ - int err; - - BUG_ON(!host); - BUG_ON(!host->card); - - mmc_claim_host(host); - - /* - * Just check if our card has been removed. - */ - err = mmc_send_status(host->card, NULL); - - mmc_release_host(host); - - if (err != MMC_ERR_NONE) { - mmc_remove_card(host->card); - host->card = NULL; - - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); - } -} - -static const struct mmc_bus_ops mmc_ops = { - .remove = mmc_remove, - .detect = mmc_detect, -}; - -/* - * Starting point for MMC card init. - */ -int mmc_attach_mmc(struct mmc_host *host, u32 ocr) +static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + struct mmc_card *oldcard) { struct mmc_card *card; int err; @@ -287,27 +245,6 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) BUG_ON(!host); BUG_ON(!host->claimed); - mmc_attach_bus(host, &mmc_ops); - - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - printk(KERN_WARNING "%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } - - host->ocr = mmc_select_voltage(host, ocr); - - /* - * Can we support the voltage of the card? - */ - if (!host->ocr) - goto err; - /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle @@ -317,7 +254,9 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_go_idle(host); /* The extra bit indicates that we support high capacity */ - mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); + err = mmc_send_op_cond(host, ocr | (1 << 30), NULL); + if (err != MMC_ERR_NONE) + goto err; /* * Fetch CID from card. @@ -326,16 +265,23 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) if (err != MMC_ERR_NONE) goto err; - /* - * Allocate card structure. - */ - card = mmc_alloc_card(host); - if (IS_ERR(card)) - goto err; + if (oldcard) { + if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) + goto err; - card->type = MMC_TYPE_MMC; - card->rca = 1; - memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + card = oldcard; + } else { + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host); + if (IS_ERR(card)) + goto err; + + card->type = MMC_TYPE_MMC; + card->rca = 1; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + } /* * Set card RCA. @@ -346,15 +292,17 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); - /* - * Fetch CSD from card. - */ - err = mmc_send_csd(card, card->raw_csd); - if (err != MMC_ERR_NONE) - goto free_card; + if (!oldcard) { + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); + if (err != MMC_ERR_NONE) + goto free_card; - mmc_decode_csd(card); - mmc_decode_cid(card); + mmc_decode_csd(card); + mmc_decode_cid(card); + } /* * Select card, as all following commands rely on that. @@ -363,12 +311,14 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) if (err != MMC_ERR_NONE) goto free_card; - /* - * Fetch and process extened CSD. - */ - err = mmc_read_ext_csd(card); - if (err != MMC_ERR_NONE) - goto free_card; + if (!oldcard) { + /* + * Fetch and process extened CSD. + */ + err = mmc_read_ext_csd(card); + if (err != MMC_ERR_NONE) + goto free_card; + } /* * Activate high speed (if supported) @@ -412,11 +362,157 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); } - host->card = card; + if (!oldcard) + host->card = card; + + return MMC_ERR_NONE; + +free_card: + if (!oldcard) + mmc_remove_card(card); +err: + + return MMC_ERR_FAILED; +} + +/* + * Host is being removed. Free up the current card. + */ +static void mmc_remove(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_remove_card(host->card); + host->card = NULL; +} + +/* + * Card detection callback from host. + */ +static void mmc_detect(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + /* + * Just check if our card has been removed. + */ + err = mmc_send_status(host->card, NULL); mmc_release_host(host); - err = mmc_register_card(card); + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } +} + +#ifdef CONFIG_MMC_UNSAFE_RESUME + +/* + * Suspend callback from host. + */ +static void mmc_suspend(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); +} + +/* + * Resume callback from host. + * + * This function tries to determine if the same card is still present + * and, if so, restore all state to it. + */ +static void mmc_resume(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + err = mmc_sd_init_card(host, host->ocr, host->card); + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_detach_bus(host); + } + + mmc_release_host(host); +} + +#else + +#define mmc_suspend NULL +#define mmc_resume NULL + +#endif + +static const struct mmc_bus_ops mmc_ops = { + .remove = mmc_remove, + .detect = mmc_detect, + .suspend = mmc_suspend, + .resume = mmc_resume, +}; + +/* + * Starting point for MMC card init. + */ +int mmc_attach_mmc(struct mmc_host *host, u32 ocr) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_ops); + + /* + * Sanity check the voltages that the card claims to + * support. + */ + if (ocr & 0x7F) { + printk(KERN_WARNING "%s: card claims to support voltages " + "below the defined range. These will be ignored.\n", + mmc_hostname(host)); + ocr &= ~0x7F; + } + + host->ocr = mmc_select_voltage(host, ocr); + + /* + * Can we support the voltage of the card? + */ + if (!host->ocr) + goto err; + + /* + * Detect and init the card. + */ + err = mmc_sd_init_card(host, host->ocr, NULL); + if (err != MMC_ERR_NONE) + goto err; + + mmc_release_host(host); + + err = mmc_register_card(host->card); if (err) goto reclaim_host; @@ -424,8 +520,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) reclaim_host: mmc_claim_host(host); -free_card: - mmc_remove_card(card); + mmc_remove_card(host->card); host->card = NULL; err: mmc_detach_bus(host); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index ae54e8eb7fea..6597e778f70e 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -262,6 +262,161 @@ static int mmc_switch_hs(struct mmc_card *card) return err; } +/* + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. + */ +static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + struct mmc_card *oldcard) +{ + struct mmc_card *card; + int err; + u32 cid[4]; + unsigned int max_dtr; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + /* + * Since we're changing the OCR value, we seem to + * need to tell some cards to go back to the idle + * state. We wait 1ms to give cards time to + * respond. + */ + mmc_go_idle(host); + + /* + * If SD_SEND_IF_COND indicates an SD 2.0 + * compliant card and we should set bit 30 + * of the ocr to indicate that we can handle + * block-addressed SDHC cards. + */ + err = mmc_send_if_cond(host, ocr); + if (err == MMC_ERR_NONE) + ocr |= 1 << 30; + + err = mmc_send_app_op_cond(host, ocr, NULL); + if (err != MMC_ERR_NONE) + goto err; + + /* + * Fetch CID from card. + */ + err = mmc_all_send_cid(host, cid); + if (err != MMC_ERR_NONE) + goto err; + + if (oldcard) { + if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) + goto err; + + card = oldcard; + } else { + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host); + if (IS_ERR(card)) + goto err; + + card->type = MMC_TYPE_SD; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + } + + /* + * Set card RCA. + */ + err = mmc_send_relative_addr(host, &card->rca); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + + if (!oldcard) { + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_decode_csd(card); + mmc_decode_cid(card); + } + + /* + * Select card, as all following commands rely on that. + */ + err = mmc_select_card(card); + if (err != MMC_ERR_NONE) + goto free_card; + + if (!oldcard) { + /* + * Fetch SCR from card. + */ + err = mmc_app_send_scr(card, card->raw_scr); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_decode_scr(card); + + /* + * Fetch switch information from card. + */ + err = mmc_read_switch(card); + if (err != MMC_ERR_NONE) + goto free_card; + } + + /* + * Attempt to change to high-speed (if supported) + */ + err = mmc_switch_hs(card); + if (err != MMC_ERR_NONE) + goto free_card; + + /* + * Compute bus speed. + */ + max_dtr = (unsigned int)-1; + + if (mmc_card_highspeed(card)) { + if (max_dtr > card->sw_caps.hs_max_dtr) + max_dtr = card->sw_caps.hs_max_dtr; + } else if (max_dtr > card->csd.max_dtr) { + max_dtr = card->csd.max_dtr; + } + + mmc_set_clock(host, max_dtr); + + /* + * Switch to wider bus (if supported). + */ + if ((host->caps && MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); + } + + if (!oldcard) + host->card = card; + + return MMC_ERR_NONE; + +free_card: + if (!oldcard) + mmc_remove_card(card); +err: + + return MMC_ERR_FAILED; +} + /* * Host is being removed. Free up the current card. */ @@ -303,9 +458,60 @@ static void mmc_sd_detect(struct mmc_host *host) } } +#ifdef CONFIG_MMC_UNSAFE_RESUME + +/* + * Suspend callback from host. + */ +static void mmc_sd_suspend(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); +} + +/* + * Resume callback from host. + * + * This function tries to determine if the same card is still present + * and, if so, restore all state to it. + */ +static void mmc_sd_resume(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + err = mmc_sd_init_card(host, host->ocr, host->card); + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_detach_bus(host); + } + + mmc_release_host(host); +} + +#else + +#define mmc_sd_suspend NULL +#define mmc_sd_resume NULL + +#endif + static const struct mmc_bus_ops mmc_sd_ops = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, + .suspend = mmc_sd_suspend, + .resume = mmc_sd_resume, }; /* @@ -313,10 +519,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { */ int mmc_attach_sd(struct mmc_host *host, u32 ocr) { - struct mmc_card *card; int err; - u32 cid[4]; - unsigned int max_dtr; BUG_ON(!host); BUG_ON(!host->claimed); @@ -350,119 +553,15 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) goto err; /* - * Since we're changing the OCR value, we seem to - * need to tell some cards to go back to the idle - * state. We wait 1ms to give cards time to - * respond. + * Detect and init the card. */ - mmc_go_idle(host); - - /* - * If SD_SEND_IF_COND indicates an SD 2.0 - * compliant card and we should set bit 30 - * of the ocr to indicate that we can handle - * block-addressed SDHC cards. - */ - err = mmc_send_if_cond(host, host->ocr); - if (err == MMC_ERR_NONE) - ocr = host->ocr | (1 << 30); - - mmc_send_app_op_cond(host, ocr, NULL); - - /* - * Fetch CID from card. - */ - err = mmc_all_send_cid(host, cid); + err = mmc_sd_init_card(host, host->ocr, NULL); if (err != MMC_ERR_NONE) goto err; - /* - * Allocate card structure. - */ - card = mmc_alloc_card(host); - if (IS_ERR(card)) - goto err; - - card->type = MMC_TYPE_SD; - memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); - - /* - * Set card RCA. - */ - err = mmc_send_relative_addr(host, &card->rca); - if (err != MMC_ERR_NONE) - goto free_card; - - mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); - - /* - * Fetch CSD from card. - */ - err = mmc_send_csd(card, card->raw_csd); - if (err != MMC_ERR_NONE) - goto free_card; - - mmc_decode_csd(card); - mmc_decode_cid(card); - - /* - * Fetch SCR from card. - */ - err = mmc_select_card(card); - if (err != MMC_ERR_NONE) - goto free_card; - - err = mmc_app_send_scr(card, card->raw_scr); - if (err != MMC_ERR_NONE) - goto free_card; - - mmc_decode_scr(card); - - /* - * Fetch switch information from card. - */ - err = mmc_read_switch(card); - if (err != MMC_ERR_NONE) - goto free_card; - - /* - * Attempt to change to high-speed (if supported) - */ - err = mmc_switch_hs(card); - if (err != MMC_ERR_NONE) - goto free_card; - - /* - * Compute bus speed. - */ - max_dtr = (unsigned int)-1; - - if (mmc_card_highspeed(card)) { - if (max_dtr > card->sw_caps.hs_max_dtr) - max_dtr = card->sw_caps.hs_max_dtr; - } else if (max_dtr > card->csd.max_dtr) { - max_dtr = card->csd.max_dtr; - } - - mmc_set_clock(host, max_dtr); - - /* - * Switch to wider bus (if supported). - */ - if ((host->caps && MMC_CAP_4_BIT_DATA) && - (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { - err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); - if (err != MMC_ERR_NONE) - goto free_card; - - mmc_set_bus_width(host, MMC_BUS_WIDTH_4); - } - - host->card = card; - mmc_release_host(host); - err = mmc_register_card(card); + err = mmc_register_card(host->card); if (err) goto reclaim_host; @@ -470,8 +569,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) reclaim_host: mmc_claim_host(host); -free_card: - mmc_remove_card(card); + mmc_remove_card(host->card); host->card = NULL; err: mmc_detach_bus(host);