diff options
Diffstat (limited to 'drivers/mmc/core/mmc.c')
-rw-r--r-- | drivers/mmc/core/mmc.c | 103 |
1 files changed, 64 insertions, 39 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 1522fd2b517d..5be9b42d5057 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -33,6 +33,12 @@ #define MIN_CACHE_EN_TIMEOUT_MS 1600 #define CACHE_FLUSH_TIMEOUT_MS 30000 /* 30s */ +enum mmc_poweroff_type { + MMC_POWEROFF_SUSPEND, + MMC_POWEROFF_SHUTDOWN, + MMC_POWEROFF_UNBIND, +}; + static const unsigned int tran_exp[] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 @@ -453,7 +459,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) * There are two boot regions of equal size, defined in * multiples of 128K. */ - if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_boot_partition_access(card->host)) { + if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_host_can_access_boot(card->host)) { for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) { part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; mmc_part_add(card, part_size, @@ -572,7 +578,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) * RPMB regions are defined in multiples of 128K. */ card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT]; - if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) { + if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_can_cmd23(card->host)) { mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17, EXT_CSD_PART_CONFIG_ACC_RPMB, "rpmb", 0, false, @@ -674,7 +680,7 @@ static int mmc_read_ext_csd(struct mmc_card *card) u8 *ext_csd; int err; - if (!mmc_can_ext_csd(card)) + if (!mmc_card_can_ext_csd(card)) return 0; err = mmc_get_ext_csd(card, &ext_csd); @@ -953,7 +959,7 @@ static int mmc_select_powerclass(struct mmc_card *card) int err, ddr; /* Power class selection is supported for versions >= 4.0 */ - if (!mmc_can_ext_csd(card)) + if (!mmc_card_can_ext_csd(card)) return 0; bus_width = host->ios.bus_width; @@ -1016,7 +1022,7 @@ static int mmc_select_bus_width(struct mmc_card *card) unsigned idx, bus_width = 0; int err = 0; - if (!mmc_can_ext_csd(card) || + if (!mmc_card_can_ext_csd(card) || !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) return 0; @@ -1537,7 +1543,7 @@ static int mmc_select_timing(struct mmc_card *card) { int err = 0; - if (!mmc_can_ext_csd(card)) + if (!mmc_card_can_ext_csd(card)) goto bus_speed; if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) { @@ -1798,9 +1804,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* set erase_arg */ - if (mmc_can_discard(card)) + if (mmc_card_can_discard(card)) card->erase_arg = MMC_DISCARD_ARG; - else if (mmc_can_trim(card)) + else if (mmc_card_can_trim(card)) card->erase_arg = MMC_TRIM_ARG; else card->erase_arg = MMC_ERASE_ARG; @@ -1949,7 +1955,7 @@ err: return err; } -static int mmc_can_sleep(struct mmc_card *card) +static bool mmc_card_can_sleep(struct mmc_card *card) { return card->ext_csd.rev >= 3; } @@ -2007,13 +2013,26 @@ out_release: return err; } -static int mmc_can_poweroff_notify(const struct mmc_card *card) +static bool mmc_card_can_poweroff_notify(const struct mmc_card *card) { return card && mmc_card_mmc(card) && (card->ext_csd.power_off_notification == EXT_CSD_POWER_ON); } +static bool mmc_host_can_poweroff_notify(const struct mmc_host *host, + enum mmc_poweroff_type pm_type) +{ + if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) + return true; + + if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND && + pm_type == MMC_POWEROFF_SUSPEND) + return true; + + return pm_type == MMC_POWEROFF_SHUTDOWN; +} + static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) { unsigned int timeout = card->ext_csd.generic_cmd6_time; @@ -2037,15 +2056,6 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) } /* - * Host is being removed. Free up the current card. - */ -static void mmc_remove(struct mmc_host *host) -{ - mmc_remove_card(host->card); - host->card = NULL; -} - -/* * Card detection - card is alive. */ static int mmc_alive(struct mmc_host *host) @@ -2070,7 +2080,8 @@ static void mmc_detect(struct mmc_host *host) mmc_put_card(host->card, NULL); if (err) { - mmc_remove(host); + mmc_remove_card(host->card); + host->card = NULL; mmc_claim_host(host); mmc_detach_bus(host); @@ -2108,11 +2119,13 @@ static int _mmc_flush_cache(struct mmc_host *host) return err; } -static int _mmc_suspend(struct mmc_host *host, bool is_suspend) +static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type) { + unsigned int notify_type = EXT_CSD_POWER_OFF_SHORT; int err = 0; - unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT : - EXT_CSD_POWER_OFF_LONG; + + if (pm_type == MMC_POWEROFF_SHUTDOWN) + notify_type = EXT_CSD_POWER_OFF_LONG; mmc_claim_host(host); @@ -2123,11 +2136,10 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) if (err) goto out; - if (mmc_can_poweroff_notify(host->card) && - ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend || - (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND))) + if (mmc_card_can_poweroff_notify(host->card) && + mmc_host_can_poweroff_notify(host, pm_type)) err = mmc_poweroff_notify(host->card, notify_type); - else if (mmc_can_sleep(host->card)) + else if (mmc_card_can_sleep(host->card)) err = mmc_sleep(host); else if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); @@ -2142,13 +2154,27 @@ out: } /* + * Host is being removed. Free up the current card and do a graceful power-off. + */ +static void mmc_remove(struct mmc_host *host) +{ + get_device(&host->card->dev); + mmc_remove_card(host->card); + + _mmc_suspend(host, MMC_POWEROFF_UNBIND); + + put_device(&host->card->dev); + host->card = NULL; +} + +/* * Suspend callback */ static int mmc_suspend(struct mmc_host *host) { int err; - err = _mmc_suspend(host, true); + err = _mmc_suspend(host, MMC_POWEROFF_SUSPEND); if (!err) { pm_runtime_disable(&host->card->dev); pm_runtime_set_suspended(&host->card->dev); @@ -2187,15 +2213,16 @@ static int mmc_shutdown(struct mmc_host *host) int err = 0; /* - * In a specific case for poweroff notify, we need to resume the card - * before we can shutdown it properly. + * If the card remains suspended at this point and it was done by using + * the sleep-cmd (CMD5), we may need to re-initialize it first, to allow + * us to send the preferred poweroff-notification cmd at shutdown. */ - if (mmc_can_poweroff_notify(host->card) && - !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)) + if (mmc_card_can_poweroff_notify(host->card) && + !mmc_host_can_poweroff_notify(host, MMC_POWEROFF_SUSPEND)) err = _mmc_resume(host); if (!err) - err = _mmc_suspend(host, false); + err = _mmc_suspend(host, MMC_POWEROFF_SHUTDOWN); return err; } @@ -2219,7 +2246,7 @@ static int mmc_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = _mmc_suspend(host, true); + err = _mmc_suspend(host, MMC_POWEROFF_SUSPEND); if (err) pr_err("%s: error %d doing aggressive suspend\n", mmc_hostname(host), err); @@ -2242,14 +2269,12 @@ static int mmc_runtime_resume(struct mmc_host *host) return 0; } -static int mmc_can_reset(struct mmc_card *card) +static bool mmc_card_can_reset(struct mmc_card *card) { u8 rst_n_function; rst_n_function = card->ext_csd.rst_n_function; - if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED) - return 0; - return 1; + return ((rst_n_function & EXT_CSD_RST_N_EN_MASK) == EXT_CSD_RST_N_ENABLED); } static int _mmc_hw_reset(struct mmc_host *host) @@ -2263,7 +2288,7 @@ static int _mmc_hw_reset(struct mmc_host *host) _mmc_flush_cache(host); if ((host->caps & MMC_CAP_HW_RESET) && host->ops->card_hw_reset && - mmc_can_reset(card)) { + mmc_card_can_reset(card)) { /* If the card accept RST_n signal, send it. */ mmc_set_clock(host, host->f_init); host->ops->card_hw_reset(host); |