From 436f8daa6f5a2943a20df8f3447da250b46f0d87 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:18 +0300 Subject: mmc: mmc: Hold re-tuning in mmc_sleep() The sleep command is issued after deselecting the card, but re-tuning won't work on a deselected card so re-tuning must be held. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/core/mmc.c') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f36c76f8b232..fd41b289ac91 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -21,6 +21,7 @@ #include #include "core.h" +#include "host.h" #include "bus.h" #include "mmc_ops.h" #include "sd_ops.h" @@ -1511,9 +1512,12 @@ static int mmc_sleep(struct mmc_host *host) unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000); int err; + /* Re-tuning can't be done once the card is deselected */ + mmc_retune_hold(host); + err = mmc_deselect_cards(host); if (err) - return err; + goto out_release; cmd.opcode = MMC_SLEEP_AWAKE; cmd.arg = card->rca << 16; @@ -1534,7 +1538,7 @@ static int mmc_sleep(struct mmc_host *host) err = mmc_wait_for_cmd(host, &cmd, 0); if (err) - return err; + goto out_release; /* * If the host does not wait while the card signals busy, then we will @@ -1545,6 +1549,8 @@ static int mmc_sleep(struct mmc_host *host) if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) mmc_delay(timeout_ms); +out_release: + mmc_retune_release(host); return err; } -- cgit v1.2.3 From 6376f69d20a6905c1d83be451065f70200490b98 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:20 +0300 Subject: mmc: core: Add support for HS400 re-tuning HS400 re-tuning must be done in HS200 mode. Add the ability to switch from HS400 mode to HS200 mode before re-tuning and switch back to HS400 after re-tuning. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'drivers/mmc/core/mmc.c') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index fd41b289ac91..a8028633253a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1092,6 +1092,94 @@ static int mmc_select_hs400(struct mmc_card *card) return 0; } +int mmc_hs200_to_hs400(struct mmc_card *card) +{ + return mmc_select_hs400(card); +} + +/* Caller must hold re-tuning */ +static int mmc_switch_status(struct mmc_card *card) +{ + u32 status; + int err; + + err = mmc_send_status(card, &status); + if (err) + return err; + + return mmc_switch_status_error(card->host, status); +} + +int mmc_hs400_to_hs200(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + bool send_status = true; + unsigned int max_dtr; + int err; + + if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) + send_status = false; + + /* Reduce frequency to HS */ + max_dtr = card->ext_csd.hs_max_dtr; + mmc_set_clock(host, max_dtr); + + /* Switch HS400 to HS DDR */ + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, + EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time, + true, send_status, true); + if (err) + goto out_err; + + mmc_set_timing(host, MMC_TIMING_MMC_DDR52); + + if (!send_status) { + err = mmc_switch_status(card); + if (err) + goto out_err; + } + + /* Switch HS DDR to HS */ + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, + EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time, + true, send_status, true); + if (err) + goto out_err; + + mmc_set_timing(host, MMC_TIMING_MMC_HS); + + if (!send_status) { + err = mmc_switch_status(card); + if (err) + goto out_err; + } + + /* Switch HS to HS200 */ + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, + EXT_CSD_TIMING_HS200, + card->ext_csd.generic_cmd6_time, true, send_status, + true); + if (err) + goto out_err; + + mmc_set_timing(host, MMC_TIMING_MMC_HS200); + + if (!send_status) { + err = mmc_switch_status(card); + if (err) + goto out_err; + } + + mmc_set_bus_speed(card); + + return 0; + +out_err: + pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host), + __func__, err); + return err; +} + /* * For device supporting HS200 mode, the following sequence * should be done before executing the tuning process. -- cgit v1.2.3 From ded8a5f9613dbb62cd74f9f26a9caa3193eb3764 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 11 May 2015 07:34:53 +0000 Subject: mmc: cast u8 to unsigned long long to avoid unexpected error card->ext_csd.enhanced_area_offset is defined as "unsigned long long", and, ext_csd[] is defined as u8. unsigned long long data might have strange data if first bit of ext_csd[] was 1. this patch cast it to (unsigned long long) Special thanks to coverity ex) u8 data8; u64 data64; data8 = 0x80; data64 = (data8 << 24); // 0xffffffff80000000 data64 = (((unsigned long long)data8) << 24); // 0x80000000; Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/core/mmc.c') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a8028633253a..03c94c2f23e1 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -267,8 +267,10 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd) * calculate the enhanced data area offset, in bytes */ card->ext_csd.enhanced_area_offset = - (ext_csd[139] << 24) + (ext_csd[138] << 16) + - (ext_csd[137] << 8) + ext_csd[136]; + (((unsigned long long)ext_csd[139]) << 24) + + (((unsigned long long)ext_csd[138]) << 16) + + (((unsigned long long)ext_csd[137]) << 8) + + (((unsigned long long)ext_csd[136])); if (mmc_card_blockaddr(card)) card->ext_csd.enhanced_area_offset <<= 9; /* -- cgit v1.2.3 From b097e07f57930eda774c83aa46e8e401686d01dc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:57 +0200 Subject: mmc: mmc: Read card's valid driver strength mask In preparation for supporing drive strength selection for eMMC, read the card's valid driver strengths. Note that though the SD spec uses the term "drive strength", the JEDEC eMMC spec uses the term "driver strength". Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc/core/mmc.c') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 03c94c2f23e1..9b808d1d6ca7 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -437,6 +437,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.raw_trim_mult = ext_csd[EXT_CSD_TRIM_MULT]; card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT]; + card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH]; if (card->ext_csd.rev >= 4) { if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] & EXT_CSD_PART_SETTING_COMPLETED) -- cgit v1.2.3 From cc4f414c885cd04f7227ad9bcd6b18fd78d718d9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:58 +0200 Subject: mmc: mmc: Add driver strength selection Add the ability to set eMMC driver strength for HS200 and HS400. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'drivers/mmc/core/mmc.c') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 9b808d1d6ca7..e519e3110a20 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1044,6 +1044,7 @@ static int mmc_select_hs400(struct mmc_card *card) { struct mmc_host *host = card->host; int err = 0; + u8 val; /* * HS400 mode requires 8-bit bus width @@ -1059,8 +1060,10 @@ static int mmc_select_hs400(struct mmc_card *card) mmc_set_timing(card->host, MMC_TIMING_MMC_HS); mmc_set_bus_speed(card); + val = EXT_CSD_TIMING_HS | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, + EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, true, true, true); if (err) { @@ -1079,8 +1082,10 @@ static int mmc_select_hs400(struct mmc_card *card) return err; } + val = EXT_CSD_TIMING_HS400 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, + EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, true, true, true); if (err) { @@ -1119,6 +1124,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) bool send_status = true; unsigned int max_dtr; int err; + u8 val; if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) send_status = false; @@ -1128,8 +1134,10 @@ int mmc_hs400_to_hs200(struct mmc_card *card) mmc_set_clock(host, max_dtr); /* Switch HS400 to HS DDR */ + val = EXT_CSD_TIMING_HS | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, - EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time, + val, card->ext_csd.generic_cmd6_time, true, send_status, true); if (err) goto out_err; @@ -1158,10 +1166,11 @@ int mmc_hs400_to_hs200(struct mmc_card *card) } /* Switch HS to HS200 */ + val = EXT_CSD_TIMING_HS200 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, - EXT_CSD_TIMING_HS200, - card->ext_csd.generic_cmd6_time, true, send_status, - true); + val, card->ext_csd.generic_cmd6_time, true, + send_status, true); if (err) goto out_err; @@ -1183,6 +1192,23 @@ out_err: return err; } +static void mmc_select_driver_type(struct mmc_card *card) +{ + int card_drv_type, drive_strength, drv_type; + + card_drv_type = card->ext_csd.raw_driver_strength | + mmc_driver_type_mask(0); + + drive_strength = mmc_select_drive_strength(card, + card->ext_csd.hs200_max_dtr, + card_drv_type, &drv_type); + + card->drive_strength = drive_strength; + + if (drv_type) + mmc_set_driver_type(card->host, drv_type); +} + /* * For device supporting HS200 mode, the following sequence * should be done before executing the tuning process. @@ -1194,6 +1220,7 @@ static int mmc_select_hs200(struct mmc_card *card) { struct mmc_host *host = card->host; int err = -EINVAL; + u8 val; if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); @@ -1205,14 +1232,18 @@ static int mmc_select_hs200(struct mmc_card *card) if (err) goto err; + mmc_select_driver_type(card); + /* * Set the bus width(4 or 8) with host's support and * switch to HS200 mode if bus width is set successfully. */ err = mmc_select_bus_width(card); if (!IS_ERR_VALUE(err)) { + val = EXT_CSD_TIMING_HS200 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, + EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, true, true, true); if (!err) -- cgit v1.2.3 From 364549ddc29dd5490e81039cb85fbf0123365754 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 1 Jun 2015 11:14:57 +0200 Subject: mmc: core: Remove redundant ->power_restore() callback for MMC Since the ->reset() callback is implemented for MMC, the ->power_restore() callback has become redundant, let's remove it. Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/mmc/core/mmc.c') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index e519e3110a20..e726903170a8 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1910,17 +1910,6 @@ static int mmc_runtime_resume(struct mmc_host *host) return 0; } -static int mmc_power_restore(struct mmc_host *host) -{ - int ret; - - mmc_claim_host(host); - ret = mmc_init_card(host, host->card->ocr, host->card); - mmc_release_host(host); - - return ret; -} - int mmc_can_reset(struct mmc_card *card) { u8 rst_n_function; @@ -1958,7 +1947,7 @@ static int mmc_reset(struct mmc_host *host) mmc_set_initial_state(host); mmc_host_clk_release(host); - return mmc_power_restore(host); + return mmc_init_card(host, card->ocr, card); } static const struct mmc_bus_ops mmc_ops = { @@ -1968,7 +1957,6 @@ static const struct mmc_bus_ops mmc_ops = { .resume = mmc_resume, .runtime_suspend = mmc_runtime_suspend, .runtime_resume = mmc_runtime_resume, - .power_restore = mmc_power_restore, .alive = mmc_alive, .shutdown = mmc_shutdown, .reset = mmc_reset, -- cgit v1.2.3