Loading drivers/mmc/core/mmc.c +162 −13 Original line number Diff line number Diff line Loading @@ -1405,7 +1405,7 @@ static int mmc_select_cmdq(struct mmc_card *card) } mmc_host_clk_release(card->host); pr_info("%s: CMDQ enabled on card\n", mmc_hostname(host)); pr_info_once("%s: CMDQ enabled on card\n", mmc_hostname(host)); out: return ret; } Loading Loading @@ -2049,24 +2049,28 @@ 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 = {0}; struct mmc_card *card = host->card; unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000); int err; if (sleep) { err = mmc_deselect_cards(host); if (err) return err; } cmd.opcode = MMC_SLEEP_AWAKE; cmd.arg = card->rca << 16; if (sleep) cmd.arg |= 1 << 15; /* Loading Loading @@ -2095,6 +2099,9 @@ static int mmc_sleep(struct mmc_host *host) if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) mmc_delay(timeout_ms); if (!sleep) err = mmc_select_card(card); return err; } Loading Loading @@ -2203,6 +2210,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]; 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]; mmc_free_ext_csd(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], 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]) && (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])); mmc_free_ext_csd(ext_csd); return err; } static int _mmc_suspend(struct mmc_host *host, bool is_suspend) { int err = 0; Loading Loading @@ -2254,10 +2324,13 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) if (err) goto out; if (mmc_can_sleep(host->card)) err = mmc_sleep(host); else if (!mmc_host_is_spi(host)) if (mmc_can_sleepawake(host)) { memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios)); mmc_cache_card_ext_csd(host); err = mmc_sleepawake(host, true); } else if (!mmc_host_is_spi(host)) { err = mmc_deselect_cards(host); } if (!err) { mmc_power_off(host); Loading @@ -2272,6 +2345,71 @@ out: 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); mmc_host_clk_hold(host); if (mmc_card_hs200(card) || mmc_card_hs400(card)) { if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) err = host->ops->enhanced_strobe(host); else 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__); if (card->ext_csd.cmdq_support && (card->host->caps2 & MMC_CAP2_CMD_QUEUE)) { err = mmc_select_cmdq(card); if (err) { pr_warn("%s: %s: enabling CMDQ mode failed (%d)\n", mmc_hostname(card->host), __func__, err); } } out: mmc_host_clk_release(host); pr_debug("%s: %s: done partial init (%d)\n", mmc_hostname(host), __func__, err); return err; } /* * Suspend callback */ Loading @@ -2297,7 +2435,7 @@ static int mmc_suspend(struct mmc_host *host) */ static int _mmc_resume(struct mmc_host *host) { int err = 0; int err = -ENOSYS; int retries; BUG_ON(!host); Loading @@ -2313,7 +2451,18 @@ static int _mmc_resume(struct mmc_host *host) mmc_power_up(host, host->card->ocr); retries = 3; 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); if (err) { pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n", mmc_hostname(host), err, retries); Loading include/linux/mmc/card.h +4 −0 Original line number Diff line number Diff line Loading @@ -93,12 +93,16 @@ struct mmc_ext_csd { unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int boot_ro_lock; /* ro lock support */ bool boot_ro_lockable; u8 raw_ext_csd_cmdq; /* 15 */ u8 raw_ext_csd_cache_ctrl; /* 33 */ u8 raw_exception_status; /* 54 */ u8 raw_partition_support; /* 160 */ u8 raw_rpmb_size_mult; /* 168 */ u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_bus_width; /* 183 */ u8 strobe_support; /* 184 */ #define MMC_STROBE_SUPPORT (1 << 0) u8 raw_ext_csd_hs_timing; /* 185 */ u8 raw_ext_csd_structure; /* 194 */ u8 raw_card_type; /* 196 */ u8 raw_drive_strength; /* 197 */ Loading include/linux/mmc/host.h +3 −0 Original line number Diff line number Diff line Loading @@ -433,6 +433,8 @@ struct mmc_host { #define MMC_CAP2_NONHOTPLUG (1 << 25) /*Don't support hotplug*/ #define MMC_CAP2_CMD_QUEUE (1 << 26) /* support eMMC command queue */ #define MMC_CAP2_SANITIZE (1 << 27) /* Support Sanitize */ #define MMC_CAP2_SLEEP_AWAKE (1 << 28) /* Use Sleep/Awake (CMD5) */ mmc_pm_flag_t pm_caps; /* supported pm features */ #ifdef CONFIG_MMC_CLKGATE Loading Loading @@ -460,6 +462,7 @@ struct mmc_host { spinlock_t lock; /* lock for claim and bus ops */ struct mmc_ios ios; /* current io bus settings */ struct mmc_ios cached_ios; /* group bitfields together to minimize padding */ unsigned int use_spi_crc:1; Loading Loading
drivers/mmc/core/mmc.c +162 −13 Original line number Diff line number Diff line Loading @@ -1405,7 +1405,7 @@ static int mmc_select_cmdq(struct mmc_card *card) } mmc_host_clk_release(card->host); pr_info("%s: CMDQ enabled on card\n", mmc_hostname(host)); pr_info_once("%s: CMDQ enabled on card\n", mmc_hostname(host)); out: return ret; } Loading Loading @@ -2049,24 +2049,28 @@ 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 = {0}; struct mmc_card *card = host->card; unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000); int err; if (sleep) { err = mmc_deselect_cards(host); if (err) return err; } cmd.opcode = MMC_SLEEP_AWAKE; cmd.arg = card->rca << 16; if (sleep) cmd.arg |= 1 << 15; /* Loading Loading @@ -2095,6 +2099,9 @@ static int mmc_sleep(struct mmc_host *host) if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) mmc_delay(timeout_ms); if (!sleep) err = mmc_select_card(card); return err; } Loading Loading @@ -2203,6 +2210,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]; 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]; mmc_free_ext_csd(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], 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]) && (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])); mmc_free_ext_csd(ext_csd); return err; } static int _mmc_suspend(struct mmc_host *host, bool is_suspend) { int err = 0; Loading Loading @@ -2254,10 +2324,13 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) if (err) goto out; if (mmc_can_sleep(host->card)) err = mmc_sleep(host); else if (!mmc_host_is_spi(host)) if (mmc_can_sleepawake(host)) { memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios)); mmc_cache_card_ext_csd(host); err = mmc_sleepawake(host, true); } else if (!mmc_host_is_spi(host)) { err = mmc_deselect_cards(host); } if (!err) { mmc_power_off(host); Loading @@ -2272,6 +2345,71 @@ out: 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); mmc_host_clk_hold(host); if (mmc_card_hs200(card) || mmc_card_hs400(card)) { if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) err = host->ops->enhanced_strobe(host); else 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__); if (card->ext_csd.cmdq_support && (card->host->caps2 & MMC_CAP2_CMD_QUEUE)) { err = mmc_select_cmdq(card); if (err) { pr_warn("%s: %s: enabling CMDQ mode failed (%d)\n", mmc_hostname(card->host), __func__, err); } } out: mmc_host_clk_release(host); pr_debug("%s: %s: done partial init (%d)\n", mmc_hostname(host), __func__, err); return err; } /* * Suspend callback */ Loading @@ -2297,7 +2435,7 @@ static int mmc_suspend(struct mmc_host *host) */ static int _mmc_resume(struct mmc_host *host) { int err = 0; int err = -ENOSYS; int retries; BUG_ON(!host); Loading @@ -2313,7 +2451,18 @@ static int _mmc_resume(struct mmc_host *host) mmc_power_up(host, host->card->ocr); retries = 3; 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); if (err) { pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n", mmc_hostname(host), err, retries); Loading
include/linux/mmc/card.h +4 −0 Original line number Diff line number Diff line Loading @@ -93,12 +93,16 @@ struct mmc_ext_csd { unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int boot_ro_lock; /* ro lock support */ bool boot_ro_lockable; u8 raw_ext_csd_cmdq; /* 15 */ u8 raw_ext_csd_cache_ctrl; /* 33 */ u8 raw_exception_status; /* 54 */ u8 raw_partition_support; /* 160 */ u8 raw_rpmb_size_mult; /* 168 */ u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_bus_width; /* 183 */ u8 strobe_support; /* 184 */ #define MMC_STROBE_SUPPORT (1 << 0) u8 raw_ext_csd_hs_timing; /* 185 */ u8 raw_ext_csd_structure; /* 194 */ u8 raw_card_type; /* 196 */ u8 raw_drive_strength; /* 197 */ Loading
include/linux/mmc/host.h +3 −0 Original line number Diff line number Diff line Loading @@ -433,6 +433,8 @@ struct mmc_host { #define MMC_CAP2_NONHOTPLUG (1 << 25) /*Don't support hotplug*/ #define MMC_CAP2_CMD_QUEUE (1 << 26) /* support eMMC command queue */ #define MMC_CAP2_SANITIZE (1 << 27) /* Support Sanitize */ #define MMC_CAP2_SLEEP_AWAKE (1 << 28) /* Use Sleep/Awake (CMD5) */ mmc_pm_flag_t pm_caps; /* supported pm features */ #ifdef CONFIG_MMC_CLKGATE Loading Loading @@ -460,6 +462,7 @@ struct mmc_host { spinlock_t lock; /* lock for claim and bus ops */ struct mmc_ios ios; /* current io bus settings */ struct mmc_ios cached_ios; /* group bitfields together to minimize padding */ unsigned int use_spi_crc:1; Loading