Loading drivers/mmc/core/mmc.c +192 −20 Original line number Original line Diff line number Diff line Loading @@ -2316,12 +2316,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, return err; return err; } } static int mmc_can_sleep(struct mmc_card *card) static int mmc_can_sleepawake(struct mmc_host *host) { { return (card && card->ext_csd.rev >= 3); return host && (host->caps2 & MMC_CAP2_SLEEP_AWAKE) && host->card && (host->card->ext_csd.rev >= 3); } } static int mmc_sleep(struct mmc_host *host) static int mmc_sleepawake(struct mmc_host *host, bool sleep) { { struct mmc_command cmd = {}; struct mmc_command cmd = {}; struct mmc_card *card = host->card; struct mmc_card *card = host->card; Loading @@ -2331,12 +2332,15 @@ static int mmc_sleep(struct mmc_host *host) /* Re-tuning can't be done once the card is deselected */ /* Re-tuning can't be done once the card is deselected */ mmc_retune_hold(host); mmc_retune_hold(host); if (sleep) { err = mmc_deselect_cards(host); err = mmc_deselect_cards(host); if (err) if (err) goto out_release; goto out_release; } cmd.opcode = MMC_SLEEP_AWAKE; cmd.opcode = MMC_SLEEP_AWAKE; cmd.arg = card->rca << 16; cmd.arg = card->rca << 16; if (sleep) cmd.arg |= 1 << 15; cmd.arg |= 1 << 15; /* /* Loading Loading @@ -2365,6 +2369,9 @@ static int mmc_sleep(struct mmc_host *host) if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) mmc_delay(timeout_ms); mmc_delay(timeout_ms); if (!sleep) err = mmc_select_card(card); out_release: out_release: mmc_retune_release(host); mmc_retune_release(host); return err; return err; Loading Loading @@ -2467,6 +2474,69 @@ static void mmc_detect(struct mmc_host *host) } } } } static int mmc_cache_card_ext_csd(struct mmc_host *host) { int err; u8 *ext_csd; struct mmc_card *card = host->card; err = mmc_get_ext_csd(card, &ext_csd); if (err || !ext_csd) { pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n", mmc_hostname(host), __func__, err); return err; } /* only cache read/write fields that the sw changes */ card->ext_csd.raw_ext_csd_cmdq = ext_csd[EXT_CSD_CMDQ_MODE_EN]; card->ext_csd.raw_ext_csd_cache_ctrl = ext_csd[EXT_CSD_CACHE_CTRL]; card->ext_csd.raw_ext_csd_bus_width = ext_csd[EXT_CSD_BUS_WIDTH]; card->ext_csd.raw_ext_csd_hs_timing = ext_csd[EXT_CSD_HS_TIMING]; kfree(ext_csd); return 0; } static int mmc_test_awake_ext_csd(struct mmc_host *host) { int err; u8 *ext_csd; struct mmc_card *card = host->card; err = mmc_get_ext_csd(card, &ext_csd); if (err) { pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n", mmc_hostname(host), __func__, err); return err; } /* only compare read/write fields that the sw changes */ pr_debug("%s: %s: type(cached:current) cmdq(%d:%d) cache_ctrl(%d:%d) bus_width (%d:%d) timing(%d:%d)\n", mmc_hostname(host), __func__, card->ext_csd.raw_ext_csd_cmdq, ext_csd[EXT_CSD_CMDQ_MODE_EN], card->ext_csd.raw_ext_csd_cache_ctrl, ext_csd[EXT_CSD_CACHE_CTRL], card->ext_csd.raw_ext_csd_bus_width, ext_csd[EXT_CSD_BUS_WIDTH], card->ext_csd.raw_ext_csd_hs_timing, ext_csd[EXT_CSD_HS_TIMING]); err = !((card->ext_csd.raw_ext_csd_cmdq == ext_csd[EXT_CSD_CMDQ_MODE_EN]) && (card->ext_csd.raw_ext_csd_cache_ctrl == ext_csd[EXT_CSD_CACHE_CTRL]) && (card->ext_csd.raw_ext_csd_bus_width == ext_csd[EXT_CSD_BUS_WIDTH]) && (card->ext_csd.raw_ext_csd_hs_timing == ext_csd[EXT_CSD_HS_TIMING])); kfree(ext_csd); return err; } static int _mmc_suspend(struct mmc_host *host, bool is_suspend) static int _mmc_suspend(struct mmc_host *host, bool is_suspend) { { int err = 0; int err = 0; Loading Loading @@ -2500,10 +2570,13 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) if (err) if (err) goto out; goto out; if (mmc_can_sleep(host->card)) if (mmc_can_sleepawake(host)) { err = mmc_sleep(host); memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios)); else if (!mmc_host_is_spi(host)) mmc_cache_card_ext_csd(host); err = mmc_sleepawake(host, true); } else if (!mmc_host_is_spi(host)) { err = mmc_deselect_cards(host); err = mmc_deselect_cards(host); } if (!err) { if (!err) { mmc_power_off(host); mmc_power_off(host); Loading @@ -2517,6 +2590,93 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) return err; return err; } } static int mmc_partial_init(struct mmc_host *host) { int err = 0; struct mmc_card *card = host->card; pr_debug("%s: %s: starting partial init\n", mmc_hostname(host), __func__); mmc_set_bus_width(host, host->cached_ios.bus_width); mmc_set_timing(host, host->cached_ios.timing); mmc_set_clock(host, host->cached_ios.clock); mmc_set_bus_mode(host, host->cached_ios.bus_mode); if (mmc_card_hs400(card)) { if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) err = host->ops->enhanced_strobe(host); else if (host->ops->execute_tuning) err = host->ops->execute_tuning(host, MMC_SEND_TUNING_BLOCK_HS200); } else if (mmc_card_hs200(card) && host->ops->execute_tuning) { err = host->ops->execute_tuning(host, MMC_SEND_TUNING_BLOCK_HS200); if (err) pr_warn("%s: %s: tuning execution failed (%d)\n", mmc_hostname(host), __func__, err); } /* * The ext_csd is read to make sure the card did not went through * Power-failure during sleep period. * A subset of the W/E_P, W/C_P register will be tested. In case * these registers values are different from the values that were * cached during suspend, we will conclude that a Power-failure occurred * and will do full initialization sequence. * In addition, full init sequence also transfer ext_csd before moving * to CMDQ mode which has a side affect of configuring SDHCI registers * which needed to be done before moving to CMDQ mode. The same * registers need to be configured for partial init. */ err = mmc_test_awake_ext_csd(host); if (err) { pr_debug("%s: %s: fail on ext_csd read (%d)\n", mmc_hostname(host), __func__, err); goto out; } pr_debug("%s: %s: reading and comparing ext_csd successful\n", mmc_hostname(host), __func__); /* * Enable Command Queue if supported. Note that Packed Commands cannot * be used with Command Queue. */ card->ext_csd.cmdq_en = false; if (card->ext_csd.cmdq_support && host->caps2 & MMC_CAP2_CQE) { err = mmc_cmdq_enable(card); if (err) { pr_warn("%s: Enabling CMDQ failed\n", mmc_hostname(card->host)); card->ext_csd.cmdq_support = false; card->ext_csd.cmdq_depth = 0; goto out; } } /* * In some cases (e.g. RPMB or mmc_test), the Command Queue must be * disabled for a time, so a flag is needed to indicate to re-enable the * Command Queue. */ card->reenable_cmdq = card->ext_csd.cmdq_en; if (card->ext_csd.cmdq_en && !host->cqe_enabled) { err = host->cqe_ops->cqe_enable(host, card); if (err) { pr_err("%s: Failed to enable CQE, error %d\n", mmc_hostname(host), err); } else { host->cqe_enabled = true; pr_debug("%s: Command Queue Engine enabled\n", mmc_hostname(host)); } } out: pr_debug("%s: %s: done partial init (%d)\n", mmc_hostname(host), __func__, err); return err; } /* /* * Suspend callback * Suspend callback */ */ Loading @@ -2542,7 +2702,7 @@ static int mmc_suspend(struct mmc_host *host) */ */ static int _mmc_resume(struct mmc_host *host) static int _mmc_resume(struct mmc_host *host) { { int err = 0; int err = -EINVAL; int retries = 3; int retries = 3; mmc_claim_host(host); mmc_claim_host(host); Loading @@ -2555,10 +2715,21 @@ static int _mmc_resume(struct mmc_host *host) mmc_log_string(host, "Enter\n"); mmc_log_string(host, "Enter\n"); mmc_power_up(host, host->card->ocr); mmc_power_up(host, host->card->ocr); while (retries) { while (retries) { if (mmc_can_sleepawake(host)) { err = mmc_sleepawake(host, false); if (!err) err = mmc_partial_init(host); if (err) pr_err("%s: %s: awake failed (%d), fallback to full init\n", mmc_hostname(host), __func__, err); } if (err) { err = mmc_init_card(host, host->card->ocr, host->card); err = mmc_init_card(host, host->card->ocr, host->card); if (err) { if (err) { pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n", pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n", mmc_hostname(host), err, retries); mmc_hostname(host), err, retries); retries--; retries--; mmc_power_off(host); mmc_power_off(host); usleep_range(5000, 5500); usleep_range(5000, 5500); Loading @@ -2566,6 +2737,7 @@ static int _mmc_resume(struct mmc_host *host) mmc_select_voltage(host, host->card->ocr); mmc_select_voltage(host, host->card->ocr); continue; continue; } } } break; break; } } mmc_card_clr_suspended(host->card); mmc_card_clr_suspended(host->card); Loading Loading
drivers/mmc/core/mmc.c +192 −20 Original line number Original line Diff line number Diff line Loading @@ -2316,12 +2316,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, return err; return err; } } static int mmc_can_sleep(struct mmc_card *card) static int mmc_can_sleepawake(struct mmc_host *host) { { return (card && card->ext_csd.rev >= 3); return host && (host->caps2 & MMC_CAP2_SLEEP_AWAKE) && host->card && (host->card->ext_csd.rev >= 3); } } static int mmc_sleep(struct mmc_host *host) static int mmc_sleepawake(struct mmc_host *host, bool sleep) { { struct mmc_command cmd = {}; struct mmc_command cmd = {}; struct mmc_card *card = host->card; struct mmc_card *card = host->card; Loading @@ -2331,12 +2332,15 @@ static int mmc_sleep(struct mmc_host *host) /* Re-tuning can't be done once the card is deselected */ /* Re-tuning can't be done once the card is deselected */ mmc_retune_hold(host); mmc_retune_hold(host); if (sleep) { err = mmc_deselect_cards(host); err = mmc_deselect_cards(host); if (err) if (err) goto out_release; goto out_release; } cmd.opcode = MMC_SLEEP_AWAKE; cmd.opcode = MMC_SLEEP_AWAKE; cmd.arg = card->rca << 16; cmd.arg = card->rca << 16; if (sleep) cmd.arg |= 1 << 15; cmd.arg |= 1 << 15; /* /* Loading Loading @@ -2365,6 +2369,9 @@ static int mmc_sleep(struct mmc_host *host) if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) mmc_delay(timeout_ms); mmc_delay(timeout_ms); if (!sleep) err = mmc_select_card(card); out_release: out_release: mmc_retune_release(host); mmc_retune_release(host); return err; return err; Loading Loading @@ -2467,6 +2474,69 @@ static void mmc_detect(struct mmc_host *host) } } } } static int mmc_cache_card_ext_csd(struct mmc_host *host) { int err; u8 *ext_csd; struct mmc_card *card = host->card; err = mmc_get_ext_csd(card, &ext_csd); if (err || !ext_csd) { pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n", mmc_hostname(host), __func__, err); return err; } /* only cache read/write fields that the sw changes */ card->ext_csd.raw_ext_csd_cmdq = ext_csd[EXT_CSD_CMDQ_MODE_EN]; card->ext_csd.raw_ext_csd_cache_ctrl = ext_csd[EXT_CSD_CACHE_CTRL]; card->ext_csd.raw_ext_csd_bus_width = ext_csd[EXT_CSD_BUS_WIDTH]; card->ext_csd.raw_ext_csd_hs_timing = ext_csd[EXT_CSD_HS_TIMING]; kfree(ext_csd); return 0; } static int mmc_test_awake_ext_csd(struct mmc_host *host) { int err; u8 *ext_csd; struct mmc_card *card = host->card; err = mmc_get_ext_csd(card, &ext_csd); if (err) { pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n", mmc_hostname(host), __func__, err); return err; } /* only compare read/write fields that the sw changes */ pr_debug("%s: %s: type(cached:current) cmdq(%d:%d) cache_ctrl(%d:%d) bus_width (%d:%d) timing(%d:%d)\n", mmc_hostname(host), __func__, card->ext_csd.raw_ext_csd_cmdq, ext_csd[EXT_CSD_CMDQ_MODE_EN], card->ext_csd.raw_ext_csd_cache_ctrl, ext_csd[EXT_CSD_CACHE_CTRL], card->ext_csd.raw_ext_csd_bus_width, ext_csd[EXT_CSD_BUS_WIDTH], card->ext_csd.raw_ext_csd_hs_timing, ext_csd[EXT_CSD_HS_TIMING]); err = !((card->ext_csd.raw_ext_csd_cmdq == ext_csd[EXT_CSD_CMDQ_MODE_EN]) && (card->ext_csd.raw_ext_csd_cache_ctrl == ext_csd[EXT_CSD_CACHE_CTRL]) && (card->ext_csd.raw_ext_csd_bus_width == ext_csd[EXT_CSD_BUS_WIDTH]) && (card->ext_csd.raw_ext_csd_hs_timing == ext_csd[EXT_CSD_HS_TIMING])); kfree(ext_csd); return err; } static int _mmc_suspend(struct mmc_host *host, bool is_suspend) static int _mmc_suspend(struct mmc_host *host, bool is_suspend) { { int err = 0; int err = 0; Loading Loading @@ -2500,10 +2570,13 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) if (err) if (err) goto out; goto out; if (mmc_can_sleep(host->card)) if (mmc_can_sleepawake(host)) { err = mmc_sleep(host); memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios)); else if (!mmc_host_is_spi(host)) mmc_cache_card_ext_csd(host); err = mmc_sleepawake(host, true); } else if (!mmc_host_is_spi(host)) { err = mmc_deselect_cards(host); err = mmc_deselect_cards(host); } if (!err) { if (!err) { mmc_power_off(host); mmc_power_off(host); Loading @@ -2517,6 +2590,93 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) return err; return err; } } static int mmc_partial_init(struct mmc_host *host) { int err = 0; struct mmc_card *card = host->card; pr_debug("%s: %s: starting partial init\n", mmc_hostname(host), __func__); mmc_set_bus_width(host, host->cached_ios.bus_width); mmc_set_timing(host, host->cached_ios.timing); mmc_set_clock(host, host->cached_ios.clock); mmc_set_bus_mode(host, host->cached_ios.bus_mode); if (mmc_card_hs400(card)) { if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) err = host->ops->enhanced_strobe(host); else if (host->ops->execute_tuning) err = host->ops->execute_tuning(host, MMC_SEND_TUNING_BLOCK_HS200); } else if (mmc_card_hs200(card) && host->ops->execute_tuning) { err = host->ops->execute_tuning(host, MMC_SEND_TUNING_BLOCK_HS200); if (err) pr_warn("%s: %s: tuning execution failed (%d)\n", mmc_hostname(host), __func__, err); } /* * The ext_csd is read to make sure the card did not went through * Power-failure during sleep period. * A subset of the W/E_P, W/C_P register will be tested. In case * these registers values are different from the values that were * cached during suspend, we will conclude that a Power-failure occurred * and will do full initialization sequence. * In addition, full init sequence also transfer ext_csd before moving * to CMDQ mode which has a side affect of configuring SDHCI registers * which needed to be done before moving to CMDQ mode. The same * registers need to be configured for partial init. */ err = mmc_test_awake_ext_csd(host); if (err) { pr_debug("%s: %s: fail on ext_csd read (%d)\n", mmc_hostname(host), __func__, err); goto out; } pr_debug("%s: %s: reading and comparing ext_csd successful\n", mmc_hostname(host), __func__); /* * Enable Command Queue if supported. Note that Packed Commands cannot * be used with Command Queue. */ card->ext_csd.cmdq_en = false; if (card->ext_csd.cmdq_support && host->caps2 & MMC_CAP2_CQE) { err = mmc_cmdq_enable(card); if (err) { pr_warn("%s: Enabling CMDQ failed\n", mmc_hostname(card->host)); card->ext_csd.cmdq_support = false; card->ext_csd.cmdq_depth = 0; goto out; } } /* * In some cases (e.g. RPMB or mmc_test), the Command Queue must be * disabled for a time, so a flag is needed to indicate to re-enable the * Command Queue. */ card->reenable_cmdq = card->ext_csd.cmdq_en; if (card->ext_csd.cmdq_en && !host->cqe_enabled) { err = host->cqe_ops->cqe_enable(host, card); if (err) { pr_err("%s: Failed to enable CQE, error %d\n", mmc_hostname(host), err); } else { host->cqe_enabled = true; pr_debug("%s: Command Queue Engine enabled\n", mmc_hostname(host)); } } out: pr_debug("%s: %s: done partial init (%d)\n", mmc_hostname(host), __func__, err); return err; } /* /* * Suspend callback * Suspend callback */ */ Loading @@ -2542,7 +2702,7 @@ static int mmc_suspend(struct mmc_host *host) */ */ static int _mmc_resume(struct mmc_host *host) static int _mmc_resume(struct mmc_host *host) { { int err = 0; int err = -EINVAL; int retries = 3; int retries = 3; mmc_claim_host(host); mmc_claim_host(host); Loading @@ -2555,10 +2715,21 @@ static int _mmc_resume(struct mmc_host *host) mmc_log_string(host, "Enter\n"); mmc_log_string(host, "Enter\n"); mmc_power_up(host, host->card->ocr); mmc_power_up(host, host->card->ocr); while (retries) { while (retries) { if (mmc_can_sleepawake(host)) { err = mmc_sleepawake(host, false); if (!err) err = mmc_partial_init(host); if (err) pr_err("%s: %s: awake failed (%d), fallback to full init\n", mmc_hostname(host), __func__, err); } if (err) { err = mmc_init_card(host, host->card->ocr, host->card); err = mmc_init_card(host, host->card->ocr, host->card); if (err) { if (err) { pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n", pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n", mmc_hostname(host), err, retries); mmc_hostname(host), err, retries); retries--; retries--; mmc_power_off(host); mmc_power_off(host); usleep_range(5000, 5500); usleep_range(5000, 5500); Loading @@ -2566,6 +2737,7 @@ static int _mmc_resume(struct mmc_host *host) mmc_select_voltage(host, host->card->ocr); mmc_select_voltage(host, host->card->ocr); continue; continue; } } } break; break; } } mmc_card_clr_suspended(host->card); mmc_card_clr_suspended(host->card); Loading