diff options
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 29 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 2 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 289 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 318 |
5 files changed, 442 insertions, 213 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig new file mode 100644 index 00000000000..94222b9a15e --- /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 75333a2461d..72c7cf4a9f9 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 fad8edc3809..177264d090a 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 47449e87013..619581e4916 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -229,55 +229,13 @@ out: } /* - * 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); - - 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. + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. */ -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 = 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)); + 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); + + 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); +} - err = mmc_register_card(card); +/* + * 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 ae54e8eb7fe..6597e778f70 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -263,55 +263,13 @@ out: } /* - * Host is being removed. Free up the current card. - */ -static void mmc_sd_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_sd_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_sd_ops = { - .remove = mmc_sd_remove, - .detect = mmc_sd_detect, -}; - -/* - * Starting point for SD card init. + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. */ -int mmc_attach_sd(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; @@ -321,34 +279,6 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) BUG_ON(!host); BUG_ON(!host->claimed); - mmc_attach_bus(host, &mmc_sd_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; - } - - if (ocr & MMC_VDD_165_195) { - printk(KERN_WARNING "%s: SD card claims to support the " - "incompletely defined 'low voltage range'. This " - "will be ignored.\n", mmc_hostname(host)); - ocr &= ~MMC_VDD_165_195; - } - - host->ocr = mmc_select_voltage(host, ocr); - - /* - * Can we support the voltage(s) of the card(s)? - */ - 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 @@ -363,11 +293,13 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) * of the ocr to indicate that we can handle * block-addressed SDHC cards. */ - err = mmc_send_if_cond(host, host->ocr); + err = mmc_send_if_cond(host, ocr); if (err == MMC_ERR_NONE) - ocr = host->ocr | (1 << 30); + ocr |= 1 << 30; - mmc_send_app_op_cond(host, ocr, NULL); + err = mmc_send_app_op_cond(host, ocr, NULL); + if (err != MMC_ERR_NONE) + goto err; /* * Fetch CID from card. @@ -376,15 +308,22 @@ int mmc_attach_sd(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_SD; - 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_SD; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + } /* * Set card RCA. @@ -395,35 +334,42 @@ int mmc_attach_sd(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); + } /* - * Fetch SCR from card. + * Select card, as all following commands rely on that. */ 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; + 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); + mmc_decode_scr(card); - /* - * Fetch switch information from card. - */ - err = mmc_read_switch(card); - if (err != MMC_ERR_NONE) - goto free_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) @@ -458,11 +404,164 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) mmc_set_bus_width(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_sd_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_sd_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_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, +}; + +/* + * Starting point for SD card init. + */ +int mmc_attach_sd(struct mmc_host *host, u32 ocr) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_sd_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; + } + + if (ocr & MMC_VDD_165_195) { + printk(KERN_WARNING "%s: SD card claims to support the " + "incompletely defined 'low voltage range'. This " + "will be ignored.\n", mmc_hostname(host)); + ocr &= ~MMC_VDD_165_195; + } + + host->ocr = mmc_select_voltage(host, ocr); + + /* + * Can we support the voltage(s) of the card(s)? + */ + 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; @@ -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); |