From 18e7c8f6aa5fe3893b85830abf2458559087b42e Mon Sep 17 00:00:00 2001 From: Suniel Mahesh Date: Thu, 5 Oct 2017 11:32:00 +0530 Subject: [PATCH 01/56] drivers: mmc: Change buffer type in ALLOC_CACHE_ALIGN_BUFFER macro __be32_to_cpu() accepts argument of type __be32. This patch changes type of the buffer in ALLOC_CACHE_ALIGN_BUFFER macro to __be32, which is then passed to __be32_to_cpu(). This prevents sparse build warnings. drivers/mmc/mmc.c: warning: cast to restricted __be32 Signed-off-by: Suniel Mahesh Signed-off-by: Karthik Tummala --- drivers/mmc/mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 38d2e07dd5..8716ac7308 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -882,8 +882,8 @@ static int sd_change_freq(struct mmc *mmc) { int err; struct mmc_cmd cmd; - ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2); - ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); + ALLOC_CACHE_ALIGN_BUFFER(__be32, scr, 2); + ALLOC_CACHE_ALIGN_BUFFER(__be32, switch_status, 16); struct mmc_data data; int timeout; From 0e1746acacbb6dcaa0e9205f2712faf9d1e321a0 Mon Sep 17 00:00:00 2001 From: Suniel Mahesh Date: Thu, 5 Oct 2017 11:48:56 +0530 Subject: [PATCH 02/56] drivers: mmc: Avoid memory leak in case of failure priv pointer should be freed before returning with an error value from exynos_dwmci_get_config(). Signed-off-by: Suniel Mahesh Signed-off-by: Raghu Bharadwaj --- drivers/mmc/exynos_dw_mmc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 5edd383c68..e40575e589 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -168,6 +168,7 @@ static int exynos_dwmci_get_config(const void *blob, int node, if (host->dev_index > 4) { printf("DWMMC%d: Can't get the dev index\n", host->dev_index); + free(priv); return -EINVAL; } @@ -178,6 +179,7 @@ static int exynos_dwmci_get_config(const void *blob, int node, base = fdtdec_get_addr(blob, node, "reg"); if (!base) { printf("DWMMC%d: Can't get base address\n", host->dev_index); + free(priv); return -EINVAL; } host->ioaddr = (void *)base; @@ -187,6 +189,7 @@ static int exynos_dwmci_get_config(const void *blob, int node, if (err) { printf("DWMMC%d: Can't get sdr-timings for devider\n", host->dev_index); + free(priv); return -EINVAL; } From 339d578900edb03b633b3de4dad3da0aea2c1b15 Mon Sep 17 00:00:00 2001 From: Felix Brack Date: Wed, 11 Oct 2017 17:05:28 +0200 Subject: [PATCH 03/56] mmc: sanitize includes for DM i2c This patch fixes some warnings when building boards that do not define DM_I2C_COMPAT i.e. boards that entirely rely on the new i2c layer. Signed-off-by: Felix Brack --- drivers/mmc/omap_hsmmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index efa43896fc..30443d13a0 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -28,9 +28,9 @@ #include #include #include -#include -#include +#if defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX) #include +#endif #include #include #if !defined(CONFIG_SOC_KEYSTONE) From 713e6815d953108a78bd33470ab90fc2ec5a7198 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Thu, 2 Nov 2017 15:10:21 +0100 Subject: [PATCH 04/56] mmc: sdhci: don't clear SDHCI_INT_STATUS register during CMD_INHIBIT Fixes emmc initialization regression on the db410c platform. Clearing this register while SDHCI_PRESENT_STATE reports SDHCI_CMD_INHIBIT leads to undefined behaviour on the db410c. When commit 7dde50 was merged (mmc: sdhci: Wait for SDHCI_INT_DATA_END when transferring), SDHCI transfers transitioned to wait for bit SDHCI_INT_DATA_END before flagging transfers done. Without this patch, the db410 platform fails to initialize its eMMC due to all of its transfers timing out (SDHCI_INT_DATA_END is never raised after all the blocks have been transferred). Signed-off-by: Jorge Ramirez-Ortiz --- drivers/mmc/sdhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 11d1f0c24c..f0c5aad7ca 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -157,7 +157,6 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, /* Timeout unit - ms */ static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; - sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even @@ -181,6 +180,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, udelay(1000); } + sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); + mask = SDHCI_INT_RESPONSE; if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; From bb7b4ef3704cfd25e30a36f76342df118ec24a25 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 10 Nov 2017 21:13:34 +0100 Subject: [PATCH 05/56] mmc: sdhci: do not compare pointer to 0 data is defined as struct mmc_data *data. So it should not be compared to 0. Problem identified with Coccinelle. Signed-off-by: Heinrich Schuchardt Reviewed-by: Bin Meng --- drivers/mmc/sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index f0c5aad7ca..e2ddf5dccd 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -202,7 +202,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, flags |= SDHCI_CMD_DATA; /* Set Transfer mode regarding to data flag */ - if (data != 0) { + if (data) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); mode = SDHCI_TRNS_BLK_CNT_EN; trans_bytes = data->blocks * data->blocksize; @@ -250,7 +250,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); #ifdef CONFIG_MMC_SDHCI_SDMA - if (data != 0) { + if (data) { trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE); flush_cache(start_addr, trans_bytes); } From 06ec045feeea08993834e2f1d2d1e4ec52cdeff1 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:48 +0200 Subject: [PATCH 06/56] mmc: dm: get the IO-line and main voltage regulators from the dts Get a reference to the regulator devices from the dts and store them in the struct mmc for later use. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 24 ++++++++++++++---------- include/mmc.h | 4 ++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 8716ac7308..73a46121d8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1618,21 +1618,25 @@ __weak void board_mmc_power_init(void) static int mmc_power_init(struct mmc *mmc) { #if CONFIG_IS_ENABLED(DM_MMC) -#if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD) - struct udevice *vmmc_supply; +#if CONFIG_IS_ENABLED(DM_REGULATOR) int ret; ret = device_get_supply_regulator(mmc->dev, "vmmc-supply", - &vmmc_supply); - if (ret) { + &mmc->vmmc_supply); + if (ret) debug("%s: No vmmc supply\n", mmc->dev->name); - return 0; - } - ret = regulator_set_enable(vmmc_supply, true); - if (ret) { - puts("Error enabling VMMC supply\n"); - return ret; + ret = device_get_supply_regulator(mmc->dev, "vqmmc-supply", + &mmc->vqmmc_supply); + if (ret) + debug("%s: No vqmmc supply\n", mmc->dev->name); + + if (mmc->vmmc_supply) { + ret = regulator_set_enable(mmc->vmmc_supply, true); + if (ret) { + puts("Error enabling VMMC supply\n"); + return ret; + } } #endif #else /* !CONFIG_DM_MMC */ diff --git a/include/mmc.h b/include/mmc.h index 010ebe048c..188dc749dd 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -457,6 +457,10 @@ struct mmc { int ddr_mode; #if CONFIG_IS_ENABLED(DM_MMC) struct udevice *dev; /* Device for this MMC controller */ +#if CONFIG_IS_ENABLED(DM_REGULATOR) + struct udevice *vmmc_supply; /* Main voltage regulator (Vcc)*/ + struct udevice *vqmmc_supply; /* IO voltage regulator (Vccq)*/ +#endif #endif }; From 8ac8a2630459bacb2d9b6516d49bedad61ca63f8 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:49 +0200 Subject: [PATCH 07/56] mmc: split mmc_startup() No functionnal change here. The function is really big and can be split. The part related to bus configuration are put in 2 separate functions: one for MMC and one for SD. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 274 +++++++++++++++++++++++++--------------------- 1 file changed, 148 insertions(+), 126 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 73a46121d8..f1c190ee16 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1103,6 +1103,152 @@ static void mmc_set_bus_width(struct mmc *mmc, uint width) mmc_set_ios(mmc); } +static int sd_select_bus_freq_width(struct mmc *mmc) +{ + int err; + struct mmc_cmd cmd; + + err = sd_change_freq(mmc); + if (err) + return err; + + /* Restrict card's capabilities by what the host can do */ + mmc->card_caps &= mmc->cfg->host_caps; + + if (mmc->card_caps & MMC_MODE_4BIT) { + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 2; + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + mmc_set_bus_width(mmc, 4); + } + + err = sd_read_ssr(mmc); + if (err) + return err; + + if (mmc->card_caps & MMC_MODE_HS) + mmc->tran_speed = 50000000; + else + mmc->tran_speed = 25000000; + + return 0; +} + +static int mmc_select_bus_freq_width(struct mmc *mmc, const u8 *ext_csd) +{ + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + /* An array of possible bus widths in order of preference */ + static const unsigned int ext_csd_bits[] = { + EXT_CSD_DDR_BUS_WIDTH_8, + EXT_CSD_DDR_BUS_WIDTH_4, + EXT_CSD_BUS_WIDTH_8, + EXT_CSD_BUS_WIDTH_4, + EXT_CSD_BUS_WIDTH_1, + }; + /* An array to map CSD bus widths to host cap bits */ + static const unsigned int ext_to_hostcaps[] = { + [EXT_CSD_DDR_BUS_WIDTH_4] = + MMC_MODE_DDR_52MHz | MMC_MODE_4BIT, + [EXT_CSD_DDR_BUS_WIDTH_8] = + MMC_MODE_DDR_52MHz | MMC_MODE_8BIT, + [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, + [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, + }; + /* An array to map chosen bus width to an integer */ + static const unsigned int widths[] = { + 8, 4, 8, 4, 1, + }; + int err; + int idx; + + err = mmc_change_freq(mmc); + if (err) + return err; + + /* Restrict card's capabilities by what the host can do */ + mmc->card_caps &= mmc->cfg->host_caps; + + /* Only version 4 of MMC supports wider bus widths */ + if (mmc->version < MMC_VERSION_4) + return 0; + + for (idx = 0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { + unsigned int extw = ext_csd_bits[idx]; + unsigned int caps = ext_to_hostcaps[extw]; + /* + * If the bus width is still not changed, + * don't try to set the default again. + * Otherwise, recover from switch attempts + * by switching to 1-bit bus width. + */ + if (extw == EXT_CSD_BUS_WIDTH_1 && + mmc->bus_width == 1) { + err = 0; + break; + } + + /* + * Check to make sure the card and controller support + * these capabilities + */ + if ((mmc->card_caps & caps) != caps) + continue; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, extw); + + if (err) + continue; + + mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0; + mmc_set_bus_width(mmc, widths[idx]); + + err = mmc_send_ext_csd(mmc, test_csd); + + if (err) + continue; + + /* Only compare read only fields */ + if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] + == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && + ext_csd[EXT_CSD_HC_WP_GRP_SIZE] + == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && + ext_csd[EXT_CSD_REV] + == test_csd[EXT_CSD_REV] && + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && + memcmp(&ext_csd[EXT_CSD_SEC_CNT], + &test_csd[EXT_CSD_SEC_CNT], 4) == 0) + break; + + err = -EBADMSG; + } + + if (err) + return err; + + if (mmc->card_caps & MMC_MODE_HS) { + if (mmc->card_caps & MMC_MODE_HS_52MHz) + mmc->tran_speed = 52000000; + else + mmc->tran_speed = 26000000; + } + + return err; +} + static int mmc_startup(struct mmc *mmc) { int err, i; @@ -1110,7 +1256,6 @@ static int mmc_startup(struct mmc *mmc) u64 cmult, csize, capacity; struct mmc_cmd cmd; ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); - ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); bool has_parts = false; bool part_completed; struct blk_desc *bdesc; @@ -1415,136 +1560,13 @@ static int mmc_startup(struct mmc *mmc) return err; if (IS_SD(mmc)) - err = sd_change_freq(mmc); + err = sd_select_bus_freq_width(mmc); else - err = mmc_change_freq(mmc); + err = mmc_select_bus_freq_width(mmc, ext_csd); if (err) return err; - /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= mmc->cfg->host_caps; - - if (IS_SD(mmc)) { - if (mmc->card_caps & MMC_MODE_4BIT) { - cmd.cmdidx = MMC_CMD_APP_CMD; - cmd.resp_type = MMC_RSP_R1; - cmd.cmdarg = mmc->rca << 16; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; - - cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; - cmd.resp_type = MMC_RSP_R1; - cmd.cmdarg = 2; - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; - - mmc_set_bus_width(mmc, 4); - } - - err = sd_read_ssr(mmc); - if (err) - return err; - - if (mmc->card_caps & MMC_MODE_HS) - mmc->tran_speed = 50000000; - else - mmc->tran_speed = 25000000; - } else if (mmc->version >= MMC_VERSION_4) { - /* Only version 4 of MMC supports wider bus widths */ - int idx; - - /* An array of possible bus widths in order of preference */ - static unsigned ext_csd_bits[] = { - EXT_CSD_DDR_BUS_WIDTH_8, - EXT_CSD_DDR_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_8, - EXT_CSD_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_1, - }; - - /* An array to map CSD bus widths to host cap bits */ - static unsigned ext_to_hostcaps[] = { - [EXT_CSD_DDR_BUS_WIDTH_4] = - MMC_MODE_DDR_52MHz | MMC_MODE_4BIT, - [EXT_CSD_DDR_BUS_WIDTH_8] = - MMC_MODE_DDR_52MHz | MMC_MODE_8BIT, - [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, - [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, - }; - - /* An array to map chosen bus width to an integer */ - static unsigned widths[] = { - 8, 4, 8, 4, 1, - }; - - for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { - unsigned int extw = ext_csd_bits[idx]; - unsigned int caps = ext_to_hostcaps[extw]; - - /* - * If the bus width is still not changed, - * don't try to set the default again. - * Otherwise, recover from switch attempts - * by switching to 1-bit bus width. - */ - if (extw == EXT_CSD_BUS_WIDTH_1 && - mmc->bus_width == 1) { - err = 0; - break; - } - - /* - * Check to make sure the card and controller support - * these capabilities - */ - if ((mmc->card_caps & caps) != caps) - continue; - - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, extw); - - if (err) - continue; - - mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0; - mmc_set_bus_width(mmc, widths[idx]); - - err = mmc_send_ext_csd(mmc, test_csd); - - if (err) - continue; - - /* Only compare read only fields */ - if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] - == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && - ext_csd[EXT_CSD_HC_WP_GRP_SIZE] - == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && - ext_csd[EXT_CSD_REV] - == test_csd[EXT_CSD_REV] && - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] - == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && - memcmp(&ext_csd[EXT_CSD_SEC_CNT], - &test_csd[EXT_CSD_SEC_CNT], 4) == 0) - break; - else - err = -EBADMSG; - } - - if (err) - return err; - - if (mmc->card_caps & MMC_MODE_HS) { - if (mmc->card_caps & MMC_MODE_HS_52MHz) - mmc->tran_speed = 52000000; - else - mmc->tran_speed = 26000000; - } - } - mmc_set_clock(mmc, mmc->tran_speed); /* Fix the block length for DDR mode */ From c744b6f6dcf6054f4eb8ff2428edb6ba408c4a46 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:50 +0200 Subject: [PATCH 08/56] mmc: move the MMC startup for version above v4.0 in a separate function no functionnal change. This is only to further reduce the size o mmc_startup(). Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 318 ++++++++++++++++++++++++---------------------- 1 file changed, 167 insertions(+), 151 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index f1c190ee16..707b3da6c8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1249,15 +1249,176 @@ static int mmc_select_bus_freq_width(struct mmc *mmc, const u8 *ext_csd) return err; } +static int mmc_startup_v4(struct mmc *mmc, u8 *ext_csd) +{ + int err, i; + u64 capacity; + bool has_parts = false; + bool part_completed; + + if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4)) + return 0; + + /* check ext_csd version and capacity */ + err = mmc_send_ext_csd(mmc, ext_csd); + if (err) + return err; + if (ext_csd[EXT_CSD_REV] >= 2) { + /* + * According to the JEDEC Standard, the value of + * ext_csd's capacity is valid if the value is more + * than 2GB + */ + capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 + | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 + | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 + | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; + capacity *= MMC_MAX_BLOCK_LEN; + if ((capacity >> 20) > 2 * 1024) + mmc->capacity_user = capacity; + } + + switch (ext_csd[EXT_CSD_REV]) { + case 1: + mmc->version = MMC_VERSION_4_1; + break; + case 2: + mmc->version = MMC_VERSION_4_2; + break; + case 3: + mmc->version = MMC_VERSION_4_3; + break; + case 5: + mmc->version = MMC_VERSION_4_41; + break; + case 6: + mmc->version = MMC_VERSION_4_5; + break; + case 7: + mmc->version = MMC_VERSION_5_0; + break; + case 8: + mmc->version = MMC_VERSION_5_1; + break; + } + + /* The partition data may be non-zero but it is only + * effective if PARTITION_SETTING_COMPLETED is set in + * EXT_CSD, so ignore any data if this bit is not set, + * except for enabling the high-capacity group size + * definition (see below). + */ + part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & + EXT_CSD_PARTITION_SETTING_COMPLETED); + + /* store the partition info of emmc */ + mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || + ext_csd[EXT_CSD_BOOT_MULT]) + mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; + if (part_completed && + (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT)) + mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]; + + mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; + + mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + + for (i = 0; i < 4; i++) { + int idx = EXT_CSD_GP_SIZE_MULT + i * 3; + uint mult = (ext_csd[idx + 2] << 16) + + (ext_csd[idx + 1] << 8) + ext_csd[idx]; + if (mult) + has_parts = true; + if (!part_completed) + continue; + mmc->capacity_gp[i] = mult; + mmc->capacity_gp[i] *= + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + mmc->capacity_gp[i] <<= 19; + } + + if (part_completed) { + mmc->enh_user_size = + (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) + + (ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) + + ext_csd[EXT_CSD_ENH_SIZE_MULT]; + mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + mmc->enh_user_size <<= 19; + mmc->enh_user_start = + (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24) + + (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) + + (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) + + ext_csd[EXT_CSD_ENH_START_ADDR]; + if (mmc->high_capacity) + mmc->enh_user_start <<= 9; + } + + /* + * Host needs to enable ERASE_GRP_DEF bit if device is + * partitioned. This bit will be lost every time after a reset + * or power off. This will affect erase size. + */ + if (part_completed) + has_parts = true; + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && + (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) + has_parts = true; + if (has_parts) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GROUP_DEF, 1); + + if (err) + return err; + + ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; + } + + if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { + /* Read out group size from ext_csd */ + mmc->erase_grp_size = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; + /* + * if high capacity and partition setting completed + * SEC_COUNT is valid even if it is smaller than 2 GiB + * JEDEC Standard JESD84-B45, 6.2.4 + */ + if (mmc->high_capacity && part_completed) { + capacity = (ext_csd[EXT_CSD_SEC_CNT]) | + (ext_csd[EXT_CSD_SEC_CNT + 1] << 8) | + (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | + (ext_csd[EXT_CSD_SEC_CNT + 3] << 24); + capacity *= MMC_MAX_BLOCK_LEN; + mmc->capacity_user = capacity; + } + } else { + /* Calculate the group size from the csd value. */ + int erase_gsz, erase_gmul; + + erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; + erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; + mmc->erase_grp_size = (erase_gsz + 1) + * (erase_gmul + 1); + } + + mmc->hc_wp_grp_size = 1024 + * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + + mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; + + return 0; +} + static int mmc_startup(struct mmc *mmc) { int err, i; uint mult, freq; - u64 cmult, csize, capacity; + u64 cmult, csize; struct mmc_cmd cmd; ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); - bool has_parts = false; - bool part_completed; struct blk_desc *bdesc; #ifdef CONFIG_MMC_SPI_CRC_ON @@ -1405,155 +1566,10 @@ static int mmc_startup(struct mmc *mmc) */ mmc->erase_grp_size = 1; mmc->part_config = MMCPART_NOAVAILABLE; - if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { - /* check ext_csd version and capacity */ - err = mmc_send_ext_csd(mmc, ext_csd); - if (err) - return err; - if (ext_csd[EXT_CSD_REV] >= 2) { - /* - * According to the JEDEC Standard, the value of - * ext_csd's capacity is valid if the value is more - * than 2GB - */ - capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 - | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 - | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 - | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; - capacity *= MMC_MAX_BLOCK_LEN; - if ((capacity >> 20) > 2 * 1024) - mmc->capacity_user = capacity; - } - switch (ext_csd[EXT_CSD_REV]) { - case 1: - mmc->version = MMC_VERSION_4_1; - break; - case 2: - mmc->version = MMC_VERSION_4_2; - break; - case 3: - mmc->version = MMC_VERSION_4_3; - break; - case 5: - mmc->version = MMC_VERSION_4_41; - break; - case 6: - mmc->version = MMC_VERSION_4_5; - break; - case 7: - mmc->version = MMC_VERSION_5_0; - break; - case 8: - mmc->version = MMC_VERSION_5_1; - break; - } - - /* The partition data may be non-zero but it is only - * effective if PARTITION_SETTING_COMPLETED is set in - * EXT_CSD, so ignore any data if this bit is not set, - * except for enabling the high-capacity group size - * definition (see below). */ - part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & - EXT_CSD_PARTITION_SETTING_COMPLETED); - - /* store the partition info of emmc */ - mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; - if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || - ext_csd[EXT_CSD_BOOT_MULT]) - mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; - if (part_completed && - (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT)) - mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]; - - mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; - - mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; - - for (i = 0; i < 4; i++) { - int idx = EXT_CSD_GP_SIZE_MULT + i * 3; - uint mult = (ext_csd[idx + 2] << 16) + - (ext_csd[idx + 1] << 8) + ext_csd[idx]; - if (mult) - has_parts = true; - if (!part_completed) - continue; - mmc->capacity_gp[i] = mult; - mmc->capacity_gp[i] *= - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - mmc->capacity_gp[i] <<= 19; - } - - if (part_completed) { - mmc->enh_user_size = - (ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) + - (ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) + - ext_csd[EXT_CSD_ENH_SIZE_MULT]; - mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - mmc->enh_user_size <<= 19; - mmc->enh_user_start = - (ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) + - (ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) + - (ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) + - ext_csd[EXT_CSD_ENH_START_ADDR]; - if (mmc->high_capacity) - mmc->enh_user_start <<= 9; - } - - /* - * Host needs to enable ERASE_GRP_DEF bit if device is - * partitioned. This bit will be lost every time after a reset - * or power off. This will affect erase size. - */ - if (part_completed) - has_parts = true; - if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && - (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) - has_parts = true; - if (has_parts) { - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_ERASE_GROUP_DEF, 1); - - if (err) - return err; - else - ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; - } - - if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { - /* Read out group size from ext_csd */ - mmc->erase_grp_size = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; - /* - * if high capacity and partition setting completed - * SEC_COUNT is valid even if it is smaller than 2 GiB - * JEDEC Standard JESD84-B45, 6.2.4 - */ - if (mmc->high_capacity && part_completed) { - capacity = (ext_csd[EXT_CSD_SEC_CNT]) | - (ext_csd[EXT_CSD_SEC_CNT + 1] << 8) | - (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | - (ext_csd[EXT_CSD_SEC_CNT + 3] << 24); - capacity *= MMC_MAX_BLOCK_LEN; - mmc->capacity_user = capacity; - } - } else { - /* Calculate the group size from the csd value. */ - int erase_gsz, erase_gmul; - erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; - erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; - mmc->erase_grp_size = (erase_gsz + 1) - * (erase_gmul + 1); - } - - mmc->hc_wp_grp_size = 1024 - * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] - * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - - mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; - } + err = mmc_startup_v4(mmc, ext_csd); + if (err) + return err; err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart); if (err) From dfda9d88e5062620952956f7ed8e2a31ceffa6e6 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:51 +0200 Subject: [PATCH 09/56] mmc: make ext_csd part of struct mmc The ext csd is used for comparison many times. Keep a reference content of the ext csd in the struct mmc to avoid reading multiple times Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 22 +++++++++++++++++----- include/mmc.h | 1 + 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 707b3da6c8..694091d085 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1146,9 +1146,10 @@ static int sd_select_bus_freq_width(struct mmc *mmc) return 0; } -static int mmc_select_bus_freq_width(struct mmc *mmc, const u8 *ext_csd) +static int mmc_select_bus_freq_width(struct mmc *mmc) { ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + const u8 *ext_csd = mmc->ext_csd; /* An array of possible bus widths in order of preference */ static const unsigned int ext_csd_bits[] = { EXT_CSD_DDR_BUS_WIDTH_8, @@ -1184,6 +1185,11 @@ static int mmc_select_bus_freq_width(struct mmc *mmc, const u8 *ext_csd) if (mmc->version < MMC_VERSION_4) return 0; + if (!mmc->ext_csd) { + debug("No ext_csd found!\n"); /* this should enver happen */ + return -ENOTSUPP; + } + for (idx = 0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { unsigned int extw = ext_csd_bits[idx]; unsigned int caps = ext_to_hostcaps[extw]; @@ -1249,16 +1255,23 @@ static int mmc_select_bus_freq_width(struct mmc *mmc, const u8 *ext_csd) return err; } -static int mmc_startup_v4(struct mmc *mmc, u8 *ext_csd) +static int mmc_startup_v4(struct mmc *mmc) { int err, i; u64 capacity; bool has_parts = false; bool part_completed; + u8 *ext_csd; if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4)) return 0; + ext_csd = malloc_cache_aligned(MMC_MAX_BLOCK_LEN); + if (!ext_csd) + return -ENOMEM; + + mmc->ext_csd = ext_csd; + /* check ext_csd version and capacity */ err = mmc_send_ext_csd(mmc, ext_csd); if (err) @@ -1418,7 +1431,6 @@ static int mmc_startup(struct mmc *mmc) uint mult, freq; u64 cmult, csize; struct mmc_cmd cmd; - ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); struct blk_desc *bdesc; #ifdef CONFIG_MMC_SPI_CRC_ON @@ -1567,7 +1579,7 @@ static int mmc_startup(struct mmc *mmc) mmc->erase_grp_size = 1; mmc->part_config = MMCPART_NOAVAILABLE; - err = mmc_startup_v4(mmc, ext_csd); + err = mmc_startup_v4(mmc); if (err) return err; @@ -1578,7 +1590,7 @@ static int mmc_startup(struct mmc *mmc) if (IS_SD(mmc)) err = sd_select_bus_freq_width(mmc); else - err = mmc_select_bus_freq_width(mmc, ext_csd); + err = mmc_select_bus_freq_width(mmc); if (err) return err; diff --git a/include/mmc.h b/include/mmc.h index 188dc749dd..7d2b363acb 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -462,6 +462,7 @@ struct mmc { struct udevice *vqmmc_supply; /* IO voltage regulator (Vccq)*/ #endif #endif + u8 *ext_csd; }; struct mmc_hwpart_conf { From 7382e691ca528785d9d3ca8b74c4811b4f0bfe07 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:52 +0200 Subject: [PATCH 10/56] mmc: add a function to read and test the ext csd (mmc >= 4) This will be reused later in the selection of high speed and ddr modes. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 53 +++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 694091d085..c4067e210b 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1146,10 +1146,39 @@ static int sd_select_bus_freq_width(struct mmc *mmc) return 0; } +/* + * read the compare the part of ext csd that is constant. + * This can be used to check that the transfer is working + * as expected. + */ +static int mmc_read_and_compare_ext_csd(struct mmc *mmc) +{ + int err; + const u8 *ext_csd = mmc->ext_csd; + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + + err = mmc_send_ext_csd(mmc, test_csd); + if (err) + return err; + + /* Only compare read only fields */ + if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] + == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && + ext_csd[EXT_CSD_HC_WP_GRP_SIZE] + == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && + ext_csd[EXT_CSD_REV] + == test_csd[EXT_CSD_REV] && + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && + memcmp(&ext_csd[EXT_CSD_SEC_CNT], + &test_csd[EXT_CSD_SEC_CNT], 4) == 0) + return 0; + + return -EBADMSG; +} + static int mmc_select_bus_freq_width(struct mmc *mmc) { - ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); - const u8 *ext_csd = mmc->ext_csd; /* An array of possible bus widths in order of preference */ static const unsigned int ext_csd_bits[] = { EXT_CSD_DDR_BUS_WIDTH_8, @@ -1221,25 +1250,9 @@ static int mmc_select_bus_freq_width(struct mmc *mmc) mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0; mmc_set_bus_width(mmc, widths[idx]); - err = mmc_send_ext_csd(mmc, test_csd); - - if (err) - continue; - - /* Only compare read only fields */ - if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] - == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && - ext_csd[EXT_CSD_HC_WP_GRP_SIZE] - == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && - ext_csd[EXT_CSD_REV] - == test_csd[EXT_CSD_REV] && - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] - == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && - memcmp(&ext_csd[EXT_CSD_SEC_CNT], - &test_csd[EXT_CSD_SEC_CNT], 4) == 0) + err = mmc_read_and_compare_ext_csd(mmc); + if (!err) break; - - err = -EBADMSG; } if (err) From 35f9e196f9573af4091076c19aaa6d5afeb91338 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:53 +0200 Subject: [PATCH 11/56] mmc: introduce mmc modes no functionnal changes. In order to add the support for the high speed SD and MMC modes, it is useful to track this information. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/Kconfig | 14 ++++++++++++ drivers/mmc/mmc.c | 56 +++++++++++++++++++++++++++++++++++++++------ include/mmc.h | 35 +++++++++++++++++++++++----- 3 files changed, 92 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 8fbeaa740d..a8a689f4f5 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -42,6 +42,20 @@ config ARM_PL180_MMCI If you have an ARM(R) platform with a Multimedia Card slot, say Y or M here. +config MMC_VERBOSE + bool "Output more information about the MMC" + default y + help + Enable the output of more information about the card such as the + operating mode. + +config SPL_MMC_VERBOSE + bool "Output more information about the MMC in SPL" + default n + help + Enable the output of more information about the card such as the + operating mode. + config SPL_MMC_TINY bool "Tiny MMC framework in SPL" help diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index c4067e210b..4bfc6bf441 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -149,6 +149,39 @@ void mmc_trace_state(struct mmc *mmc, struct mmc_cmd *cmd) } #endif +#if CONFIG_IS_ENABLED(MMC_VERBOSE) || defined(DEBUG) +const char *mmc_mode_name(enum bus_mode mode) +{ + static const char *const names[] = { + [MMC_LEGACY] = "MMC legacy", + [SD_LEGACY] = "SD Legacy", + [MMC_HS] = "MMC High Speed (26MHz)", + [SD_HS] = "SD High Speed (50MHz)", + [UHS_SDR12] = "UHS SDR12 (25MHz)", + [UHS_SDR25] = "UHS SDR25 (50MHz)", + [UHS_SDR50] = "UHS SDR50 (100MHz)", + [UHS_SDR104] = "UHS SDR104 (208MHz)", + [UHS_DDR50] = "UHS DDR50 (50MHz)", + [MMC_HS_52] = "MMC High Speed (52MHz)", + [MMC_DDR_52] = "MMC DDR52 (52MHz)", + [MMC_HS_200] = "HS200 (200MHz)", + }; + + if (mode >= MMC_MODES_END) + return "Unknown mode"; + else + return names[mode]; +} +#endif + +static int mmc_select_mode(struct mmc *mmc, enum bus_mode mode) +{ + mmc->selected_mode = mode; + debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), + mmc->tran_speed / 1000000); + return 0; +} + #if !CONFIG_IS_ENABLED(DM_MMC) int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { @@ -1138,10 +1171,13 @@ static int sd_select_bus_freq_width(struct mmc *mmc) if (err) return err; - if (mmc->card_caps & MMC_MODE_HS) + if (mmc->card_caps & MMC_MODE_HS) { + mmc_select_mode(mmc, SD_HS); mmc->tran_speed = 50000000; - else + } else { + mmc_select_mode(mmc, SD_LEGACY); mmc->tran_speed = 25000000; + } return 0; } @@ -1258,11 +1294,15 @@ static int mmc_select_bus_freq_width(struct mmc *mmc) if (err) return err; - if (mmc->card_caps & MMC_MODE_HS) { - if (mmc->card_caps & MMC_MODE_HS_52MHz) - mmc->tran_speed = 52000000; + if (mmc->card_caps & MMC_MODE_HS_52MHz) { + if (mmc->ddr_mode) + mmc_select_mode(mmc, MMC_DDR_52); else - mmc->tran_speed = 26000000; + mmc_select_mode(mmc, MMC_HS_52); + mmc->tran_speed = 52000000; + } else if (mmc->card_caps & MMC_MODE_HS) { + mmc_select_mode(mmc, MMC_HS); + mmc->tran_speed = 26000000; } return err; @@ -1534,7 +1574,9 @@ static int mmc_startup(struct mmc *mmc) freq = fbase[(cmd.response[0] & 0x7)]; mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; - mmc->tran_speed = freq * mult; + mmc->legacy_speed = freq * mult; + mmc->tran_speed = mmc->legacy_speed; + mmc_select_mode(mmc, MMC_LEGACY); mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); diff --git a/include/mmc.h b/include/mmc.h index 7d2b363acb..76bd57ae42 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -52,12 +52,15 @@ #define MMC_VERSION_5_0 MAKE_MMC_VERSION(5, 0, 0) #define MMC_VERSION_5_1 MAKE_MMC_VERSION(5, 1, 0) -#define MMC_MODE_HS (1 << 0) -#define MMC_MODE_HS_52MHz (1 << 1) -#define MMC_MODE_4BIT (1 << 2) -#define MMC_MODE_8BIT (1 << 3) -#define MMC_MODE_SPI (1 << 4) -#define MMC_MODE_DDR_52MHz (1 << 5) +#define MMC_CAP(mode) (1 << mode) +#define MMC_MODE_HS (MMC_CAP(MMC_HS) | MMC_CAP(SD_HS)) +#define MMC_MODE_HS_52MHz MMC_CAP(MMC_HS_52) +#define MMC_MODE_DDR_52MHz MMC_CAP(MMC_DDR_52) + +#define MMC_MODE_8BIT BIT(30) +#define MMC_MODE_4BIT BIT(29) +#define MMC_MODE_SPI BIT(27) + #define SD_DATA_4BIT 0x00040000 @@ -406,6 +409,24 @@ struct sd_ssr { unsigned int erase_offset; /* In milliseconds */ }; +enum bus_mode { + MMC_LEGACY, + SD_LEGACY, + MMC_HS, + SD_HS, + UHS_SDR12, + UHS_SDR25, + UHS_SDR50, + UHS_SDR104, + UHS_DDR50, + MMC_HS_52, + MMC_DDR_52, + MMC_HS_200, + MMC_MODES_END +}; + +const char *mmc_mode_name(enum bus_mode mode); + /* * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device * with mmc_get_mmc_dev(). @@ -436,6 +457,7 @@ struct mmc { u8 wr_rel_set; u8 part_config; uint tran_speed; + uint legacy_speed; /* speed for the legacy mode provided by the card */ uint read_bl_len; uint write_bl_len; uint erase_grp_size; /* in 512-byte sectors */ @@ -463,6 +485,7 @@ struct mmc { #endif #endif u8 *ext_csd; + enum bus_mode selected_mode; }; struct mmc_hwpart_conf { From 4c9d2aaa7ea720ff8c304dd8621afa3ed085486e Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:54 +0200 Subject: [PATCH 12/56] mmc: Add a function to dump the mmc capabilities This adds a simple helper function to display information (bus width and mode) based on a capability mask. Useful for debug. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 24 ++++++++++++++++++++++++ include/mmc.h | 1 + 2 files changed, 25 insertions(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 4bfc6bf441..ae82914396 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1136,6 +1136,30 @@ static void mmc_set_bus_width(struct mmc *mmc, uint width) mmc_set_ios(mmc); } +#if CONFIG_IS_ENABLED(MMC_VERBOSE) || defined(DEBUG) +/* + * helper function to display the capabilities in a human + * friendly manner. The capabilities include bus width and + * supported modes. + */ +void mmc_dump_capabilities(const char *text, uint caps) +{ + enum bus_mode mode; + + printf("%s: widths [", text); + if (caps & MMC_MODE_8BIT) + printf("8, "); + if (caps & MMC_MODE_4BIT) + printf("4, "); + printf("1] modes ["); + + for (mode = MMC_LEGACY; mode < MMC_MODES_END; mode++) + if (MMC_CAP(mode) & caps) + printf("%s, ", mmc_mode_name(mode)); + printf("\b\b]\n"); +} +#endif + static int sd_select_bus_freq_width(struct mmc *mmc) { int err; diff --git a/include/mmc.h b/include/mmc.h index 76bd57ae42..dd83f14b6c 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -426,6 +426,7 @@ enum bus_mode { }; const char *mmc_mode_name(enum bus_mode mode); +void mmc_dump_capabilities(const char *text, uint caps); /* * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device From 05038576e0112a1547f1833bc17b70c34ce006bd Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:55 +0200 Subject: [PATCH 13/56] mmc: use mmc modes to select the correct bus speed Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index ae82914396..deb4786585 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -174,9 +174,34 @@ const char *mmc_mode_name(enum bus_mode mode) } #endif +static uint mmc_mode2freq(struct mmc *mmc, enum bus_mode mode) +{ + static const int freqs[] = { + [SD_LEGACY] = 25000000, + [MMC_HS] = 26000000, + [SD_HS] = 50000000, + [UHS_SDR12] = 25000000, + [UHS_SDR25] = 50000000, + [UHS_SDR50] = 100000000, + [UHS_SDR104] = 208000000, + [UHS_DDR50] = 50000000, + [MMC_HS_52] = 52000000, + [MMC_DDR_52] = 52000000, + [MMC_HS_200] = 200000000, + }; + + if (mode == MMC_LEGACY) + return mmc->legacy_speed; + else if (mode >= MMC_MODES_END) + return 0; + else + return freqs[mode]; +} + static int mmc_select_mode(struct mmc *mmc, enum bus_mode mode) { mmc->selected_mode = mode; + mmc->tran_speed = mmc_mode2freq(mmc, mode); debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), mmc->tran_speed / 1000000); return 0; @@ -1195,13 +1220,10 @@ static int sd_select_bus_freq_width(struct mmc *mmc) if (err) return err; - if (mmc->card_caps & MMC_MODE_HS) { + if (mmc->card_caps & MMC_MODE_HS) mmc_select_mode(mmc, SD_HS); - mmc->tran_speed = 50000000; - } else { + else mmc_select_mode(mmc, SD_LEGACY); - mmc->tran_speed = 25000000; - } return 0; } @@ -1323,11 +1345,8 @@ static int mmc_select_bus_freq_width(struct mmc *mmc) mmc_select_mode(mmc, MMC_DDR_52); else mmc_select_mode(mmc, MMC_HS_52); - mmc->tran_speed = 52000000; - } else if (mmc->card_caps & MMC_MODE_HS) { + } else if (mmc->card_caps & MMC_MODE_HS) mmc_select_mode(mmc, MMC_HS); - mmc->tran_speed = 26000000; - } return err; } @@ -1599,7 +1618,6 @@ static int mmc_startup(struct mmc *mmc) mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; mmc->legacy_speed = freq * mult; - mmc->tran_speed = mmc->legacy_speed; mmc_select_mode(mmc, MMC_LEGACY); mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); @@ -1674,7 +1692,6 @@ static int mmc_startup(struct mmc *mmc) if (err) return err; - mmc_set_clock(mmc, mmc->tran_speed); /* Fix the block length for DDR mode */ if (mmc->ddr_mode) { From 7a96ec74592847cd7bf5664cb72996500ebde92b Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:56 +0200 Subject: [PATCH 14/56] cmd: mmc: display the mode name and current bus speed in the mmc info Display the mode name when the user execute 'mmc info'. Also instead of displaying tran_speed, display the actual bus speed. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- cmd/mmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/mmc.c b/cmd/mmc.c index 5def4ea1a2..6d48ecbe0b 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -23,7 +23,8 @@ static void print_mmcinfo(struct mmc *mmc) (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); - printf("Tran Speed: %d\n", mmc->tran_speed); + printf("Bus Speed: %d\n", mmc->clock); + printf("Mode : %s\n", mmc_mode_name(mmc->selected_mode)); printf("Rd Block Len: %d\n", mmc->read_bl_len); printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC", From d0c221fe7336fc7d9ada57d96f4a8911a3aac041 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:57 +0200 Subject: [PATCH 15/56] mmc: refactor SD startup to make it easier to support new modes The SDcard startup process currently handles only 2 modes. To make it easier to add support for more modes, let's make the process more generic and use a list of the modes to try. The major functional change is that when a mode fails we try the next one. Not all modes are tried, only those supported by the card and the host. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 177 +++++++++++++++++++++++++++++++++------------- include/mmc.h | 1 + 2 files changed, 127 insertions(+), 51 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index deb4786585..3002b727c8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -237,7 +237,8 @@ int mmc_send_status(struct mmc *mmc, int timeout) (cmd.response[0] & MMC_STATUS_CURR_STATE) != MMC_STATE_PRG) break; - else if (cmd.response[0] & MMC_STATUS_MASK) { + + if (cmd.response[0] & MMC_STATUS_MASK) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Status Error: 0x%08X\n", cmd.response[0]); @@ -610,7 +611,7 @@ static int mmc_change_freq(struct mmc *mmc) char cardtype; int err; - mmc->card_caps = 0; + mmc->card_caps = MMC_MODE_1BIT; if (mmc_host_is_spi(mmc)) return 0; @@ -936,7 +937,7 @@ static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) } -static int sd_change_freq(struct mmc *mmc) +static int sd_get_capabilities(struct mmc *mmc) { int err; struct mmc_cmd cmd; @@ -945,7 +946,7 @@ static int sd_change_freq(struct mmc *mmc) struct mmc_data data; int timeout; - mmc->card_caps = 0; + mmc->card_caps = MMC_MODE_1BIT; if (mmc_host_is_spi(mmc)) return 0; @@ -1022,26 +1023,53 @@ retry_scr: } /* If high-speed isn't supported, we return */ - if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) - return 0; + if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED) + mmc->card_caps |= MMC_CAP(SD_HS); - /* - * If the host doesn't support SD_HIGHSPEED, do not switch card to - * HIGHSPEED mode even if the card support SD_HIGHSPPED. - * This can avoid furthur problem when the card runs in different - * mode between the host. - */ - if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) && - (mmc->cfg->host_caps & MMC_MODE_HS))) - return 0; + return 0; +} + +static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode) +{ + int err; + + ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); - if (err) return err; - if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) - mmc->card_caps |= MMC_MODE_HS; + if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) != 0x01000000) + return -ENOTSUPP; + + return 0; +} + +int sd_select_bus_width(struct mmc *mmc, int w) +{ + int err; + struct mmc_cmd cmd; + + if ((w != 4) && (w != 1)) + return -EINVAL; + + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; + cmd.resp_type = MMC_RSP_R1; + if (w == 4) + cmd.cmdarg = 2; + else if (w == 1) + cmd.cmdarg = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; return 0; } @@ -1133,6 +1161,18 @@ static const u8 multipliers[] = { 80, }; +static inline int bus_width(uint cap) +{ + if (cap == MMC_MODE_8BIT) + return 8; + if (cap == MMC_MODE_4BIT) + return 4; + if (cap == MMC_MODE_1BIT) + return 1; + printf("invalid bus witdh capability 0x%x\n", cap); + return 0; +} + #if !CONFIG_IS_ENABLED(DM_MMC) static void mmc_set_ios(struct mmc *mmc) { @@ -1176,8 +1216,9 @@ void mmc_dump_capabilities(const char *text, uint caps) printf("8, "); if (caps & MMC_MODE_4BIT) printf("4, "); - printf("1] modes ["); - + if (caps & MMC_MODE_1BIT) + printf("1, "); + printf("\b\b] modes ["); for (mode = MMC_LEGACY; mode < MMC_MODES_END; mode++) if (MMC_CAP(mode) & caps) printf("%s, ", mmc_mode_name(mode)); @@ -1185,47 +1226,81 @@ void mmc_dump_capabilities(const char *text, uint caps) } #endif -static int sd_select_bus_freq_width(struct mmc *mmc) +struct mode_width_tuning { + enum bus_mode mode; + uint widths; +}; + +static const struct mode_width_tuning sd_modes_by_pref[] = { + { + .mode = SD_HS, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = SD_LEGACY, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + } +}; + +#define for_each_sd_mode_by_pref(caps, mwt) \ + for (mwt = sd_modes_by_pref;\ + mwt < sd_modes_by_pref + ARRAY_SIZE(sd_modes_by_pref);\ + mwt++) \ + if (caps & MMC_CAP(mwt->mode)) + +static int sd_select_mode_and_width(struct mmc *mmc) { int err; - struct mmc_cmd cmd; + uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT}; + const struct mode_width_tuning *mwt; - err = sd_change_freq(mmc); + err = sd_get_capabilities(mmc); if (err) return err; - /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= mmc->cfg->host_caps; + mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT); - if (mmc->card_caps & MMC_MODE_4BIT) { - cmd.cmdidx = MMC_CMD_APP_CMD; - cmd.resp_type = MMC_RSP_R1; - cmd.cmdarg = mmc->rca << 16; + for_each_sd_mode_by_pref(mmc->card_caps, mwt) { + uint *w; - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; + for (w = widths; w < widths + ARRAY_SIZE(widths); w++) { + if (*w & mmc->card_caps & mwt->widths) { + debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(*w), + mmc_mode2freq(mmc, mwt->mode) / 1000000); - cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; - cmd.resp_type = MMC_RSP_R1; - cmd.cmdarg = 2; - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; + /* configure the bus width (card + host) */ + err = sd_select_bus_width(mmc, bus_width(*w)); + if (err) + goto error; + mmc_set_bus_width(mmc, bus_width(*w)); - mmc_set_bus_width(mmc, 4); + /* configure the bus mode (card) */ + err = sd_set_card_speed(mmc, mwt->mode); + if (err) + goto error; + + /* configure the bus mode (host) */ + mmc_select_mode(mmc, mwt->mode); + mmc_set_clock(mmc, mmc->tran_speed); + + err = sd_read_ssr(mmc); + if (!err) + return 0; + + printf("bad ssr\n"); + +error: + /* revert to a safer bus speed */ + mmc_select_mode(mmc, SD_LEGACY); + mmc_set_clock(mmc, mmc->tran_speed); + } + } } - err = sd_read_ssr(mmc); - if (err) - return err; - - if (mmc->card_caps & MMC_MODE_HS) - mmc_select_mode(mmc, SD_HS); - else - mmc_select_mode(mmc, SD_LEGACY); - - return 0; + printf("unable to select a mode\n"); + return -ENOTSUPP; } /* @@ -1290,7 +1365,7 @@ static int mmc_select_bus_freq_width(struct mmc *mmc) return err; /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= mmc->cfg->host_caps; + mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT); /* Only version 4 of MMC supports wider bus widths */ if (mmc->version < MMC_VERSION_4) @@ -1685,7 +1760,7 @@ static int mmc_startup(struct mmc *mmc) return err; if (IS_SD(mmc)) - err = sd_select_bus_freq_width(mmc); + err = sd_select_mode_and_width(mmc); else err = mmc_select_bus_freq_width(mmc); diff --git a/include/mmc.h b/include/mmc.h index dd83f14b6c..9fe6a87e88 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -59,6 +59,7 @@ #define MMC_MODE_8BIT BIT(30) #define MMC_MODE_4BIT BIT(29) +#define MMC_MODE_1BIT BIT(28) #define MMC_MODE_SPI BIT(27) From 3862b854740d296356a5cfd3146b270ad3501d97 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:29:58 +0200 Subject: [PATCH 16/56] mmc: refactor MMC startup to make it easier to support new modes The MMC startup process currently handles 4 modes. To make it easier to add support for more modes, let's make the process more generic and use a list of the modes to try. The major functional change is that when a mode fails we try the next one. Not all modes are tried, only those supported by the card and the host. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 249 ++++++++++++++++++++++++++++------------------ include/mmc.h | 11 ++ 2 files changed, 162 insertions(+), 98 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 3002b727c8..822b66b906 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -202,6 +202,7 @@ static int mmc_select_mode(struct mmc *mmc, enum bus_mode mode) { mmc->selected_mode = mode; mmc->tran_speed = mmc_mode2freq(mmc, mode); + mmc->ddr_mode = mmc_is_mode_ddr(mode); debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), mmc->tran_speed / 1000000); return 0; @@ -605,11 +606,47 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) } -static int mmc_change_freq(struct mmc *mmc) +static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode) { - ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); - char cardtype; int err; + int speed_bits; + + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + + switch (mode) { + case MMC_HS: + case MMC_HS_52: + case MMC_DDR_52: + speed_bits = EXT_CSD_TIMING_HS; + case MMC_LEGACY: + speed_bits = EXT_CSD_TIMING_LEGACY; + break; + default: + return -EINVAL; + } + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, + speed_bits); + if (err) + return err; + + if ((mode == MMC_HS) || (mode == MMC_HS_52)) { + /* Now check to see that it worked */ + err = mmc_send_ext_csd(mmc, test_csd); + if (err) + return err; + + /* No high-speed support */ + if (!test_csd[EXT_CSD_HS_TIMING]) + return -ENOTSUPP; + } + + return 0; +} + +static int mmc_get_capabilities(struct mmc *mmc) +{ + u8 *ext_csd = mmc->ext_csd; + char cardtype; mmc->card_caps = MMC_MODE_1BIT; @@ -620,38 +657,23 @@ static int mmc_change_freq(struct mmc *mmc) if (mmc->version < MMC_VERSION_4) return 0; + if (!ext_csd) { + printf("No ext_csd found!\n"); /* this should enver happen */ + return -ENOTSUPP; + } + mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; - err = mmc_send_ext_csd(mmc, ext_csd); - - if (err) - return err; - cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); - - if (err) - return err; - - /* Now check to see that it worked */ - err = mmc_send_ext_csd(mmc, ext_csd); - - if (err) - return err; - - /* No high-speed support */ - if (!ext_csd[EXT_CSD_HS_TIMING]) - return 0; - /* High Speed is set, there are two types: 52MHz and 26MHz */ if (cardtype & EXT_CSD_CARD_TYPE_52) { - if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V) + if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) mmc->card_caps |= MMC_MODE_DDR_52MHz; - mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; - } else { - mmc->card_caps |= MMC_MODE_HS; + mmc->card_caps |= MMC_MODE_HS_52MHz; } + if (cardtype & EXT_CSD_CARD_TYPE_26) + mmc->card_caps |= MMC_MODE_HS; return 0; } @@ -1334,33 +1356,60 @@ static int mmc_read_and_compare_ext_csd(struct mmc *mmc) return -EBADMSG; } -static int mmc_select_bus_freq_width(struct mmc *mmc) -{ - /* An array of possible bus widths in order of preference */ - static const unsigned int ext_csd_bits[] = { - EXT_CSD_DDR_BUS_WIDTH_8, - EXT_CSD_DDR_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_8, - EXT_CSD_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_1, - }; - /* An array to map CSD bus widths to host cap bits */ - static const unsigned int ext_to_hostcaps[] = { - [EXT_CSD_DDR_BUS_WIDTH_4] = - MMC_MODE_DDR_52MHz | MMC_MODE_4BIT, - [EXT_CSD_DDR_BUS_WIDTH_8] = - MMC_MODE_DDR_52MHz | MMC_MODE_8BIT, - [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, - [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, - }; - /* An array to map chosen bus width to an integer */ - static const unsigned int widths[] = { - 8, 4, 8, 4, 1, - }; - int err; - int idx; +static const struct mode_width_tuning mmc_modes_by_pref[] = { + { + .mode = MMC_HS_200, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, + }, + { + .mode = MMC_DDR_52, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, + }, + { + .mode = MMC_HS_52, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = MMC_HS, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = MMC_LEGACY, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, + } +}; - err = mmc_change_freq(mmc); +#define for_each_mmc_mode_by_pref(caps, mwt) \ + for (mwt = mmc_modes_by_pref;\ + mwt < mmc_modes_by_pref + ARRAY_SIZE(mmc_modes_by_pref);\ + mwt++) \ + if (caps & MMC_CAP(mwt->mode)) + +static const struct ext_csd_bus_width { + uint cap; + bool is_ddr; + uint ext_csd_bits; +} ext_csd_bus_width[] = { + {MMC_MODE_8BIT, true, EXT_CSD_DDR_BUS_WIDTH_8}, + {MMC_MODE_4BIT, true, EXT_CSD_DDR_BUS_WIDTH_4}, + {MMC_MODE_8BIT, false, EXT_CSD_BUS_WIDTH_8}, + {MMC_MODE_4BIT, false, EXT_CSD_BUS_WIDTH_4}, + {MMC_MODE_1BIT, false, EXT_CSD_BUS_WIDTH_1}, +}; + +#define for_each_supported_width(caps, ddr, ecbv) \ + for (ecbv = ext_csd_bus_width;\ + ecbv < ext_csd_bus_width + ARRAY_SIZE(ext_csd_bus_width);\ + ecbv++) \ + if ((ddr == ecbv->is_ddr) && (caps & ecbv->cap)) + +static int mmc_select_mode_and_width(struct mmc *mmc) +{ + int err; + const struct mode_width_tuning *mwt; + const struct ext_csd_bus_width *ecbw; + + err = mmc_get_capabilities(mmc); if (err) return err; @@ -1376,54 +1425,58 @@ static int mmc_select_bus_freq_width(struct mmc *mmc) return -ENOTSUPP; } - for (idx = 0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { - unsigned int extw = ext_csd_bits[idx]; - unsigned int caps = ext_to_hostcaps[extw]; - /* - * If the bus width is still not changed, - * don't try to set the default again. - * Otherwise, recover from switch attempts - * by switching to 1-bit bus width. - */ - if (extw == EXT_CSD_BUS_WIDTH_1 && - mmc->bus_width == 1) { - err = 0; - break; + for_each_mmc_mode_by_pref(mmc->card_caps, mwt) { + for_each_supported_width(mmc->card_caps & mwt->widths, + mmc_is_mode_ddr(mwt->mode), ecbw) { + debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(ecbw->cap), + mmc_mode2freq(mmc, mwt->mode) / 1000000); + /* configure the bus width (card + host) */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + ecbw->ext_csd_bits & ~EXT_CSD_DDR_FLAG); + if (err) + goto error; + mmc_set_bus_width(mmc, bus_width(ecbw->cap)); + + /* configure the bus speed (card) */ + err = mmc_set_card_speed(mmc, mwt->mode); + if (err) + goto error; + + /* + * configure the bus width AND the ddr mode (card) + * The host side will be taken care of in the next step + */ + if (ecbw->ext_csd_bits & EXT_CSD_DDR_FLAG) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + ecbw->ext_csd_bits); + if (err) + goto error; + } + + /* configure the bus mode (host) */ + mmc_select_mode(mmc, mwt->mode); + mmc_set_clock(mmc, mmc->tran_speed); + + /* do a transfer to check the configuration */ + err = mmc_read_and_compare_ext_csd(mmc); + if (!err) + return 0; +error: + /* if an error occured, revert to a safer bus mode */ + mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1); + mmc_select_mode(mmc, MMC_LEGACY); + mmc_set_bus_width(mmc, 1); } - - /* - * Check to make sure the card and controller support - * these capabilities - */ - if ((mmc->card_caps & caps) != caps) - continue; - - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, extw); - - if (err) - continue; - - mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0; - mmc_set_bus_width(mmc, widths[idx]); - - err = mmc_read_and_compare_ext_csd(mmc); - if (!err) - break; } - if (err) - return err; + printf("unable to select a mode\n"); - if (mmc->card_caps & MMC_MODE_HS_52MHz) { - if (mmc->ddr_mode) - mmc_select_mode(mmc, MMC_DDR_52); - else - mmc_select_mode(mmc, MMC_HS_52); - } else if (mmc->card_caps & MMC_MODE_HS) - mmc_select_mode(mmc, MMC_HS); - - return err; + return -ENOTSUPP; } static int mmc_startup_v4(struct mmc *mmc) @@ -1762,7 +1815,7 @@ static int mmc_startup(struct mmc *mmc) if (IS_SD(mmc)) err = sd_select_mode_and_width(mmc); else - err = mmc_select_bus_freq_width(mmc); + err = mmc_select_mode_and_width(mmc); if (err) return err; diff --git a/include/mmc.h b/include/mmc.h index 9fe6a87e88..988bf177d7 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -215,7 +215,10 @@ #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ +#define EXT_CSD_DDR_FLAG BIT(2) /* Flag for DDR mode */ +#define EXT_CSD_TIMING_LEGACY 0 /* no high speed */ +#define EXT_CSD_TIMING_HS 1 /* HS */ #define EXT_CSD_BOOT_ACK_ENABLE (1 << 6) #define EXT_CSD_BOOT_PARTITION_ENABLE (1 << 3) #define EXT_CSD_PARTITION_ACCESS_ENABLE (1 << 0) @@ -429,6 +432,14 @@ enum bus_mode { const char *mmc_mode_name(enum bus_mode mode); void mmc_dump_capabilities(const char *text, uint caps); +static inline bool mmc_is_mode_ddr(enum bus_mode mode) +{ + if ((mode == MMC_DDR_52) || (mode == UHS_DDR50)) + return true; + else + return false; +} + /* * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device * with mmc_get_mmc_dev(). From 2a4d212f7110712f51ae8af73abbdb0ae2af0814 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:29:59 +0200 Subject: [PATCH 17/56] mmc: make mmc_set_ios() return status set_ios callback has a return value of 'int' but the mmc_set_ios() function ignore this. Modify mmc_set_ios() and the callers of mmc_set_ios() to to return the error status. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 16 ++++++++++------ include/mmc.h | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 822b66b906..588cfb2b2e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1196,14 +1196,18 @@ static inline int bus_width(uint cap) } #if !CONFIG_IS_ENABLED(DM_MMC) -static void mmc_set_ios(struct mmc *mmc) +static int mmc_set_ios(struct mmc *mmc) { + int ret = 0; + if (mmc->cfg->ops->set_ios) - mmc->cfg->ops->set_ios(mmc); + ret = mmc->cfg->ops->set_ios(mmc); + + return ret; } #endif -void mmc_set_clock(struct mmc *mmc, uint clock) +int mmc_set_clock(struct mmc *mmc, uint clock) { if (clock > mmc->cfg->f_max) clock = mmc->cfg->f_max; @@ -1213,14 +1217,14 @@ void mmc_set_clock(struct mmc *mmc, uint clock) mmc->clock = clock; - mmc_set_ios(mmc); + return mmc_set_ios(mmc); } -static void mmc_set_bus_width(struct mmc *mmc, uint width) +static int mmc_set_bus_width(struct mmc *mmc, uint width) { mmc->bus_width = width; - mmc_set_ios(mmc); + return mmc_set_ios(mmc); } #if CONFIG_IS_ENABLED(MMC_VERBOSE) || defined(DEBUG) diff --git a/include/mmc.h b/include/mmc.h index 988bf177d7..3e57887af0 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -549,7 +549,7 @@ int mmc_unbind(struct udevice *dev); int mmc_initialize(bd_t *bis); int mmc_init(struct mmc *mmc); int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size); -void mmc_set_clock(struct mmc *mmc, uint clock); +int mmc_set_clock(struct mmc *mmc, uint clock); struct mmc *find_mmc_device(int dev_num); int mmc_set_dev(int dev_num); void print_mmc_devices(char separator); From aff5d3c83fb8c294b3c4b97c5b6386306b47f1a0 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:30:00 +0200 Subject: [PATCH 18/56] mmc: Enable signal voltage to be selected from mmc core Add a new function *mmc_set_signal_voltage* in mmc core which can be used during mmc initialization to select the signal voltage. Platform driver should use the set_ios callback function to select the signal voltage. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 16 ++++++++++++++++ include/mmc.h | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 588cfb2b2e..24b5e16a3b 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -30,6 +30,8 @@ static const unsigned int sd_au_size[] = { SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512, }; +static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage); + #if CONFIG_IS_ENABLED(MMC_TINY) static struct mmc mmc_static; struct mmc *find_mmc_device(int dev_num) @@ -1257,6 +1259,12 @@ struct mode_width_tuning { uint widths; }; +static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) +{ + mmc->signal_voltage = signal_voltage; + return mmc_set_ios(mmc); +} + static const struct mode_width_tuning sd_modes_by_pref[] = { { .mode = SD_HS, @@ -1964,6 +1972,14 @@ int mmc_start_init(struct mmc *mmc) return err; #endif mmc->ddr_mode = 0; + + /* First try to set 3.3V. If it fails set to 1.8V */ + err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_330); + if (err != 0) + err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); + if (err != 0) + printf("failed to set signal voltage\n"); + mmc_set_bus_width(mmc, 1); mmc_set_clock(mmc, 1); diff --git a/include/mmc.h b/include/mmc.h index 3e57887af0..e5249637c8 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -272,6 +272,13 @@ #define ENHNCD_SUPPORT (0x2) #define PART_ENH_ATTRIB (0x1f) +enum mmc_voltage { + MMC_SIGNAL_VOLTAGE_000 = 0, + MMC_SIGNAL_VOLTAGE_120, + MMC_SIGNAL_VOLTAGE_180, + MMC_SIGNAL_VOLTAGE_330 +}; + /* Maximum block size for MMC */ #define MMC_MAX_BLOCK_LEN 512 @@ -457,6 +464,7 @@ struct mmc { int high_capacity; uint bus_width; uint clock; + enum mmc_voltage signal_voltage; uint card_caps; uint ocr; uint dsr; From 318a7a576bc49aa8b4207e694d3fbd48c663d6ac Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:30:01 +0200 Subject: [PATCH 19/56] mmc: Add a new callback function to perform the 74 clocks cycle sequence Add a new callback function *send_init_stream* which start a sequence of at least 74 clock cycles. The mmc core uses *mmc_send_init_stream* in order to invoke the callback function. This will be used during power cycle where the specification requires such a sequence after power up. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc-uclass.c | 13 +++++++++++++ drivers/mmc/mmc.c | 6 ++++++ include/mmc.h | 10 ++++++++++ 3 files changed, 29 insertions(+) diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 5dda20cda5..9c6a8ba476 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -51,6 +51,19 @@ int mmc_set_ios(struct mmc *mmc) return dm_mmc_set_ios(mmc->dev); } +void dm_mmc_send_init_stream(struct udevice *dev) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (ops->send_init_stream) + ops->send_init_stream(dev); +} + +void mmc_send_init_stream(struct mmc *mmc) +{ + dm_mmc_send_init_stream(mmc->dev); +} + int dm_mmc_get_wp(struct udevice *dev) { struct dm_mmc_ops *ops = mmc_get_ops(dev); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 24b5e16a3b..1875b31ad1 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1198,6 +1198,10 @@ static inline int bus_width(uint cap) } #if !CONFIG_IS_ENABLED(DM_MMC) +static void mmc_send_init_stream(struct mmc *mmc) +{ +} + static int mmc_set_ios(struct mmc *mmc) { int ret = 0; @@ -1983,6 +1987,8 @@ int mmc_start_init(struct mmc *mmc) mmc_set_bus_width(mmc, 1); mmc_set_clock(mmc, 1); + mmc_send_init_stream(mmc); + /* Reset the Card */ err = mmc_go_idle(mmc); diff --git a/include/mmc.h b/include/mmc.h index e5249637c8..bd096aeb21 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -360,6 +360,14 @@ struct dm_mmc_ops { */ int (*set_ios)(struct udevice *dev); + /** + * send_init_stream() - send the initialization stream: 74 clock cycles + * This is used after power up before sending the first command + * + * @dev: Device to update + */ + void (*send_init_stream)(struct udevice *dev); + /** * get_cd() - See whether a card is present * @@ -382,11 +390,13 @@ struct dm_mmc_ops { int dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data); int dm_mmc_set_ios(struct udevice *dev); +void dm_mmc_send_init_stream(struct udevice *dev); int dm_mmc_get_cd(struct udevice *dev); int dm_mmc_get_wp(struct udevice *dev); /* Transition functions for compatibility */ int mmc_set_ios(struct mmc *mmc); +void mmc_send_init_stream(struct mmc *mmc); int mmc_getcd(struct mmc *mmc); int mmc_getwp(struct mmc *mmc); From fb7c3beb5178e5c81a00642e71a4b73427b52429 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:30:02 +0200 Subject: [PATCH 20/56] mmc: add power cyle support in mmc core mmc/sd specification requires vdd to be disabled for 1 ms and then enabled again during power cycle. Add a function in mmc core to perform power cycle and set the io signal to it's initial state. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 90 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1875b31ad1..0ffe7bf417 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -31,6 +31,7 @@ static const unsigned int sd_au_size[] = { }; static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage); +static int mmc_power_cycle(struct mmc *mmc); #if CONFIG_IS_ENABLED(MMC_TINY) static struct mmc mmc_static; @@ -1920,14 +1921,6 @@ static int mmc_power_init(struct mmc *mmc) &mmc->vqmmc_supply); if (ret) debug("%s: No vqmmc supply\n", mmc->dev->name); - - if (mmc->vmmc_supply) { - ret = regulator_set_enable(mmc->vmmc_supply, true); - if (ret) { - puts("Error enabling VMMC supply\n"); - return ret; - } - } #endif #else /* !CONFIG_DM_MMC */ /* @@ -1939,6 +1932,72 @@ static int mmc_power_init(struct mmc *mmc) return 0; } +/* + * put the host in the initial state: + * - turn on Vdd (card power supply) + * - configure the bus width and clock to minimal values + */ +static void mmc_set_initial_state(struct mmc *mmc) +{ + int err; + + /* First try to set 3.3V. If it fails set to 1.8V */ + err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_330); + if (err != 0) + err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); + if (err != 0) + printf("mmc: failed to set signal voltage\n"); + + mmc_select_mode(mmc, MMC_LEGACY); + mmc_set_bus_width(mmc, 1); + mmc_set_clock(mmc, 0); +} + +static int mmc_power_on(struct mmc *mmc) +{ +#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR) + if (mmc->vmmc_supply) { + int ret = regulator_set_enable(mmc->vmmc_supply, true); + + if (ret) { + puts("Error enabling VMMC supply\n"); + return ret; + } + } +#endif + return 0; +} + +static int mmc_power_off(struct mmc *mmc) +{ +#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR) + if (mmc->vmmc_supply) { + int ret = regulator_set_enable(mmc->vmmc_supply, false); + + if (ret) { + puts("Error disabling VMMC supply\n"); + return ret; + } + } +#endif + return 0; +} + +static int mmc_power_cycle(struct mmc *mmc) +{ + int ret; + + ret = mmc_power_off(mmc); + if (ret) + return ret; + /* + * SD spec recommends at least 1ms of delay. Let's wait for 2ms + * to be on the safer side. + */ + udelay(2000); + return mmc_power_on(mmc); +} + int mmc_start_init(struct mmc *mmc) { bool no_card; @@ -1967,6 +2026,10 @@ int mmc_start_init(struct mmc *mmc) if (err) return err; + err = mmc_power_on(mmc); + if (err) + return err; + #if CONFIG_IS_ENABLED(DM_MMC) /* The device has already been probed ready for use */ #else @@ -1977,16 +2040,7 @@ int mmc_start_init(struct mmc *mmc) #endif mmc->ddr_mode = 0; - /* First try to set 3.3V. If it fails set to 1.8V */ - err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_330); - if (err != 0) - err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); - if (err != 0) - printf("failed to set signal voltage\n"); - - mmc_set_bus_width(mmc, 1); - mmc_set_clock(mmc, 1); - + mmc_set_initial_state(mmc); mmc_send_init_stream(mmc); /* Reset the Card */ From 35f6782055c99410fbeae33ab28ea68de154360c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:30:03 +0200 Subject: [PATCH 21/56] mmc: add a new mmc parameter to disable mmc clock mmc clock has to be disabled in certain cases like during the voltage switch sequence. Modify mmc_set_clock function to take disable as an argument that signifies if the clock has to be enabled or disabled. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/fsl_esdhc.c | 2 +- drivers/mmc/mmc.c | 11 ++++++----- include/mmc.h | 12 +++++++++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 499d622c6d..cca57f8a79 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -665,7 +665,7 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) #endif /* Set the initial clock speed */ - mmc_set_clock(mmc, 400000); + mmc_set_clock(mmc, 400000, false); /* Disable the BRR and BWR bits in IRQSTAT */ esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0ffe7bf417..664b71affd 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1214,7 +1214,7 @@ static int mmc_set_ios(struct mmc *mmc) } #endif -int mmc_set_clock(struct mmc *mmc, uint clock) +int mmc_set_clock(struct mmc *mmc, uint clock, bool disable) { if (clock > mmc->cfg->f_max) clock = mmc->cfg->f_max; @@ -1223,6 +1223,7 @@ int mmc_set_clock(struct mmc *mmc, uint clock) clock = mmc->cfg->f_min; mmc->clock = clock; + mmc->clk_disable = disable; return mmc_set_ios(mmc); } @@ -1322,7 +1323,7 @@ static int sd_select_mode_and_width(struct mmc *mmc) /* configure the bus mode (host) */ mmc_select_mode(mmc, mwt->mode); - mmc_set_clock(mmc, mmc->tran_speed); + mmc_set_clock(mmc, mmc->tran_speed, false); err = sd_read_ssr(mmc); if (!err) @@ -1333,7 +1334,7 @@ static int sd_select_mode_and_width(struct mmc *mmc) error: /* revert to a safer bus speed */ mmc_select_mode(mmc, SD_LEGACY); - mmc_set_clock(mmc, mmc->tran_speed); + mmc_set_clock(mmc, mmc->tran_speed, false); } } } @@ -1476,7 +1477,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc) /* configure the bus mode (host) */ mmc_select_mode(mmc, mwt->mode); - mmc_set_clock(mmc, mmc->tran_speed); + mmc_set_clock(mmc, mmc->tran_speed, false); /* do a transfer to check the configuration */ err = mmc_read_and_compare_ext_csd(mmc); @@ -1950,7 +1951,7 @@ static void mmc_set_initial_state(struct mmc *mmc) mmc_select_mode(mmc, MMC_LEGACY); mmc_set_bus_width(mmc, 1); - mmc_set_clock(mmc, 0); + mmc_set_clock(mmc, 0, false); } static int mmc_power_on(struct mmc *mmc) diff --git a/include/mmc.h b/include/mmc.h index bd096aeb21..8d6e0f8fb0 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -472,6 +472,7 @@ struct mmc { void *priv; uint has_init; int high_capacity; + bool clk_disable; /* true if the clock can be turned off */ uint bus_width; uint clock; enum mmc_voltage signal_voltage; @@ -567,7 +568,16 @@ int mmc_unbind(struct udevice *dev); int mmc_initialize(bd_t *bis); int mmc_init(struct mmc *mmc); int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size); -int mmc_set_clock(struct mmc *mmc, uint clock); + +/** + * mmc_set_clock() - change the bus clock + * @mmc: MMC struct + * @clock: bus frequency in Hz + * @disable: flag indicating if the clock must on or off + * @return 0 if OK, -ve on error + */ +int mmc_set_clock(struct mmc *mmc, uint clock, bool disable); + struct mmc *find_mmc_device(int dev_num); int mmc_set_dev(int dev_num); void print_mmc_devices(char separator); From 2e7410d76ad11856d09284c18d262d0bb2a3da0c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:30:04 +0200 Subject: [PATCH 22/56] mmc: disable the mmc clock during power off There is no point in having the mmc clock enabled during power off. Disable the mmc clock. This is similar to how it's programmed in Linux Kernel. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Vignesh R Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 664b71affd..be68d8d930 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1971,6 +1971,7 @@ static int mmc_power_on(struct mmc *mmc) static int mmc_power_off(struct mmc *mmc) { + mmc_set_clock(mmc, 1, true); #if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR) if (mmc->vmmc_supply) { int ret = regulator_set_enable(mmc->vmmc_supply, false); From ec841209a7d250c1616d56744e1e79acab6c2921 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:30:05 +0200 Subject: [PATCH 23/56] mmc: Add a execute_tuning() callback to the mmc operations. Tuning is a mandatory step in the initialization of SDR104 and HS200 modes. This callback execute the tuning process. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc-uclass.c | 14 ++++++++++++++ drivers/mmc/mmc.c | 5 +++++ include/mmc.h | 11 +++++++++++ 3 files changed, 30 insertions(+) diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 9c6a8ba476..60cc0ac4cc 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -92,6 +92,20 @@ int mmc_getcd(struct mmc *mmc) return dm_mmc_get_cd(mmc->dev); } +int dm_mmc_execute_tuning(struct udevice *dev, uint opcode) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (!ops->execute_tuning) + return -ENOSYS; + return ops->execute_tuning(dev, opcode); +} + +int mmc_execute_tuning(struct mmc *mmc, uint opcode) +{ + return dm_mmc_execute_tuning(mmc->dev, opcode); +} + struct mmc *mmc_get_mmc_dev(struct udevice *dev) { struct mmc_uclass_priv *upriv; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index be68d8d930..b06e4bc917 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1199,6 +1199,11 @@ static inline int bus_width(uint cap) } #if !CONFIG_IS_ENABLED(DM_MMC) +static int mmc_execute_tuning(struct mmc *mmc, uint opcode) +{ + return -ENOTSUPP; +} + static void mmc_send_init_stream(struct mmc *mmc) { } diff --git a/include/mmc.h b/include/mmc.h index 8d6e0f8fb0..56fa869ea8 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -383,6 +383,15 @@ struct dm_mmc_ops { * @return 0 if write-enabled, 1 if write-protected, -ve on error */ int (*get_wp)(struct udevice *dev); + + /** + * execute_tuning() - Start the tuning process + * + * @dev: Device to start the tuning + * @opcode: Command opcode to send + * @return 0 if OK, -ve on error + */ + int (*execute_tuning)(struct udevice *dev, uint opcode); }; #define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops) @@ -393,12 +402,14 @@ int dm_mmc_set_ios(struct udevice *dev); void dm_mmc_send_init_stream(struct udevice *dev); int dm_mmc_get_cd(struct udevice *dev); int dm_mmc_get_wp(struct udevice *dev); +int dm_mmc_execute_tuning(struct udevice *dev, uint opcode); /* Transition functions for compatibility */ int mmc_set_ios(struct mmc *mmc); void mmc_send_init_stream(struct mmc *mmc); int mmc_getcd(struct mmc *mmc); int mmc_getwp(struct mmc *mmc); +int mmc_execute_tuning(struct mmc *mmc, uint opcode); #else struct mmc_ops { From 634d48494011fafc615ce613ab9aeeee77a9434d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:30:06 +0200 Subject: [PATCH 24/56] mmc: add HS200 support in MMC core Add HS200 to the list of supported modes and introduce tuning in the MMC startup process. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 22 ++++++++++++++++++++-- include/mmc.h | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b06e4bc917..d59cce611e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -621,6 +621,10 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode) case MMC_HS_52: case MMC_DDR_52: speed_bits = EXT_CSD_TIMING_HS; + break; + case MMC_HS_200: + speed_bits = EXT_CSD_TIMING_HS200; + break; case MMC_LEGACY: speed_bits = EXT_CSD_TIMING_LEGACY; break; @@ -667,9 +671,12 @@ static int mmc_get_capabilities(struct mmc *mmc) mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; - cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; + cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x3f; - /* High Speed is set, there are two types: 52MHz and 26MHz */ + if (cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V | + EXT_CSD_CARD_TYPE_HS200_1_8V)) { + mmc->card_caps |= MMC_MODE_HS200; + } if (cardtype & EXT_CSD_CARD_TYPE_52) { if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) mmc->card_caps |= MMC_MODE_DDR_52MHz; @@ -1268,6 +1275,7 @@ void mmc_dump_capabilities(const char *text, uint caps) struct mode_width_tuning { enum bus_mode mode; uint widths; + uint tuning; }; static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) @@ -1383,6 +1391,7 @@ static const struct mode_width_tuning mmc_modes_by_pref[] = { { .mode = MMC_HS_200, .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, + .tuning = MMC_CMD_SEND_TUNING_BLOCK_HS200 }, { .mode = MMC_DDR_52, @@ -1484,6 +1493,15 @@ static int mmc_select_mode_and_width(struct mmc *mmc) mmc_select_mode(mmc, mwt->mode); mmc_set_clock(mmc, mmc->tran_speed, false); + /* execute tuning if needed */ + if (mwt->tuning) { + err = mmc_execute_tuning(mmc, mwt->tuning); + if (err) { + debug("tuning failed\n"); + goto error; + } + } + /* do a transfer to check the configuration */ err = mmc_read_and_compare_ext_csd(mmc); if (!err) diff --git a/include/mmc.h b/include/mmc.h index 56fa869ea8..407fddf94c 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -56,6 +56,7 @@ #define MMC_MODE_HS (MMC_CAP(MMC_HS) | MMC_CAP(SD_HS)) #define MMC_MODE_HS_52MHz MMC_CAP(MMC_HS_52) #define MMC_MODE_DDR_52MHz MMC_CAP(MMC_DDR_52) +#define MMC_MODE_HS200 MMC_CAP(MMC_HS_200) #define MMC_MODE_8BIT BIT(30) #define MMC_MODE_4BIT BIT(29) @@ -86,6 +87,7 @@ #define MMC_CMD_SET_BLOCKLEN 16 #define MMC_CMD_READ_SINGLE_BLOCK 17 #define MMC_CMD_READ_MULTIPLE_BLOCK 18 +#define MMC_CMD_SEND_TUNING_BLOCK_HS200 21 #define MMC_CMD_SET_BLOCK_COUNT 23 #define MMC_CMD_WRITE_SINGLE_BLOCK 24 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 @@ -113,6 +115,13 @@ #define SD_CMD_APP_SEND_OP_COND 41 #define SD_CMD_APP_SEND_SCR 51 +static inline bool mmc_is_tuning_cmd(uint cmdidx) +{ + if (cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) + return true; + return false; +} + /* SCR definitions in different words */ #define SD_HIGHSPEED_BUSY 0x00020000 #define SD_HIGHSPEED_SUPPORTED 0x00020000 @@ -210,6 +219,13 @@ #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \ | EXT_CSD_CARD_TYPE_DDR_1_2V) +#define EXT_CSD_CARD_TYPE_HS200_1_8V BIT(4) /* Card can run at 200MHz */ + /* SDR mode @1.8V I/O */ +#define EXT_CSD_CARD_TYPE_HS200_1_2V BIT(5) /* Card can run at 200MHz */ + /* SDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ + EXT_CSD_CARD_TYPE_HS200_1_2V) + #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ @@ -219,6 +235,8 @@ #define EXT_CSD_TIMING_LEGACY 0 /* no high speed */ #define EXT_CSD_TIMING_HS 1 /* HS */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ + #define EXT_CSD_BOOT_ACK_ENABLE (1 << 6) #define EXT_CSD_BOOT_PARTITION_ENABLE (1 << 3) #define EXT_CSD_PARTITION_ACCESS_ENABLE (1 << 0) From c10b85d6c25f9ae0cdcf3ec9809a3063e0c3c2c9 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:30:07 +0200 Subject: [PATCH 25/56] mmc: Add support for UHS modes Add UHS modes to the list of supported modes, get the UHS capabilites of the SDcard and implement the procedure to switch the voltage (UHS modes use 1v8 IO lines) During the voltage switch procedure, DAT0 is used by the card to signal when it's ready. The optional card_busy() callback can be used to get this information from the host driver. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc-uclass.c | 14 ++++ drivers/mmc/mmc.c | 176 +++++++++++++++++++++++++++++++++++++-- include/mmc.h | 40 ++++++++- 3 files changed, 221 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 60cc0ac4cc..7856e0ad08 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -64,6 +64,20 @@ void mmc_send_init_stream(struct mmc *mmc) dm_mmc_send_init_stream(mmc->dev); } +int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (!ops->wait_dat0) + return -ENOSYS; + return ops->wait_dat0(dev, state, timeout); +} + +int mmc_wait_dat0(struct mmc *mmc, int state, int timeout) +{ + return dm_mmc_wait_dat0(mmc->dev, state, timeout); +} + int dm_mmc_get_wp(struct udevice *dev) { struct dm_mmc_ops *ops = mmc_get_ops(dev); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index d59cce611e..fe31540ba2 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -57,6 +57,12 @@ struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) #endif #if !CONFIG_IS_ENABLED(DM_MMC) + +static int mmc_wait_dat0(struct mmc *mmc, int state, int timeout) +{ + return -ENOSYS; +} + __weak int board_mmc_getwp(struct mmc *mmc) { return -1; @@ -402,7 +408,67 @@ static int mmc_go_idle(struct mmc *mmc) return 0; } -static int sd_send_op_cond(struct mmc *mmc) +static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage) +{ + struct mmc_cmd cmd; + int err = 0; + + /* + * Send CMD11 only if the request is to switch the card to + * 1.8V signalling. + */ + if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) + return mmc_set_signal_voltage(mmc, signal_voltage); + + cmd.cmdidx = SD_CMD_SWITCH_UHS18V; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + if (!mmc_host_is_spi(mmc) && (cmd.response[0] & MMC_STATUS_ERROR)) + return -EIO; + + /* + * The card should drive cmd and dat[0:3] low immediately + * after the response of cmd11, but wait 100 us to be sure + */ + err = mmc_wait_dat0(mmc, 0, 100); + if (err == -ENOSYS) + udelay(100); + else if (err) + return -ETIMEDOUT; + + /* + * During a signal voltage level switch, the clock must be gated + * for 5 ms according to the SD spec + */ + mmc_set_clock(mmc, mmc->clock, true); + + err = mmc_set_signal_voltage(mmc, signal_voltage); + if (err) + return err; + + /* Keep clock gated for at least 10 ms, though spec only says 5 ms */ + mdelay(10); + mmc_set_clock(mmc, mmc->clock, false); + + /* + * Failure to switch is indicated by the card holding + * dat[0:3] low. Wait for at least 1 ms according to spec + */ + err = mmc_wait_dat0(mmc, 1, 1000); + if (err == -ENOSYS) + udelay(1000); + else if (err) + return -ETIMEDOUT; + + return 0; +} + +static int sd_send_op_cond(struct mmc *mmc, bool uhs_en) { int timeout = 1000; int err; @@ -434,6 +500,9 @@ static int sd_send_op_cond(struct mmc *mmc) if (mmc->version == SD_VERSION_2) cmd.cmdarg |= OCR_HCS; + if (uhs_en) + cmd.cmdarg |= OCR_S18R; + err = mmc_send_cmd(mmc, &cmd, NULL); if (err) @@ -464,6 +533,13 @@ static int sd_send_op_cond(struct mmc *mmc) mmc->ocr = cmd.response[0]; + if (uhs_en && !(mmc_host_is_spi(mmc)) && (cmd.response[0] & 0x41000000) + == 0x41000000) { + err = mmc_switch_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); + if (err) + return err; + } + mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); mmc->rca = 0; @@ -977,6 +1053,7 @@ static int sd_get_capabilities(struct mmc *mmc) ALLOC_CACHE_ALIGN_BUFFER(__be32, switch_status, 16); struct mmc_data data; int timeout; + u32 sd3_bus_mode; mmc->card_caps = MMC_MODE_1BIT; @@ -1058,6 +1135,22 @@ retry_scr: if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED) mmc->card_caps |= MMC_CAP(SD_HS); + /* Version before 3.0 don't support UHS modes */ + if (mmc->version < SD_VERSION_3) + return 0; + + sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f; + if (sd3_bus_mode & SD_MODE_UHS_SDR104) + mmc->card_caps |= MMC_CAP(UHS_SDR104); + if (sd3_bus_mode & SD_MODE_UHS_SDR50) + mmc->card_caps |= MMC_CAP(UHS_SDR50); + if (sd3_bus_mode & SD_MODE_UHS_SDR25) + mmc->card_caps |= MMC_CAP(UHS_SDR25); + if (sd3_bus_mode & SD_MODE_UHS_SDR12) + mmc->card_caps |= MMC_CAP(UHS_SDR12); + if (sd3_bus_mode & SD_MODE_UHS_DDR50) + mmc->card_caps |= MMC_CAP(UHS_DDR50); + return 0; } @@ -1066,12 +1159,35 @@ static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode) int err; ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); + int speed; - err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); + switch (mode) { + case SD_LEGACY: + case UHS_SDR12: + speed = UHS_SDR12_BUS_SPEED; + break; + case SD_HS: + case UHS_SDR25: + speed = UHS_SDR25_BUS_SPEED; + break; + case UHS_SDR50: + speed = UHS_SDR50_BUS_SPEED; + break; + case UHS_DDR50: + speed = UHS_DDR50_BUS_SPEED; + break; + case UHS_SDR104: + speed = UHS_SDR104_BUS_SPEED; + break; + default: + return -EINVAL; + } + + err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, speed, (u8 *)switch_status); if (err) return err; - if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) != 0x01000000) + if ((__be32_to_cpu(switch_status[4]) >> 24) != speed) return -ENOTSUPP; return 0; @@ -1285,10 +1401,31 @@ static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) } static const struct mode_width_tuning sd_modes_by_pref[] = { + { + .mode = UHS_SDR104, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + .tuning = MMC_CMD_SEND_TUNING_BLOCK + }, + { + .mode = UHS_SDR50, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = UHS_DDR50, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = UHS_SDR25, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, { .mode = SD_HS, .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, }, + { + .mode = UHS_SDR12, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, { .mode = SD_LEGACY, .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, @@ -1306,18 +1443,24 @@ static int sd_select_mode_and_width(struct mmc *mmc) int err; uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT}; const struct mode_width_tuning *mwt; + bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false; + uint caps; + err = sd_get_capabilities(mmc); if (err) return err; /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT); + caps = mmc->card_caps & (mmc->cfg->host_caps | MMC_MODE_1BIT); - for_each_sd_mode_by_pref(mmc->card_caps, mwt) { + if (!uhs_en) + caps &= ~UHS_CAPS; + + for_each_sd_mode_by_pref(caps, mwt) { uint *w; for (w = widths; w < widths + ARRAY_SIZE(widths); w++) { - if (*w & mmc->card_caps & mwt->widths) { + if (*w & caps & mwt->widths) { debug("trying mode %s width %d (at %d MHz)\n", mmc_mode_name(mwt->mode), bus_width(*w), @@ -1338,6 +1481,16 @@ static int sd_select_mode_and_width(struct mmc *mmc) mmc_select_mode(mmc, mwt->mode); mmc_set_clock(mmc, mmc->tran_speed, false); + /* execute tuning if needed */ + if (mwt->tuning && !mmc_host_is_spi(mmc)) { + err = mmc_execute_tuning(mmc, + mwt->tuning); + if (err) { + debug("tuning failed\n"); + goto error; + } + } + err = sd_read_ssr(mmc); if (!err) return 0; @@ -2000,7 +2153,7 @@ static int mmc_power_off(struct mmc *mmc) int ret = regulator_set_enable(mmc->vmmc_supply, false); if (ret) { - puts("Error disabling VMMC supply\n"); + debug("Error disabling VMMC supply\n"); return ret; } } @@ -2026,6 +2179,7 @@ static int mmc_power_cycle(struct mmc *mmc) int mmc_start_init(struct mmc *mmc) { bool no_card; + bool uhs_en = supports_uhs(mmc->cfg->host_caps); int err; /* we pretend there's no card when init is NULL */ @@ -2065,6 +2219,7 @@ int mmc_start_init(struct mmc *mmc) #endif mmc->ddr_mode = 0; +retry: mmc_set_initial_state(mmc); mmc_send_init_stream(mmc); @@ -2081,7 +2236,12 @@ int mmc_start_init(struct mmc *mmc) err = mmc_send_if_cond(mmc); /* Now try to get the SD card's operating condition */ - err = sd_send_op_cond(mmc); + err = sd_send_op_cond(mmc, uhs_en); + if (err && uhs_en) { + uhs_en = false; + mmc_power_cycle(mmc); + goto retry; + } /* If the command timed out, we check for an MMC card */ if (err == -ETIMEDOUT) { diff --git a/include/mmc.h b/include/mmc.h index 407fddf94c..ba4a13e861 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -87,6 +87,7 @@ #define MMC_CMD_SET_BLOCKLEN 16 #define MMC_CMD_READ_SINGLE_BLOCK 17 #define MMC_CMD_READ_MULTIPLE_BLOCK 18 +#define MMC_CMD_SEND_TUNING_BLOCK 19 #define MMC_CMD_SEND_TUNING_BLOCK_HS200 21 #define MMC_CMD_SET_BLOCK_COUNT 23 #define MMC_CMD_WRITE_SINGLE_BLOCK 24 @@ -117,7 +118,8 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx) { - if (cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) + if ((cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) || + (cmdidx == MMC_CMD_SEND_TUNING_BLOCK)) return true; return false; } @@ -126,8 +128,22 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx) #define SD_HIGHSPEED_BUSY 0x00020000 #define SD_HIGHSPEED_SUPPORTED 0x00020000 +#define UHS_SDR12_BUS_SPEED 0 +#define HIGH_SPEED_BUS_SPEED 1 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 + +#define SD_MODE_UHS_SDR12 BIT(UHS_SDR12_BUS_SPEED) +#define SD_MODE_UHS_SDR25 BIT(UHS_SDR25_BUS_SPEED) +#define SD_MODE_UHS_SDR50 BIT(UHS_SDR50_BUS_SPEED) +#define SD_MODE_UHS_SDR104 BIT(UHS_SDR104_BUS_SPEED) +#define SD_MODE_UHS_DDR50 BIT(UHS_DDR50_BUS_SPEED) + #define OCR_BUSY 0x80000000 #define OCR_HCS 0x40000000 +#define OCR_S18R 0x1000000 #define OCR_VOLTAGE_MASK 0x007FFF80 #define OCR_ACCESS_MODE 0x60000000 @@ -410,6 +426,17 @@ struct dm_mmc_ops { * @return 0 if OK, -ve on error */ int (*execute_tuning)(struct udevice *dev, uint opcode); + + /** + * wait_dat0() - wait until dat0 is in the target state + * (CLK must be running during the wait) + * + * @dev: Device to check + * @state: target state + * @timeout: timeout in us + * @return 0 if dat0 is in the target state, -ve on error + */ + int (*wait_dat0)(struct udevice *dev, int state, int timeout); }; #define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops) @@ -421,6 +448,7 @@ void dm_mmc_send_init_stream(struct udevice *dev); int dm_mmc_get_cd(struct udevice *dev); int dm_mmc_get_wp(struct udevice *dev); int dm_mmc_execute_tuning(struct udevice *dev, uint opcode); +int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout); /* Transition functions for compatibility */ int mmc_set_ios(struct mmc *mmc); @@ -428,6 +456,7 @@ void mmc_send_init_stream(struct mmc *mmc); int mmc_getcd(struct mmc *mmc); int mmc_getwp(struct mmc *mmc); int mmc_execute_tuning(struct mmc *mmc, uint opcode); +int mmc_wait_dat0(struct mmc *mmc, int state, int timeout); #else struct mmc_ops { @@ -486,6 +515,15 @@ static inline bool mmc_is_mode_ddr(enum bus_mode mode) return false; } +#define UHS_CAPS (MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25) | \ + MMC_CAP(UHS_SDR50) | MMC_CAP(UHS_SDR104) | \ + MMC_CAP(UHS_DDR50)) + +static inline bool supports_uhs(uint caps) +{ + return (caps & UHS_CAPS) ? true : false; +} + /* * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device * with mmc_get_mmc_dev(). From 04a2ea248f58b3b6216d0cd0a6b8698df8b14355 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:30:08 +0200 Subject: [PATCH 26/56] mmc: disable UHS modes if Vcc cannot be switched on and off If a power cycle cannot be done on Vcc, it is safer not to try the UHS modes because we wouldn't be able to recover from an error occurring during the UHS initialization. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 19 ++++++++++++++++--- include/mmc.h | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index fe31540ba2..dbd95946ea 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1451,7 +1451,7 @@ static int sd_select_mode_and_width(struct mmc *mmc) if (err) return err; /* Restrict card's capabilities by what the host can do */ - caps = mmc->card_caps & (mmc->cfg->host_caps | MMC_MODE_1BIT); + caps = mmc->card_caps & (mmc->host_caps | MMC_MODE_1BIT); if (!uhs_en) caps &= ~UHS_CAPS; @@ -1599,7 +1599,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc) return err; /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT); + mmc->card_caps &= (mmc->host_caps | MMC_MODE_1BIT); /* Only version 4 of MMC supports wider bus widths */ if (mmc->version < MMC_VERSION_4) @@ -2182,6 +2182,8 @@ int mmc_start_init(struct mmc *mmc) bool uhs_en = supports_uhs(mmc->cfg->host_caps); int err; + mmc->host_caps = mmc->cfg->host_caps; + /* we pretend there's no card when init is NULL */ no_card = mmc_getcd(mmc) == 0; #if !CONFIG_IS_ENABLED(DM_MMC) @@ -2205,7 +2207,18 @@ int mmc_start_init(struct mmc *mmc) if (err) return err; - err = mmc_power_on(mmc); + err = mmc_power_cycle(mmc); + if (err) { + /* + * if power cycling is not supported, we should not try + * to use the UHS modes, because we wouldn't be able to + * recover from an error during the UHS initialization. + */ + debug("Unable to do a full power cycle. Disabling the UHS modes for safety\n"); + uhs_en = false; + mmc->host_caps &= ~UHS_CAPS; + err = mmc_power_on(mmc); + } if (err) return err; diff --git a/include/mmc.h b/include/mmc.h index ba4a13e861..59ea363ea2 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -544,6 +544,7 @@ struct mmc { uint clock; enum mmc_voltage signal_voltage; uint card_caps; + uint host_caps; uint ocr; uint dsr; uint dsr_imp; From 01298da31d92ecc46cf9130d8cff68bc51698197 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:30:09 +0200 Subject: [PATCH 27/56] mmc: Change mode when switching to a boot partition Boot partitions do not support HS200. Changing to a lower performance mode is required to access them. mmc_select_mode_and_width() and sd_select_mode_and_width() are modified to make it easier to call them outside of the initialization context. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 66 +++++++++++++++++++++++++++++++++++------------ include/mmc.h | 7 ++++- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index dbd95946ea..6278d1fa0c 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -32,6 +32,7 @@ static const unsigned int sd_au_size[] = { static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage); static int mmc_power_cycle(struct mmc *mmc); +static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps); #if CONFIG_IS_ENABLED(MMC_TINY) static struct mmc mmc_static; @@ -792,10 +793,38 @@ static int mmc_set_capacity(struct mmc *mmc, int part_num) return 0; } +static int mmc_boot_part_access_chk(struct mmc *mmc, unsigned int part_num) +{ + int forbidden = 0; + bool change = false; + + if (part_num & PART_ACCESS_MASK) + forbidden = MMC_CAP(MMC_HS_200); + + if (MMC_CAP(mmc->selected_mode) & forbidden) { + debug("selected mode (%s) is forbidden for part %d\n", + mmc_mode_name(mmc->selected_mode), part_num); + change = true; + } else if (mmc->selected_mode != mmc->best_mode) { + debug("selected mode is not optimal\n"); + change = true; + } + + if (change) + return mmc_select_mode_and_width(mmc, + mmc->card_caps & ~forbidden); + + return 0; +} + int mmc_switch_part(struct mmc *mmc, unsigned int part_num) { int ret; + ret = mmc_boot_part_access_chk(mmc, part_num); + if (ret) + return ret; + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, (mmc->part_config & ~PART_ACCESS_MASK) | (part_num & PART_ACCESS_MASK)); @@ -1438,7 +1467,7 @@ static const struct mode_width_tuning sd_modes_by_pref[] = { mwt++) \ if (caps & MMC_CAP(mwt->mode)) -static int sd_select_mode_and_width(struct mmc *mmc) +static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) { int err; uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT}; @@ -1447,11 +1476,8 @@ static int sd_select_mode_and_width(struct mmc *mmc) uint caps; - err = sd_get_capabilities(mmc); - if (err) - return err; /* Restrict card's capabilities by what the host can do */ - caps = mmc->card_caps & (mmc->host_caps | MMC_MODE_1BIT); + caps = card_caps & (mmc->host_caps | MMC_MODE_1BIT); if (!uhs_en) caps &= ~UHS_CAPS; @@ -1588,18 +1614,14 @@ static const struct ext_csd_bus_width { ecbv++) \ if ((ddr == ecbv->is_ddr) && (caps & ecbv->cap)) -static int mmc_select_mode_and_width(struct mmc *mmc) +static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) { int err; const struct mode_width_tuning *mwt; const struct ext_csd_bus_width *ecbw; - err = mmc_get_capabilities(mmc); - if (err) - return err; - /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= (mmc->host_caps | MMC_MODE_1BIT); + card_caps &= (mmc->host_caps | MMC_MODE_1BIT); /* Only version 4 of MMC supports wider bus widths */ if (mmc->version < MMC_VERSION_4) @@ -1610,8 +1632,10 @@ static int mmc_select_mode_and_width(struct mmc *mmc) return -ENOTSUPP; } - for_each_mmc_mode_by_pref(mmc->card_caps, mwt) { - for_each_supported_width(mmc->card_caps & mwt->widths, + mmc_set_clock(mmc, mmc->legacy_speed, false); + + for_each_mmc_mode_by_pref(card_caps, mwt) { + for_each_supported_width(card_caps & mwt->widths, mmc_is_mode_ddr(mwt->mode), ecbw) { debug("trying mode %s width %d (at %d MHz)\n", mmc_mode_name(mwt->mode), @@ -2006,14 +2030,22 @@ static int mmc_startup(struct mmc *mmc) if (err) return err; - if (IS_SD(mmc)) - err = sd_select_mode_and_width(mmc); - else - err = mmc_select_mode_and_width(mmc); + if (IS_SD(mmc)) { + err = sd_get_capabilities(mmc); + if (err) + return err; + err = sd_select_mode_and_width(mmc, mmc->card_caps); + } else { + err = mmc_get_capabilities(mmc); + if (err) + return err; + mmc_select_mode_and_width(mmc, mmc->card_caps); + } if (err) return err; + mmc->best_mode = mmc->selected_mode; /* Fix the block length for DDR mode */ if (mmc->ddr_mode) { diff --git a/include/mmc.h b/include/mmc.h index 59ea363ea2..a8901bffc7 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -585,7 +585,12 @@ struct mmc { #endif #endif u8 *ext_csd; - enum bus_mode selected_mode; + enum bus_mode selected_mode; /* mode currently used */ + enum bus_mode best_mode; /* best mode is the supported mode with the + * highest bandwidth. It may not always be the + * operating mode due to limitations when + * accessing the boot partitions + */ }; struct mmc_hwpart_conf { From 83dc42271f79202b746bc9614817b41a6911c88f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:30:10 +0200 Subject: [PATCH 28/56] mmc: Retry some MMC cmds on failure With certain SD cards like Kingston 8GB/16GB UHS card, it is seen that MMC_CMD_ALL_SEND_CID cmd fails on first attempt, but succeeds subsequently. Therefore, retry MMC_CMD_ALL_SEND_CID cmd a few time as done in Linux kernel. Similarly, it is seen that MMC_CMD_SET_BLOCKLEN may fail on first attempt, therefore retry this cmd a few times as done in kernel. To make it clear that those are optionnal workarounds, a new Kconfig option 'MMC_QUIRKS' is added (enabled by default). Signed-off-by: Vignesh R Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/Kconfig | 9 +++++++++ drivers/mmc/mmc.c | 41 +++++++++++++++++++++++++++++++++++++++-- include/mmc.h | 4 ++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index a8a689f4f5..006c9016a6 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -42,6 +42,15 @@ config ARM_PL180_MMCI If you have an ARM(R) platform with a Multimedia Card slot, say Y or M here. +config MMC_QUIRKS + bool "Enable quirks" + default y + help + Some cards and hosts may sometimes behave unexpectedly (quirks). + This option enable workarounds to handle those quirks. Some of them + are enabled by default, other may require additionnal flags or are + enabled by the host driver. + config MMC_VERBOSE bool "Output more information about the MMC" default y diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 6278d1fa0c..26589b8771 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -279,6 +279,7 @@ int mmc_send_status(struct mmc *mmc, int timeout) int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd; + int err; if (mmc->ddr_mode) return 0; @@ -287,7 +288,24 @@ int mmc_set_blocklen(struct mmc *mmc, int len) cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = len; - return mmc_send_cmd(mmc, &cmd, NULL); + err = mmc_send_cmd(mmc, &cmd, NULL); + +#ifdef CONFIG_MMC_QUIRKS + if (err && (mmc->quirks & MMC_QUIRK_RETRY_SET_BLOCKLEN)) { + int retries = 4; + /* + * It has been seen that SET_BLOCKLEN may fail on the first + * attempt, let's try a few more time + */ + do { + err = mmc_send_cmd(mmc, &cmd, NULL); + if (!err) + break; + } while (retries--); + } +#endif + + return err; } static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, @@ -1881,7 +1899,6 @@ static int mmc_startup(struct mmc *mmc) cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 1; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) return err; } @@ -1895,6 +1912,21 @@ static int mmc_startup(struct mmc *mmc) err = mmc_send_cmd(mmc, &cmd, NULL); +#ifdef CONFIG_MMC_QUIRKS + if (err && (mmc->quirks & MMC_QUIRK_RETRY_SEND_CID)) { + int retries = 4; + /* + * It has been seen that SEND_CID may fail on the first + * attempt, let's try a few more time + */ + do { + err = mmc_send_cmd(mmc, &cmd, NULL); + if (!err) + break; + } while (retries--); + } +#endif + if (err) return err; @@ -2239,6 +2271,11 @@ int mmc_start_init(struct mmc *mmc) if (err) return err; +#ifdef CONFIG_MMC_QUIRKS + mmc->quirks = MMC_QUIRK_RETRY_SET_BLOCKLEN | + MMC_QUIRK_RETRY_SEND_CID; +#endif + err = mmc_power_cycle(mmc); if (err) { /* diff --git a/include/mmc.h b/include/mmc.h index a8901bffc7..a9ebc880cb 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -306,6 +306,9 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx) #define ENHNCD_SUPPORT (0x2) #define PART_ENH_ATTRIB (0x1f) +#define MMC_QUIRK_RETRY_SEND_CID BIT(0) +#define MMC_QUIRK_RETRY_SET_BLOCKLEN BIT(1) + enum mmc_voltage { MMC_SIGNAL_VOLTAGE_000 = 0, MMC_SIGNAL_VOLTAGE_120, @@ -591,6 +594,7 @@ struct mmc { * operating mode due to limitations when * accessing the boot partitions */ + u32 quirks; }; struct mmc_hwpart_conf { From bc1e3272ff3437f7cfc5e8bf1d1f2767f6d78262 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:30:11 +0200 Subject: [PATCH 29/56] mmc: use the right voltage level for MMC DDR and HS200 modes HS200 only supports 1.2v and 1.8v signal voltages. DDR52 supports 3.3v/1.8v or 1.2v signal voltages. Select the lowest voltage available when using those modes. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 20 +++++++++++--- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 26589b8771..877ce1757d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -767,6 +767,7 @@ static int mmc_get_capabilities(struct mmc *mmc) mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x3f; + mmc->cardtype = cardtype; if (cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V | EXT_CSD_CARD_TYPE_HS200_1_8V)) { @@ -1441,10 +1442,30 @@ struct mode_width_tuning { uint tuning; }; +int mmc_voltage_to_mv(enum mmc_voltage voltage) +{ + switch (voltage) { + case MMC_SIGNAL_VOLTAGE_000: return 0; + case MMC_SIGNAL_VOLTAGE_330: return 3300; + case MMC_SIGNAL_VOLTAGE_180: return 1800; + case MMC_SIGNAL_VOLTAGE_120: return 1200; + } + return -EINVAL; +} + static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) { + int err; + + if (mmc->signal_voltage == signal_voltage) + return 0; + mmc->signal_voltage = signal_voltage; - return mmc_set_ios(mmc); + err = mmc_set_ios(mmc); + if (err) + debug("unable to set voltage (err %d)\n", err); + + return err; } static const struct mode_width_tuning sd_modes_by_pref[] = { @@ -1584,6 +1605,43 @@ static int mmc_read_and_compare_ext_csd(struct mmc *mmc) return -EBADMSG; } +static int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode, + uint32_t allowed_mask) +{ + u32 card_mask = 0; + + switch (mode) { + case MMC_HS_200: + if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_8V) + card_mask |= MMC_SIGNAL_VOLTAGE_180; + if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_2V) + card_mask |= MMC_SIGNAL_VOLTAGE_120; + break; + case MMC_DDR_52: + if (mmc->cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V) + card_mask |= MMC_SIGNAL_VOLTAGE_330 | + MMC_SIGNAL_VOLTAGE_180; + if (mmc->cardtype & EXT_CSD_CARD_TYPE_DDR_1_2V) + card_mask |= MMC_SIGNAL_VOLTAGE_120; + break; + default: + card_mask |= MMC_SIGNAL_VOLTAGE_330; + break; + } + + while (card_mask & allowed_mask) { + enum mmc_voltage best_match; + + best_match = 1 << (ffs(card_mask & allowed_mask) - 1); + if (!mmc_set_signal_voltage(mmc, best_match)) + return 0; + + allowed_mask &= ~best_match; + } + + return -ENOTSUPP; +} + static const struct mode_width_tuning mmc_modes_by_pref[] = { { .mode = MMC_HS_200, @@ -1655,10 +1713,17 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) for_each_mmc_mode_by_pref(card_caps, mwt) { for_each_supported_width(card_caps & mwt->widths, mmc_is_mode_ddr(mwt->mode), ecbw) { + enum mmc_voltage old_voltage; debug("trying mode %s width %d (at %d MHz)\n", mmc_mode_name(mwt->mode), bus_width(ecbw->cap), mmc_mode2freq(mmc, mwt->mode) / 1000000); + old_voltage = mmc->signal_voltage; + err = mmc_set_lowest_voltage(mmc, mwt->mode, + MMC_ALL_SIGNAL_VOLTAGE); + if (err) + continue; + /* configure the bus width (card + host) */ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, @@ -1702,6 +1767,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) if (!err) return 0; error: + mmc_set_signal_voltage(mmc, old_voltage); /* if an error occured, revert to a safer bus mode */ mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1); diff --git a/include/mmc.h b/include/mmc.h index a9ebc880cb..c11f69859e 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -311,11 +311,15 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx) enum mmc_voltage { MMC_SIGNAL_VOLTAGE_000 = 0, - MMC_SIGNAL_VOLTAGE_120, - MMC_SIGNAL_VOLTAGE_180, - MMC_SIGNAL_VOLTAGE_330 + MMC_SIGNAL_VOLTAGE_120 = 1, + MMC_SIGNAL_VOLTAGE_180 = 2, + MMC_SIGNAL_VOLTAGE_330 = 4, }; +#define MMC_ALL_SIGNAL_VOLTAGE (MMC_SIGNAL_VOLTAGE_120 |\ + MMC_SIGNAL_VOLTAGE_180 |\ + MMC_SIGNAL_VOLTAGE_330) + /* Maximum block size for MMC */ #define MMC_MAX_BLOCK_LEN 512 @@ -588,6 +592,8 @@ struct mmc { #endif #endif u8 *ext_csd; + u32 cardtype; /* cardtype read from the MMC */ + enum mmc_voltage current_voltage; enum bus_mode selected_mode; /* mode currently used */ enum bus_mode best_mode; /* best mode is the supported mode with the * highest bandwidth. It may not always be the @@ -646,6 +652,14 @@ int mmc_initialize(bd_t *bis); int mmc_init(struct mmc *mmc); int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size); +/** + * mmc_voltage_to_mv() - Convert a mmc_voltage in mV + * + * @voltage: The mmc_voltage to convert + * @return the value in mV if OK, -EINVAL on error (invalid mmc_voltage value) + */ +int mmc_voltage_to_mv(enum mmc_voltage voltage); + /** * mmc_set_clock() - change the bus clock * @mmc: MMC struct From 9815e3ba807ddf395631be35629498e55af02fa0 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 21 Sep 2017 16:30:12 +0200 Subject: [PATCH 30/56] mmc: add a library function to send tuning command HS200/SDR104 requires tuning command to be sent to the card. Add a simple function to send tuning command and to read and compare the received data with the tuning block pattern. This function can be used by platform driver to perform DLL tuning. This patch is similar to commit 996903de92f0 ("mmc: core: add core-level function for sending tuning commands") added in linux kernel. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 1 + 2 files changed, 69 insertions(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 877ce1757d..ab2483e9db 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -308,6 +308,74 @@ int mmc_set_blocklen(struct mmc *mmc, int len) return err; } +static const u8 tuning_blk_pattern_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + +static const u8 tuning_blk_pattern_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error) +{ + struct mmc_cmd cmd; + struct mmc_data data; + const u8 *tuning_block_pattern; + int size, err; + + if (mmc->bus_width == 8) { + tuning_block_pattern = tuning_blk_pattern_8bit; + size = sizeof(tuning_blk_pattern_8bit); + } else if (mmc->bus_width == 4) { + tuning_block_pattern = tuning_blk_pattern_4bit; + size = sizeof(tuning_blk_pattern_4bit); + } else { + return -EINVAL; + } + + ALLOC_CACHE_ALIGN_BUFFER(u8, data_buf, size); + + cmd.cmdidx = opcode; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1; + + data.dest = (void *)data_buf; + data.blocks = 1; + data.blocksize = size; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + if (err) + return err; + + if (memcmp(data_buf, tuning_block_pattern, size)) + return -EIO; + + return 0; +} + static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) { diff --git a/include/mmc.h b/include/mmc.h index c11f69859e..79be6b4bc3 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -650,6 +650,7 @@ void mmc_destroy(struct mmc *mmc); int mmc_unbind(struct udevice *dev); int mmc_initialize(bd_t *bis); int mmc_init(struct mmc *mmc); +int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error); int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size); /** From 9215ef5ed508b5de8b0c021a87b76f74561d1bad Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 21 Sep 2017 16:30:13 +0200 Subject: [PATCH 31/56] dm: mmc: Add a library function to parse generic dt binding Add a new function to parse host controller dt node and set mmc_config. This function can be used by mmc controller drivers to set the generic mmc_config. This function can be extended to set other UHS mode caps once UHS mode support is added. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc-uclass.c | 46 ++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 1 + 2 files changed, 47 insertions(+) diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 7856e0ad08..e30cde7f68 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -120,6 +120,52 @@ int mmc_execute_tuning(struct mmc *mmc, uint opcode) return dm_mmc_execute_tuning(mmc->dev, opcode); } +int mmc_of_parse(const void *fdt, int node, struct mmc_config *cfg) +{ + int val; + + val = fdtdec_get_int(fdt, node, "bus-width", 1); + + switch (val) { + case 0x8: + cfg->host_caps |= MMC_MODE_8BIT; + /* fall through */ + case 0x4: + cfg->host_caps |= MMC_MODE_4BIT; + /* fall through */ + case 0x1: + cfg->host_caps |= MMC_MODE_1BIT; + break; + default: + printf("error: %s invalid bus-width property %d\n", + fdt_get_name(fdt, node, NULL), val); + return -ENOENT; + } + + cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000); + + if (fdtdec_get_bool(fdt, node, "cap-sd-highspeed")) + cfg->host_caps |= MMC_CAP(SD_HS); + if (fdtdec_get_bool(fdt, node, "cap-mmc-highspeed")) + cfg->host_caps |= MMC_CAP(MMC_HS); + if (fdtdec_get_bool(fdt, node, "sd-uhs-sdr12")) + cfg->host_caps |= MMC_CAP(UHS_SDR12); + if (fdtdec_get_bool(fdt, node, "sd-uhs-sdr25")) + cfg->host_caps |= MMC_CAP(UHS_SDR25); + if (fdtdec_get_bool(fdt, node, "sd-uhs-sdr50")) + cfg->host_caps |= MMC_CAP(UHS_SDR50); + if (fdtdec_get_bool(fdt, node, "sd-uhs-sdr104")) + cfg->host_caps |= MMC_CAP(UHS_SDR104); + if (fdtdec_get_bool(fdt, node, "sd-uhs-ddr50")) + cfg->host_caps |= MMC_CAP(UHS_DDR50); + if (fdtdec_get_bool(fdt, node, "mmc-ddr-1_8v")) + cfg->host_caps |= MMC_CAP(MMC_DDR_52); + if (fdtdec_get_bool(fdt, node, "mmc-hs200-1_8v")) + cfg->host_caps |= MMC_CAP(MMC_HS_200); + + return 0; +} + struct mmc *mmc_get_mmc_dev(struct udevice *dev) { struct mmc_uclass_priv *upriv; diff --git a/include/mmc.h b/include/mmc.h index 79be6b4bc3..6230a32823 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -651,6 +651,7 @@ int mmc_unbind(struct udevice *dev); int mmc_initialize(bd_t *bis); int mmc_init(struct mmc *mmc); int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error); +int mmc_of_parse(const void *fdt, int node, struct mmc_config *cfg); int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size); /** From ef1614acf8f39637c798125deea06aaa83b5f986 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 27 Nov 2017 18:42:05 +0900 Subject: [PATCH 32/56] mmc: meson_gx_mmc: fix the complie error mmc_set_clock() is changed. This patch is for fixing complie error. Signed-off-by: Jaehoon Chung --- drivers/mmc/meson_gx_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c index 4652fab45e..a2cd5d3a44 100644 --- a/drivers/mmc/meson_gx_mmc.c +++ b/drivers/mmc/meson_gx_mmc.c @@ -250,7 +250,7 @@ static int meson_mmc_probe(struct udevice *dev) mmc->priv = pdata; upriv->mmc = mmc; - mmc_set_clock(mmc, cfg->f_min); + mmc_set_clock(mmc, cfg->f_min, false); /* reset all status bits */ meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS); From 52d241dfba7c3903a3a7d97fe6cb815c9ed379d6 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:43:54 +0100 Subject: [PATCH 33/56] mmc: dump card and host capabilities if debug is enabled This is a useful information while debugging the initialization process or performance issues. Also dump this information with the other mmc info if the verbose option is selected Signed-off-by: Jean-Jacques Hiblot --- cmd/mmc.c | 4 ++++ drivers/mmc/mmc.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/cmd/mmc.c b/cmd/mmc.c index 6d48ecbe0b..25795e0325 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -24,7 +24,11 @@ static void print_mmcinfo(struct mmc *mmc) (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); printf("Bus Speed: %d\n", mmc->clock); +#if CONFIG_IS_ENABLED(MMC_VERBOSE) printf("Mode : %s\n", mmc_mode_name(mmc->selected_mode)); + mmc_dump_capabilities("card capabilities", mmc->card_caps); + mmc_dump_capabilities("host capabilities", mmc->host_caps); +#endif printf("Rd Block Len: %d\n", mmc->read_bl_len); printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC", diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index ab2483e9db..9b5c982578 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1582,6 +1582,10 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false; uint caps; +#ifdef DEBUG + mmc_dump_capabilities("sd card", card_caps); + mmc_dump_capabilities("host", mmc->host_caps | MMC_MODE_1BIT); +#endif /* Restrict card's capabilities by what the host can do */ caps = card_caps & (mmc->host_caps | MMC_MODE_1BIT); @@ -1764,6 +1768,11 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) const struct mode_width_tuning *mwt; const struct ext_csd_bus_width *ecbw; +#ifdef DEBUG + mmc_dump_capabilities("mmc", card_caps); + mmc_dump_capabilities("host", mmc->host_caps | MMC_MODE_1BIT); +#endif + /* Restrict card's capabilities by what the host can do */ card_caps &= (mmc->host_caps | MMC_MODE_1BIT); From 7abff2c3b3cbf091f486605684f306d0d631d8d6 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:43:55 +0100 Subject: [PATCH 34/56] dm: mmc: update mmc_of_parse() * convert to livetree API * don't fail because of an invalid bus-width, instead default to 1-bit. * recognize 1.2v DDR and 1.2v HS200 flags Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc-uclass.c | 36 ++++++++++++++++++++---------------- include/mmc.h | 11 ++++++++++- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index e30cde7f68..bfda942d46 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -10,7 +10,6 @@ #include #include #include -#include #include "mmc_private.h" DECLARE_GLOBAL_DATA_PTR; @@ -120,11 +119,11 @@ int mmc_execute_tuning(struct mmc *mmc, uint opcode) return dm_mmc_execute_tuning(mmc->dev, opcode); } -int mmc_of_parse(const void *fdt, int node, struct mmc_config *cfg) +int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg) { int val; - val = fdtdec_get_int(fdt, node, "bus-width", 1); + val = dev_read_u32_default(dev, "bus-width", 1); switch (val) { case 0x8: @@ -137,30 +136,35 @@ int mmc_of_parse(const void *fdt, int node, struct mmc_config *cfg) cfg->host_caps |= MMC_MODE_1BIT; break; default: - printf("error: %s invalid bus-width property %d\n", - fdt_get_name(fdt, node, NULL), val); - return -ENOENT; + debug("warning: %s invalid bus-width property. using 1-bit\n", + dev_read_name(dev)); + cfg->host_caps |= MMC_MODE_1BIT; + break; } - cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000); + cfg->f_max = dev_read_u32_default(dev, "max-frequency", 52000000); - if (fdtdec_get_bool(fdt, node, "cap-sd-highspeed")) + if (dev_read_bool(dev, "cap-sd-highspeed")) cfg->host_caps |= MMC_CAP(SD_HS); - if (fdtdec_get_bool(fdt, node, "cap-mmc-highspeed")) + if (dev_read_bool(dev, "cap-mmc-highspeed")) cfg->host_caps |= MMC_CAP(MMC_HS); - if (fdtdec_get_bool(fdt, node, "sd-uhs-sdr12")) + if (dev_read_bool(dev, "sd-uhs-sdr12")) cfg->host_caps |= MMC_CAP(UHS_SDR12); - if (fdtdec_get_bool(fdt, node, "sd-uhs-sdr25")) + if (dev_read_bool(dev, "sd-uhs-sdr25")) cfg->host_caps |= MMC_CAP(UHS_SDR25); - if (fdtdec_get_bool(fdt, node, "sd-uhs-sdr50")) + if (dev_read_bool(dev, "sd-uhs-sdr50")) cfg->host_caps |= MMC_CAP(UHS_SDR50); - if (fdtdec_get_bool(fdt, node, "sd-uhs-sdr104")) + if (dev_read_bool(dev, "sd-uhs-sdr104")) cfg->host_caps |= MMC_CAP(UHS_SDR104); - if (fdtdec_get_bool(fdt, node, "sd-uhs-ddr50")) + if (dev_read_bool(dev, "sd-uhs-ddr50")) cfg->host_caps |= MMC_CAP(UHS_DDR50); - if (fdtdec_get_bool(fdt, node, "mmc-ddr-1_8v")) + if (dev_read_bool(dev, "mmc-ddr-1_8v")) cfg->host_caps |= MMC_CAP(MMC_DDR_52); - if (fdtdec_get_bool(fdt, node, "mmc-hs200-1_8v")) + if (dev_read_bool(dev, "mmc-ddr-1_2v")) + cfg->host_caps |= MMC_CAP(MMC_DDR_52); + if (dev_read_bool(dev, "mmc-hs200-1_8v")) + cfg->host_caps |= MMC_CAP(MMC_HS_200); + if (dev_read_bool(dev, "mmc-hs200-1_2v")) cfg->host_caps |= MMC_CAP(MMC_HS_200); return 0; diff --git a/include/mmc.h b/include/mmc.h index 6230a32823..e3f777f680 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -651,7 +651,16 @@ int mmc_unbind(struct udevice *dev); int mmc_initialize(bd_t *bis); int mmc_init(struct mmc *mmc); int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error); -int mmc_of_parse(const void *fdt, int node, struct mmc_config *cfg); + +/** + * mmc_of_parse() - Parse the device tree to get the capabilities of the host + * + * @dev: MMC device + * @cfg: MMC configuration + * @return 0 if OK, -ve on error + */ +int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg); + int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size); /** From 00e446fa0453d435815b6f34b694d696a025f648 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:43:56 +0100 Subject: [PATCH 35/56] mmc: Fixed a problem with old sd or mmc that do not support High speed As the legacy modes were not added to the list of supported modes, old cards that do not support other modes could not be used. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Lukasz Majewski Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 9b5c982578..c1f8851ab2 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -818,7 +818,7 @@ static int mmc_get_capabilities(struct mmc *mmc) u8 *ext_csd = mmc->ext_csd; char cardtype; - mmc->card_caps = MMC_MODE_1BIT; + mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(MMC_LEGACY); if (mmc_host_is_spi(mmc)) return 0; @@ -1171,7 +1171,7 @@ static int sd_get_capabilities(struct mmc *mmc) int timeout; u32 sd3_bus_mode; - mmc->card_caps = MMC_MODE_1BIT; + mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(SD_LEGACY); if (mmc_host_is_spi(mmc)) return 0; From 1da8eb598fb7fd631483a7db0a42d5de286f98a5 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:43:57 +0100 Subject: [PATCH 36/56] mmc: all hosts support 1-bit bus width and legacy timings Make sure that those basic capabilities are advertised by the host. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Lukasz Majewski Reviewed-by: Simon Glass --- drivers/mmc/mmc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index c1f8851ab2..0ebcc45516 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1584,11 +1584,11 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) #ifdef DEBUG mmc_dump_capabilities("sd card", card_caps); - mmc_dump_capabilities("host", mmc->host_caps | MMC_MODE_1BIT); + mmc_dump_capabilities("host", mmc->host_caps); #endif /* Restrict card's capabilities by what the host can do */ - caps = card_caps & (mmc->host_caps | MMC_MODE_1BIT); + caps = card_caps & mmc->host_caps; if (!uhs_en) caps &= ~UHS_CAPS; @@ -1770,11 +1770,11 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) #ifdef DEBUG mmc_dump_capabilities("mmc", card_caps); - mmc_dump_capabilities("host", mmc->host_caps | MMC_MODE_1BIT); + mmc_dump_capabilities("host", mmc->host_caps); #endif /* Restrict card's capabilities by what the host can do */ - card_caps &= (mmc->host_caps | MMC_MODE_1BIT); + card_caps &= mmc->host_caps; /* Only version 4 of MMC supports wider bus widths */ if (mmc->version < MMC_VERSION_4) @@ -2389,7 +2389,12 @@ int mmc_start_init(struct mmc *mmc) bool uhs_en = supports_uhs(mmc->cfg->host_caps); int err; - mmc->host_caps = mmc->cfg->host_caps; + /* + * all hosts are capable of 1 bit bus-width and able to use the legacy + * timings. + */ + mmc->host_caps = mmc->cfg->host_caps | MMC_CAP(SD_LEGACY) | + MMC_CAP(MMC_LEGACY) | MMC_MODE_1BIT; /* we pretend there's no card when init is NULL */ no_card = mmc_getcd(mmc) == 0; From 1de06b9fa5871b75012307ea736c8a43edf5a888 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:43:58 +0100 Subject: [PATCH 37/56] mmc: fix for old MMCs (below version 4) The ext_csd is allocated only for MMC above version 4. The compare will crash or fail for older MMCs. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0ebcc45516..2a58031c19 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1657,6 +1657,9 @@ static int mmc_read_and_compare_ext_csd(struct mmc *mmc) const u8 *ext_csd = mmc->ext_csd; ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + if (mmc->version < MMC_VERSION_4) + return 0; + err = mmc_send_ext_csd(mmc, test_csd); if (err) return err; From f7d5dffc65c1fdb1d1e4a69606a428ef62cfcdf5 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:43:59 +0100 Subject: [PATCH 38/56] mmc: don't use malloc_cache_aligned() Not using this function reduces the size of the binary. It's replaces by a standard malloc() and the alignment requirement is handled by an intermediate buffer on the stack. Also make sure that the allocated buffer is freed in case of error. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 2a58031c19..13979a5e81 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1867,21 +1867,23 @@ static int mmc_startup_v4(struct mmc *mmc) u64 capacity; bool has_parts = false; bool part_completed; - u8 *ext_csd; + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4)) return 0; - ext_csd = malloc_cache_aligned(MMC_MAX_BLOCK_LEN); - if (!ext_csd) - return -ENOMEM; - - mmc->ext_csd = ext_csd; - /* check ext_csd version and capacity */ err = mmc_send_ext_csd(mmc, ext_csd); if (err) - return err; + goto error; + + /* store the ext csd for future reference */ + if (!mmc->ext_csd) + mmc->ext_csd = malloc(MMC_MAX_BLOCK_LEN); + if (!mmc->ext_csd) + return -ENOMEM; + memcpy(mmc->ext_csd, ext_csd, MMC_MAX_BLOCK_LEN); + if (ext_csd[EXT_CSD_REV] >= 2) { /* * According to the JEDEC Standard, the value of @@ -1990,7 +1992,7 @@ static int mmc_startup_v4(struct mmc *mmc) EXT_CSD_ERASE_GROUP_DEF, 1); if (err) - return err; + goto error; ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; } @@ -2029,6 +2031,12 @@ static int mmc_startup_v4(struct mmc *mmc) mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; return 0; +error: + if (mmc->ext_csd) { + free(mmc->ext_csd); + mmc->ext_csd = NULL; + } + return err; } static int mmc_startup(struct mmc *mmc) From d8e3d420891a5563fb307871581092c57aa1191e Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:44:00 +0100 Subject: [PATCH 39/56] mmc: convert most of printf() to pr_err() and pr_warn() This allows to compile out the log message by tweaking the LOGLEVEL. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Tom Rini --- drivers/mmc/Kconfig | 7 ------- drivers/mmc/mmc.c | 44 ++++++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 006c9016a6..c34ab219e8 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -58,13 +58,6 @@ config MMC_VERBOSE Enable the output of more information about the card such as the operating mode. -config SPL_MMC_VERBOSE - bool "Output more information about the MMC in SPL" - default n - help - Enable the output of more information about the card such as the - operating mode. - config SPL_MMC_TINY bool "Tiny MMC framework in SPL" help diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 13979a5e81..3fb82d9002 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -251,8 +251,8 @@ int mmc_send_status(struct mmc *mmc, int timeout) if (cmd.response[0] & MMC_STATUS_MASK) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("Status Error: 0x%08X\n", - cmd.response[0]); + pr_err("Status Error: 0x%08X\n", + cmd.response[0]); #endif return -ECOMM; } @@ -268,7 +268,7 @@ int mmc_send_status(struct mmc *mmc, int timeout) mmc_trace_state(mmc, &cmd); if (timeout <= 0) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("Timeout waiting card ready\n"); + pr_err("Timeout waiting card ready\n"); #endif return -ETIMEDOUT; } @@ -408,7 +408,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, cmd.resp_type = MMC_RSP_R1b; if (mmc_send_cmd(mmc, &cmd, NULL)) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("mmc fail to send stop cmd\n"); + pr_err("mmc fail to send stop cmd\n"); #endif return 0; } @@ -448,8 +448,8 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, if ((start + blkcnt) > block_dev->lba) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", - start + blkcnt, block_dev->lba); + pr_err("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", + start + blkcnt, block_dev->lba); #endif return 0; } @@ -828,7 +828,7 @@ static int mmc_get_capabilities(struct mmc *mmc) return 0; if (!ext_csd) { - printf("No ext_csd found!\n"); /* this should enver happen */ + pr_err("No ext_csd found!\n"); /* this should enver happen */ return -ENOTSUPP; } @@ -946,17 +946,17 @@ int mmc_hwpart_config(struct mmc *mmc, return -EINVAL; if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) { - printf("eMMC >= 4.4 required for enhanced user data area\n"); + pr_err("eMMC >= 4.4 required for enhanced user data area\n"); return -EMEDIUMTYPE; } if (!(mmc->part_support & PART_SUPPORT)) { - printf("Card does not support partitioning\n"); + pr_err("Card does not support partitioning\n"); return -EMEDIUMTYPE; } if (!mmc->hc_wp_grp_size) { - printf("Card does not define HC WP group size\n"); + pr_err("Card does not define HC WP group size\n"); return -EMEDIUMTYPE; } @@ -964,7 +964,7 @@ int mmc_hwpart_config(struct mmc *mmc, if (conf->user.enh_size) { if (conf->user.enh_size % mmc->hc_wp_grp_size || conf->user.enh_start % mmc->hc_wp_grp_size) { - printf("User data enhanced area not HC WP group " + pr_err("User data enhanced area not HC WP group " "size aligned\n"); return -EINVAL; } @@ -983,7 +983,7 @@ int mmc_hwpart_config(struct mmc *mmc, for (pidx = 0; pidx < 4; pidx++) { if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) { - printf("GP%i partition not HC WP group size " + pr_err("GP%i partition not HC WP group size " "aligned\n", pidx+1); return -EINVAL; } @@ -995,7 +995,7 @@ int mmc_hwpart_config(struct mmc *mmc, } if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) { - printf("Card does not support enhanced attribute\n"); + pr_err("Card does not support enhanced attribute\n"); return -EMEDIUMTYPE; } @@ -1008,7 +1008,7 @@ int mmc_hwpart_config(struct mmc *mmc, (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) + ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT]; if (tot_enh_size_mult > max_enh_size_mult) { - printf("Total enhanced size exceeds maximum (%u > %u)\n", + pr_err("Total enhanced size exceeds maximum (%u > %u)\n", tot_enh_size_mult, max_enh_size_mult); return -EMEDIUMTYPE; } @@ -1042,7 +1042,7 @@ int mmc_hwpart_config(struct mmc *mmc, if (ext_csd[EXT_CSD_PARTITION_SETTING] & EXT_CSD_PARTITION_SETTING_COMPLETED) { - printf("Card already partitioned\n"); + pr_err("Card already partitioned\n"); return -EPERM; } @@ -1433,7 +1433,7 @@ static inline int bus_width(uint cap) return 4; if (cap == MMC_MODE_1BIT) return 1; - printf("invalid bus witdh capability 0x%x\n", cap); + pr_warn("invalid bus witdh capability 0x%x\n", cap); return 0; } @@ -1632,7 +1632,7 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) if (!err) return 0; - printf("bad ssr\n"); + pr_warn("bad ssr\n"); error: /* revert to a safer bus speed */ @@ -1856,7 +1856,7 @@ error: } } - printf("unable to select a mode\n"); + pr_err("unable to select a mode\n"); return -ENOTSUPP; } @@ -2188,7 +2188,7 @@ static int mmc_startup(struct mmc *mmc) cmd.cmdarg = (mmc->dsr & 0xffff) << 16; cmd.resp_type = MMC_RSP_NONE; if (mmc_send_cmd(mmc, &cmd, NULL)) - printf("MMC: SET_DSR failed\n"); + pr_warn("MMC: SET_DSR failed\n"); } /* Select the card, and put it into Transfer Mode */ @@ -2341,7 +2341,7 @@ static void mmc_set_initial_state(struct mmc *mmc) if (err != 0) err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); if (err != 0) - printf("mmc: failed to set signal voltage\n"); + pr_warn("mmc: failed to set signal voltage\n"); mmc_select_mode(mmc, MMC_LEGACY); mmc_set_bus_width(mmc, 1); @@ -2490,7 +2490,7 @@ retry: if (err) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("Card did not respond to voltage select!\n"); + pr_err("Card did not respond to voltage select!\n"); #endif return -EOPNOTSUPP; } @@ -2596,7 +2596,7 @@ static int mmc_probe(bd_t *bis) uclass_foreach_dev(dev, uc) { ret = device_probe(dev); if (ret) - printf("%s - probe failed: %d\n", dev->name, ret); + pr_err("%s - probe failed: %d\n", dev->name, ret); } return 0; From f99c2efe5672d7355a632dcae3551ffadea1163a Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:44:01 +0100 Subject: [PATCH 40/56] mmc: make UHS and HS200 optional Supporting USH and HS200 increases the code size as it brings in IO voltage control, tuning and fatter data structures. Use Kconfig configuration to select which of those features should be built in. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/Kconfig | 46 +++++++++++++++++++++++++++++ drivers/mmc/mmc-uclass.c | 4 +++ drivers/mmc/mmc.c | 63 +++++++++++++++++++++++++++++++++++++++- include/mmc.h | 27 ++++++++++++++--- 4 files changed, 135 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c34ab219e8..2fcdaddf22 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -51,6 +51,52 @@ config MMC_QUIRKS are enabled by default, other may require additionnal flags or are enabled by the host driver. +config MMC_IO_VOLTAGE + bool "Support IO voltage configuration" + help + IO voltage configuration allows selecting the voltage level of the IO + lines (not the level of main supply). This is required for UHS + support. For eMMC this not mandatory, but not enabling this option may + prevent the driver of using the faster modes. + +config SPL_MMC_IO_VOLTAGE + bool "Support IO voltage configuration in SPL" + default n + help + IO voltage configuration allows selecting the voltage level of the IO + lines (not the level of main supply). This is required for UHS + support. For eMMC this not mandatory, but not enabling this option may + prevent the driver of using the faster modes. + +config MMC_UHS_SUPPORT + bool "enable UHS support" + depends on MMC_IO_VOLTAGE + help + The Ultra High Speed (UHS) bus is available on some SDHC and SDXC + cards. The IO voltage must be switchable from 3.3v to 1.8v. The bus + frequency can go up to 208MHz (SDR104) + +config SPL_MMC_UHS_SUPPORT + bool "enable UHS support in SPL" + depends on SPL_MMC_IO_VOLTAGE + help + The Ultra High Speed (UHS) bus is available on some SDHC and SDXC + cards. The IO voltage must be switchable from 3.3v to 1.8v. The bus + frequency can go up to 208MHz (SDR104) + +config MMC_HS200_SUPPORT + bool "enable HS200 support" + help + The HS200 mode is support by some eMMC. The bus frequency is up to + 200MHz. This mode requires tuning the IO. + + +config SPL_MMC_HS200_SUPPORT + bool "enable HS200 support in SPL" + help + The HS200 mode is support by some eMMC. The bus frequency is up to + 200MHz. This mode requires tuning the IO. + config MMC_VERBOSE bool "Output more information about the MMC" default y diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index bfda942d46..793196bfec 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -63,6 +63,7 @@ void mmc_send_init_stream(struct mmc *mmc) dm_mmc_send_init_stream(mmc->dev); } +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout) { struct dm_mmc_ops *ops = mmc_get_ops(dev); @@ -76,6 +77,7 @@ int mmc_wait_dat0(struct mmc *mmc, int state, int timeout) { return dm_mmc_wait_dat0(mmc->dev, state, timeout); } +#endif int dm_mmc_get_wp(struct udevice *dev) { @@ -105,6 +107,7 @@ int mmc_getcd(struct mmc *mmc) return dm_mmc_get_cd(mmc->dev); } +#ifdef MMC_SUPPORTS_TUNING int dm_mmc_execute_tuning(struct udevice *dev, uint opcode) { struct dm_mmc_ops *ops = mmc_get_ops(dev); @@ -118,6 +121,7 @@ int mmc_execute_tuning(struct mmc *mmc, uint opcode) { return dm_mmc_execute_tuning(mmc->dev, opcode); } +#endif int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg) { diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 3fb82d9002..400e163340 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -59,10 +59,12 @@ struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) #if !CONFIG_IS_ENABLED(DM_MMC) +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) static int mmc_wait_dat0(struct mmc *mmc, int state, int timeout) { return -ENOSYS; } +#endif __weak int board_mmc_getwp(struct mmc *mmc) { @@ -190,14 +192,20 @@ static uint mmc_mode2freq(struct mmc *mmc, enum bus_mode mode) [SD_LEGACY] = 25000000, [MMC_HS] = 26000000, [SD_HS] = 50000000, +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) [UHS_SDR12] = 25000000, [UHS_SDR25] = 50000000, [UHS_SDR50] = 100000000, - [UHS_SDR104] = 208000000, [UHS_DDR50] = 50000000, +#ifdef MMC_SUPPORTS_TUNING + [UHS_SDR104] = 208000000, +#endif +#endif [MMC_HS_52] = 52000000, [MMC_DDR_52] = 52000000, +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) [MMC_HS_200] = 200000000, +#endif }; if (mode == MMC_LEGACY) @@ -308,6 +316,7 @@ int mmc_set_blocklen(struct mmc *mmc, int len) return err; } +#ifdef MMC_SUPPORTS_TUNING static const u8 tuning_blk_pattern_4bit[] = { 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, @@ -375,6 +384,7 @@ int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error) return 0; } +#endif static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) @@ -495,6 +505,7 @@ static int mmc_go_idle(struct mmc *mmc) return 0; } +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage) { struct mmc_cmd cmd; @@ -554,6 +565,7 @@ static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage) return 0; } +#endif static int sd_send_op_cond(struct mmc *mmc, bool uhs_en) { @@ -620,12 +632,14 @@ static int sd_send_op_cond(struct mmc *mmc, bool uhs_en) mmc->ocr = cmd.response[0]; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) if (uhs_en && !(mmc_host_is_spi(mmc)) && (cmd.response[0] & 0x41000000) == 0x41000000) { err = mmc_switch_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); if (err) return err; } +#endif mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); mmc->rca = 0; @@ -880,6 +894,7 @@ static int mmc_set_capacity(struct mmc *mmc, int part_num) return 0; } +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) static int mmc_boot_part_access_chk(struct mmc *mmc, unsigned int part_num) { int forbidden = 0; @@ -903,6 +918,13 @@ static int mmc_boot_part_access_chk(struct mmc *mmc, unsigned int part_num) return 0; } +#else +static inline int mmc_boot_part_access_chk(struct mmc *mmc, + unsigned int part_num) +{ + return 0; +} +#endif int mmc_switch_part(struct mmc *mmc, unsigned int part_num) { @@ -1169,7 +1191,9 @@ static int sd_get_capabilities(struct mmc *mmc) ALLOC_CACHE_ALIGN_BUFFER(__be32, switch_status, 16); struct mmc_data data; int timeout; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) u32 sd3_bus_mode; +#endif mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(SD_LEGACY); @@ -1251,6 +1275,7 @@ retry_scr: if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED) mmc->card_caps |= MMC_CAP(SD_HS); +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) /* Version before 3.0 don't support UHS modes */ if (mmc->version < SD_VERSION_3) return 0; @@ -1266,6 +1291,7 @@ retry_scr: mmc->card_caps |= MMC_CAP(UHS_SDR12); if (sd3_bus_mode & SD_MODE_UHS_DDR50) mmc->card_caps |= MMC_CAP(UHS_DDR50); +#endif return 0; } @@ -1438,10 +1464,12 @@ static inline int bus_width(uint cap) } #if !CONFIG_IS_ENABLED(DM_MMC) +#ifdef MMC_SUPPORTS_TUNING static int mmc_execute_tuning(struct mmc *mmc, uint opcode) { return -ENOTSUPP; } +#endif static void mmc_send_init_stream(struct mmc *mmc) { @@ -1507,9 +1535,12 @@ void mmc_dump_capabilities(const char *text, uint caps) struct mode_width_tuning { enum bus_mode mode; uint widths; +#ifdef MMC_SUPPORTS_TUNING uint tuning; +#endif }; +#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE) int mmc_voltage_to_mv(enum mmc_voltage voltage) { switch (voltage) { @@ -1535,13 +1566,22 @@ static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) return err; } +#else +static inline int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) +{ + return 0; +} +#endif static const struct mode_width_tuning sd_modes_by_pref[] = { +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +#ifdef MMC_SUPPORTS_TUNING { .mode = UHS_SDR104, .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, .tuning = MMC_CMD_SEND_TUNING_BLOCK }, +#endif { .mode = UHS_SDR50, .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, @@ -1554,14 +1594,17 @@ static const struct mode_width_tuning sd_modes_by_pref[] = { .mode = UHS_SDR25, .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, }, +#endif { .mode = SD_HS, .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, }, +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) { .mode = UHS_SDR12, .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, }, +#endif { .mode = SD_LEGACY, .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, @@ -1579,7 +1622,11 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) int err; uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT}; const struct mode_width_tuning *mwt; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false; +#else + bool uhs_en = false; +#endif uint caps; #ifdef DEBUG @@ -1618,6 +1665,7 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) mmc_select_mode(mmc, mwt->mode); mmc_set_clock(mmc, mmc->tran_speed, false); +#ifdef MMC_SUPPORTS_TUNING /* execute tuning if needed */ if (mwt->tuning && !mmc_host_is_spi(mmc)) { err = mmc_execute_tuning(mmc, @@ -1627,6 +1675,7 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) goto error; } } +#endif err = sd_read_ssr(mmc); if (!err) @@ -1680,6 +1729,7 @@ static int mmc_read_and_compare_ext_csd(struct mmc *mmc) return -EBADMSG; } +#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE) static int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode, uint32_t allowed_mask) { @@ -1716,13 +1766,22 @@ static int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode, return -ENOTSUPP; } +#else +static inline int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode, + uint32_t allowed_mask) +{ + return 0; +} +#endif static const struct mode_width_tuning mmc_modes_by_pref[] = { +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) { .mode = MMC_HS_200, .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, .tuning = MMC_CMD_SEND_TUNING_BLOCK_HS200 }, +#endif { .mode = MMC_DDR_52, .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, @@ -1832,6 +1891,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) /* configure the bus mode (host) */ mmc_select_mode(mmc, mwt->mode); mmc_set_clock(mmc, mmc->tran_speed, false); +#ifdef MMC_SUPPORTS_TUNING /* execute tuning if needed */ if (mwt->tuning) { @@ -1841,6 +1901,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) goto error; } } +#endif /* do a transfer to check the configuration */ err = mmc_read_and_compare_ext_csd(mmc); diff --git a/include/mmc.h b/include/mmc.h index e3f777f680..e89ba95e5d 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -15,6 +15,13 @@ #include #include +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) +#define MMC_SUPPORTS_TUNING +#endif +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +#define MMC_SUPPORTS_TUNING +#endif + /* SD/MMC version bits; 8 flags, 8 major, 8 minor, 8 change */ #define SD_VERSION_SD (1U << 31) #define MMC_VERSION_MMC (1U << 30) @@ -425,6 +432,7 @@ struct dm_mmc_ops { */ int (*get_wp)(struct udevice *dev); +#ifdef MMC_SUPPORTS_TUNING /** * execute_tuning() - Start the tuning process * @@ -433,7 +441,9 @@ struct dm_mmc_ops { * @return 0 if OK, -ve on error */ int (*execute_tuning)(struct udevice *dev, uint opcode); +#endif +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) /** * wait_dat0() - wait until dat0 is in the target state * (CLK must be running during the wait) @@ -444,6 +454,7 @@ struct dm_mmc_ops { * @return 0 if dat0 is in the target state, -ve on error */ int (*wait_dat0)(struct udevice *dev, int state, int timeout); +#endif }; #define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops) @@ -500,13 +511,13 @@ enum bus_mode { SD_LEGACY, MMC_HS, SD_HS, + MMC_HS_52, + MMC_DDR_52, UHS_SDR12, UHS_SDR25, UHS_SDR50, - UHS_SDR104, UHS_DDR50, - MMC_HS_52, - MMC_DDR_52, + UHS_SDR104, MMC_HS_200, MMC_MODES_END }; @@ -516,8 +527,12 @@ void mmc_dump_capabilities(const char *text, uint caps); static inline bool mmc_is_mode_ddr(enum bus_mode mode) { - if ((mode == MMC_DDR_52) || (mode == UHS_DDR50)) + if (mode == MMC_DDR_52) return true; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + else if (mode == UHS_DDR50) + return true; +#endif else return false; } @@ -528,7 +543,11 @@ static inline bool mmc_is_mode_ddr(enum bus_mode mode) static inline bool supports_uhs(uint caps) { +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) return (caps & UHS_CAPS) ? true : false; +#else + return false; +#endif } /* From cf17789e078fd23b95ee86ffc441e98709c2d063 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:44:02 +0100 Subject: [PATCH 41/56] mmc: make optional the support for eMMC hardware partitioning Not all boards have an eMMC and not all users have a need for this. Allow to compile it out. By default it is still included. Signed-off-by: Jean-Jacques Hiblot --- cmd/mmc.c | 4 ++++ drivers/mmc/Kconfig | 7 +++++++ drivers/mmc/mmc.c | 2 ++ 3 files changed, 13 insertions(+) diff --git a/cmd/mmc.c b/cmd/mmc.c index 25795e0325..9a95293753 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -438,6 +438,7 @@ static int do_mmc_list(cmd_tbl_t *cmdtp, int flag, return CMD_RET_SUCCESS; } +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) static int parse_hwpart_user(struct mmc_hwpart_conf *pconf, int argc, char * const argv[]) { @@ -587,6 +588,7 @@ static int do_mmc_hwpartition(cmd_tbl_t *cmdtp, int flag, return CMD_RET_FAILURE; } } +#endif #ifdef CONFIG_SUPPORT_EMMC_BOOT static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag, @@ -796,7 +798,9 @@ static cmd_tbl_t cmd_mmc[] = { U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""), U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""), U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""), +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""), +#endif #ifdef CONFIG_SUPPORT_EMMC_BOOT U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""), U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""), diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2fcdaddf22..d196eed540 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -51,6 +51,13 @@ config MMC_QUIRKS are enabled by default, other may require additionnal flags or are enabled by the host driver. +config MMC_HW_PARTITIONING + bool "Support for HW partitioning command(eMMC)" + default y + help + This adds a command and an API to do hardware partitioning on eMMC + devices. + config MMC_IO_VOLTAGE bool "Support IO voltage configuration" help diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 400e163340..67d05c5413 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -950,6 +950,7 @@ int mmc_switch_part(struct mmc *mmc, unsigned int part_num) return ret; } +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) int mmc_hwpart_config(struct mmc *mmc, const struct mmc_hwpart_conf *conf, enum mmc_hwpart_conf_mode mode) @@ -1143,6 +1144,7 @@ int mmc_hwpart_config(struct mmc *mmc, return 0; } +#endif #if !CONFIG_IS_ENABLED(DM_MMC) int mmc_getcd(struct mmc *mmc) From 33678bd7f5a3cf7c8a226512e443ab7efbe3a8e2 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:44:03 +0100 Subject: [PATCH 42/56] configs: openrd: removed support for eMMC hardware partitioning builds are broken because the size of the binary exceeds the limit. Make some space by removing support for hardware partitioning as those boards don't have any eMMC. Signed-off-by: Jean-Jacques Hiblot --- configs/openrd_base_defconfig | 1 + configs/openrd_client_defconfig | 1 + configs/openrd_ultimate_defconfig | 1 + 3 files changed, 3 insertions(+) diff --git a/configs/openrd_base_defconfig b/configs/openrd_base_defconfig index 1a829b763f..f91e0f9edb 100644 --- a/configs/openrd_base_defconfig +++ b/configs/openrd_base_defconfig @@ -25,6 +25,7 @@ CONFIG_CMD_UBI=y CONFIG_ISO_PARTITION=y CONFIG_ENV_IS_IN_NAND=y CONFIG_MVSATA_IDE=y +# CONFIG_MMC_HW_PARTITIONING is not set CONFIG_SYS_NS16550=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/openrd_client_defconfig b/configs/openrd_client_defconfig index 7a95b5b54f..e65c437d88 100644 --- a/configs/openrd_client_defconfig +++ b/configs/openrd_client_defconfig @@ -25,6 +25,7 @@ CONFIG_CMD_UBI=y CONFIG_ISO_PARTITION=y CONFIG_ENV_IS_IN_NAND=y CONFIG_MVSATA_IDE=y +# CONFIG_MMC_HW_PARTITIONING is not set CONFIG_SYS_NS16550=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/openrd_ultimate_defconfig b/configs/openrd_ultimate_defconfig index 757be16f53..dfd809dbbb 100644 --- a/configs/openrd_ultimate_defconfig +++ b/configs/openrd_ultimate_defconfig @@ -25,6 +25,7 @@ CONFIG_CMD_UBI=y CONFIG_ISO_PARTITION=y CONFIG_ENV_IS_IN_NAND=y CONFIG_MVSATA_IDE=y +# CONFIG_MMC_HW_PARTITIONING is not set CONFIG_SYS_NS16550=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y From c9d3f34e77dc53798f9757d31aeb020b8fee460c Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 30 Nov 2017 17:44:04 +0100 Subject: [PATCH 43/56] configs: omapl138_lcdk: decrease the loglevel to reduce the size of the SPL The changes in the MMC stack have increased its footprint up to the point were its breaks the generation of the SPL for this platform. Fix this by reducing the loglevel. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Peter Howard --- configs/omapl138_lcdk_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/omapl138_lcdk_defconfig b/configs/omapl138_lcdk_defconfig index 8d6b12f5b7..0d4506ec44 100644 --- a/configs/omapl138_lcdk_defconfig +++ b/configs/omapl138_lcdk_defconfig @@ -8,6 +8,7 @@ CONFIG_SPL_MMC_SUPPORT=y CONFIG_SPL_SERIAL_SUPPORT=y CONFIG_SPL_NAND_SUPPORT=y CONFIG_BOOTDELAY=3 +CONFIG_LOGLEVEL=3 CONFIG_VERSION_VARIABLE=y # CONFIG_DISPLAY_CPUINFO is not set # CONFIG_DISPLAY_BOARDINFO is not set From 49f89252ed4abf7f3899731f914f6b67462ea690 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 14 Dec 2017 11:47:14 +0100 Subject: [PATCH 44/56] dm: mmc: sandbox: Update SD card emulation The SDcard initialization procedure does a few more things than it did earlier: * switch the bus width even for 1-bit bus width * check that speed has been properly set (in resp[4] of SD_CMD_SWITCH_FUNC) Update the SD simulator to handle those requests gracefully. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Jaehoon Chung --- drivers/mmc/sandbox_mmc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c index fdb29a5505..8a5d256c11 100644 --- a/drivers/mmc/sandbox_mmc.c +++ b/drivers/mmc/sandbox_mmc.c @@ -48,9 +48,12 @@ static int sandbox_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, cmd->response[1] = 10 << 16; /* 1 << block_len */ break; case SD_CMD_SWITCH_FUNC: { + if (!data) + break; u32 *resp = (u32 *)data->dest; - resp[7] = cpu_to_be32(SD_HIGHSPEED_BUSY); + if ((cmd->cmdarg & 0xF) == UHS_SDR12_BUS_SPEED) + resp[4] = (cmd->cmdarg & 0xF) << 24; break; } case MMC_CMD_READ_SINGLE_BLOCK: From dc80373299aec757f6dbd3bf170d91aa3f0dce7a Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Sat, 16 Dec 2017 23:01:22 -0500 Subject: [PATCH 45/56] am335x_hs_evm: Trim options in SPL to reduce binary size The am335x_hs_evm runs into size constraint problems at times with various toolchains as changes come in due to the config have a large number of options in SPL (to showcase what is possible) while also having rather constrained binary limits. Gain some of this room back by lowering the loglevel, disabling HW partition support and switching over to the tiny FIT image support. Cc: Andrew F. Davis Signed-off-by: Tom Rini --- I'd really appreciate a run-time test of this patch if at all possible as I'm a little worried about TINY_FIT being incompatible with all of the security options. Thanks! --- configs/am335x_hs_evm_defconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configs/am335x_hs_evm_defconfig b/configs/am335x_hs_evm_defconfig index 78cf48dde7..63e7a077ae 100644 --- a/configs/am335x_hs_evm_defconfig +++ b/configs/am335x_hs_evm_defconfig @@ -13,10 +13,12 @@ CONFIG_ANDROID_BOOT_IMAGE=y CONFIG_FIT_IMAGE_POST_PROCESS=y CONFIG_SPL_LOAD_FIT=y CONFIG_SPL_FIT_IMAGE_POST_PROCESS=y +CONFIG_LOGLEVEL=3 CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_VERSION_VARIABLE=y CONFIG_ARCH_MISC_INIT=y CONFIG_SPL=y +CONFIG_SPL_FIT_IMAGE_TINY=y # CONFIG_SPL_ENV_SUPPORT is not set # CONFIG_SPL_EXT_SUPPORT is not set CONFIG_SPL_MTD_SUPPORT=y @@ -37,6 +39,7 @@ CONFIG_DFU_RAM=y CONFIG_DM_I2C=y CONFIG_MISC=y CONFIG_DM_MMC=y +# CONFIG_MMC_HW_PARTITIONING is not set CONFIG_MMC_OMAP_HS=y CONFIG_NAND=y CONFIG_SPI_FLASH=y @@ -60,5 +63,6 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x0451 CONFIG_USB_GADGET_PRODUCT_NUM=0xd022 CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_USB_ETHER=y +CONFIG_SPL_TINY_MEMSET=y CONFIG_RSA=y CONFIG_LZO=y From d0e443786cd9179ad3b8e3bbc70cc446af86f8f0 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:28 +0100 Subject: [PATCH 46/56] common: do not compile common fastboot code when building the SPL This is not required as fastboot can't be started from SPL. Signed-off-by: Jean-Jacques Hiblot --- common/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/Makefile b/common/Makefile index 14166209fe..c7bde239c1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -109,6 +109,7 @@ obj-$(CONFIG_IO_TRACE) += iotrace.o obj-y += memsize.o obj-y += stdio.o +ifndef CONFIG_SPL_BUILD # This option is not just y/n - it can have a numeric value ifdef CONFIG_FASTBOOT_FLASH obj-y += image-sparse.o @@ -119,6 +120,7 @@ ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV obj-y += fb_nand.o endif endif +endif ifdef CONFIG_CMD_EEPROM_LAYOUT obj-y += eeprom/eeprom_field.o eeprom/eeprom_layout.o From 9b79dbd2019d2ef86dae4fab49b0c57360b8b61f Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:29 +0100 Subject: [PATCH 47/56] mmc: atmel: when sending a data command, use the provided block size struct mmc_data contains the block size to use for the data transfer. Use this information instead of using the default value or the block length information stored in struct mmc. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/gen_atmel_mci.c | 42 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 22154d13d7..1c108b5470 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -74,6 +74,20 @@ static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg) cmdr, cmdr & 0x3F, arg, status, msg); } +static inline void mci_set_blklen(atmel_mci_t *mci, int blklen) +{ + unsigned int version = atmel_mci_get_version(mci); + + blklen &= 0xfffc; + + /* MCI IP version >= 0x200 has blkr */ + if (version >= 0x200) + writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->blkr)), + &mci->blkr); + else + writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->mr)), &mci->mr); +} + /* Setup for MCI Clock and Block Size */ #ifdef CONFIG_DM_MMC static void mci_set_mode(struct udevice *dev, u32 hz, u32 blklen) @@ -124,7 +138,6 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) priv->curr_clk = bus_hz / (clkdiv * 2 + clkodd + 2); else priv->curr_clk = (bus_hz / (clkdiv + 1)) / 2; - blklen &= 0xfffc; mr = MMCI_BF(CLKDIV, clkdiv); @@ -138,14 +151,10 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) */ if (version >= 0x500) mr |= MMCI_BF(CLKODD, clkodd); - else - mr |= MMCI_BF(BLKLEN, blklen); writel(mr, &mci->mr); - /* MCI IP version >= 0x200 has blkr */ - if (version >= 0x200) - writel(MMCI_BF(BLKLEN, blklen), &mci->blkr); + mci_set_blklen(mci, blklen); if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS) writel(MMCI_BIT(HSMODE), &mci->cfg); @@ -236,7 +245,6 @@ static int atmel_mci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, { struct atmel_mci_plat *plat = dev_get_platdata(dev); struct atmel_mci_priv *priv = dev_get_priv(dev); - struct mmc *mmc = mmc_get_mmc_dev(dev); atmel_mci_t *mci = plat->mci; #else static int @@ -257,11 +265,13 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Figure out the transfer arguments */ cmdr = mci_encode_cmd(cmd, data, &error_flags); + mci_set_blklen(mci, data->blocksize); + /* For multi blocks read/write, set the block register */ if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) || (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) - writel(data->blocks | MMCI_BF(BLKLEN, mmc->read_bl_len), - &mci->blkr); + writel(data->blocks | MMCI_BF(BLKLEN, data->blocksize), + &mci->blkr); /* Send the command */ writel(cmd->cmdarg, &mci->argr); @@ -295,17 +305,15 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if (data) { u32 word_count, block_count; u32* ioptr; - u32 sys_blocksize, dummy, i; + u32 i; u32 (*mci_data_op) (atmel_mci_t *mci, u32* data, u32 error_flags); if (data->flags & MMC_DATA_READ) { mci_data_op = mci_data_read; - sys_blocksize = mmc->read_bl_len; ioptr = (u32*)data->dest; } else { mci_data_op = mci_data_write; - sys_blocksize = mmc->write_bl_len; ioptr = (u32*)data->src; } @@ -328,16 +336,6 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) 1, cnt, 0); } #endif -#ifdef DEBUG - if (!status && word_count < (sys_blocksize / 4)) - printf("filling rest of block...\n"); -#endif - /* fill the rest of a full block */ - while (!status && word_count < (sys_blocksize / 4)) { - status = mci_data_op(mci, &dummy, - error_flags); - word_count++; - } if (status) { dump_cmd(cmdr, cmd->cmdarg, status, "Data Transfer Failed"); From baef2070a407f14fbab10312b509732e6d104590 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:30 +0100 Subject: [PATCH 48/56] mmc: compile out more code if support for UHS and HS200 is not enabled Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 67d05c5413..18d202d3b7 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -799,9 +799,11 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode) case MMC_DDR_52: speed_bits = EXT_CSD_TIMING_HS; break; +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) case MMC_HS_200: speed_bits = EXT_CSD_TIMING_HS200; break; +#endif case MMC_LEGACY: speed_bits = EXT_CSD_TIMING_LEGACY; break; @@ -851,10 +853,12 @@ static int mmc_get_capabilities(struct mmc *mmc) cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x3f; mmc->cardtype = cardtype; +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) if (cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V | EXT_CSD_CARD_TYPE_HS200_1_8V)) { mmc->card_caps |= MMC_MODE_HS200; } +#endif if (cardtype & EXT_CSD_CARD_TYPE_52) { if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) mmc->card_caps |= MMC_MODE_DDR_52MHz; @@ -1307,10 +1311,15 @@ static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode) switch (mode) { case SD_LEGACY: - case UHS_SDR12: speed = UHS_SDR12_BUS_SPEED; break; case SD_HS: + speed = HIGH_SPEED_BUS_SPEED; + break; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + case UHS_SDR12: + speed = UHS_SDR12_BUS_SPEED; + break; case UHS_SDR25: speed = UHS_SDR25_BUS_SPEED; break; @@ -1323,6 +1332,7 @@ static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode) case UHS_SDR104: speed = UHS_SDR104_BUS_SPEED; break; +#endif default: return -EINVAL; } From 58a6fb7b04963d1bf353b66e27f3084037babd6c Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:31 +0100 Subject: [PATCH 49/56] mmc: reworked version lookup in mmc_startup_v4 Using a table versus a switch() structure saves a bit of space Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 18d202d3b7..5255c0dccf 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1940,6 +1940,17 @@ static int mmc_startup_v4(struct mmc *mmc) u64 capacity; bool has_parts = false; bool part_completed; + static const u32 mmc_versions[] = { + MMC_VERSION_4, + MMC_VERSION_4_1, + MMC_VERSION_4_2, + MMC_VERSION_4_3, + MMC_VERSION_4_41, + MMC_VERSION_4_5, + MMC_VERSION_5_0, + MMC_VERSION_5_1 + }; + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4)) @@ -1957,7 +1968,12 @@ static int mmc_startup_v4(struct mmc *mmc) return -ENOMEM; memcpy(mmc->ext_csd, ext_csd, MMC_MAX_BLOCK_LEN); - if (ext_csd[EXT_CSD_REV] >= 2) { + if (ext_csd[EXT_CSD_REV] > ARRAY_SIZE(mmc_versions)) + return -EINVAL; + + mmc->version = mmc_versions[ext_csd[EXT_CSD_REV]]; + + if (mmc->version >= MMC_VERSION_4_2) { /* * According to the JEDEC Standard, the value of * ext_csd's capacity is valid if the value is more @@ -1972,30 +1988,6 @@ static int mmc_startup_v4(struct mmc *mmc) mmc->capacity_user = capacity; } - switch (ext_csd[EXT_CSD_REV]) { - case 1: - mmc->version = MMC_VERSION_4_1; - break; - case 2: - mmc->version = MMC_VERSION_4_2; - break; - case 3: - mmc->version = MMC_VERSION_4_3; - break; - case 5: - mmc->version = MMC_VERSION_4_41; - break; - case 6: - mmc->version = MMC_VERSION_4_5; - break; - case 7: - mmc->version = MMC_VERSION_5_0; - break; - case 8: - mmc->version = MMC_VERSION_5_1; - break; - } - /* The partition data may be non-zero but it is only * effective if PARTITION_SETTING_COMPLETED is set in * EXT_CSD, so ignore any data if this bit is not set, From d6400c3f8520bb9a203fe397039279c80f093c27 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:32 +0100 Subject: [PATCH 50/56] mmc: add a Kconfig option to enable the support for MMC write operations This allows using CONFIG_IS_ENABLED(MMC_WRITE) to compile out code needed only if write support is required. The option is added for u-boot and for SPL Signed-off-by: Jean-Jacques Hiblot --- cmd/mvebu/bubt.c | 2 +- common/spl/Kconfig | 9 +++++++++ drivers/mmc/Kconfig | 7 +++++++ drivers/mmc/Makefile | 4 +--- drivers/mmc/mmc-uclass.c | 2 +- drivers/mmc/mmc_private.h | 4 ++-- 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/cmd/mvebu/bubt.c b/cmd/mvebu/bubt.c index a1997ac0d3..23fb8cd807 100644 --- a/cmd/mvebu/bubt.c +++ b/cmd/mvebu/bubt.c @@ -110,7 +110,7 @@ static ulong get_load_addr(void) /******************************************************************** * eMMC services ********************************************************************/ -#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(MMC_WRITE) static int mmc_burn_image(size_t image_size) { struct mmc *mmc; diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 9d35f41233..d686b1ecbd 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -301,6 +301,7 @@ config SPL_ENV_SUPPORT config SPL_SAVEENV bool "Support save environment" depends on SPL_ENV_SUPPORT + select SPL_MMC_WRITE if ENV_IS_IN_MMC help Enable save environment support in SPL after setenv. By default the saveenv option is not provided in SPL, but some boards need @@ -415,6 +416,14 @@ config SPL_MMC_SUPPORT this option to build the drivers in drivers/mmc as part of an SPL build. +config SPL_MMC_WRITE + bool "MMC/SD/SDIO card support for write operations in SPL" + depends on SPL_MMC_SUPPORT + default n + help + Enable write access to MMC and SD Cards in SPL + + config SPL_MPC8XXX_INIT_DDR_SUPPORT bool "Support MPC8XXX DDR init" help diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index d196eed540..ab0627a8af 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -10,6 +10,13 @@ config MMC If you want MMC/SD/SDIO support, you should say Y here and also to your specific host controller driver. +config MMC_WRITE + bool "support for MMC/SD write operations" + depends on MMC + default y + help + Enable write access to MMC and SD Cards + config DM_MMC bool "Enable MMC controllers using Driver Model" depends on DM diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9af375b044..64b6f21c61 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -7,6 +7,7 @@ obj-y += mmc.o obj-$(CONFIG_$(SPL_)DM_MMC) += mmc-uclass.o +obj-$(CONFIG_$(SPL_)MMC_WRITE) += mmc_write.o ifndef CONFIG_$(SPL_)BLK obj-y += mmc_legacy.o @@ -16,9 +17,6 @@ obj-$(CONFIG_SUPPORT_EMMC_BOOT) += mmc_boot.o ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o -obj-$(CONFIG_SPL_SAVEENV) += mmc_write.o -else -obj-y += mmc_write.o endif obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 793196bfec..26c6ab7ad1 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -370,7 +370,7 @@ static int mmc_blk_probe(struct udevice *dev) static const struct blk_ops mmc_blk_ops = { .read = mmc_bread, -#ifndef CONFIG_SPL_BUILD +#if CONFIG_IS_ENABLED(MMC_WRITE) .write = mmc_bwrite, .erase = mmc_berase, #endif diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h index 1290eed590..a9be4b0102 100644 --- a/drivers/mmc/mmc_private.h +++ b/drivers/mmc/mmc_private.h @@ -28,7 +28,7 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, void *dst); #endif -#if !(defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SAVEENV)) +#if CONFIG_IS_ENABLED(MMC_WRITE) #if CONFIG_IS_ENABLED(BLK) ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, @@ -40,7 +40,7 @@ ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt); #endif -#else /* CONFIG_SPL_BUILD and CONFIG_SPL_SAVEENV is not defined */ +#else /* CONFIG_SPL_MMC_WRITE is not defined */ /* declare dummies to reduce code size. */ From 5b2e72f32721484d2dea3d0dcf8c020bf3cbe574 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:33 +0100 Subject: [PATCH 51/56] mmc: read ssr only if MMC write support is enabled The content of ssr is useful only for erase operations. on ARM, removing sd_read_ssr() saves around 300 bytes. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 25 ++++++++++++++----------- include/mmc.h | 2 ++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5255c0dccf..fd9498708c 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -22,14 +22,6 @@ #include #include "mmc_private.h" -static const unsigned int sd_au_size[] = { - 0, SZ_16K / 512, SZ_32K / 512, - SZ_64K / 512, SZ_128K / 512, SZ_256K / 512, - SZ_512K / 512, SZ_1M / 512, SZ_2M / 512, - SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512, - SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512, -}; - static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage); static int mmc_power_cycle(struct mmc *mmc); static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps); @@ -1376,8 +1368,17 @@ int sd_select_bus_width(struct mmc *mmc, int w) return 0; } +#if CONFIG_IS_ENABLED(MMC_WRITE) static int sd_read_ssr(struct mmc *mmc) { + static const unsigned int sd_au_size[] = { + 0, SZ_16K / 512, SZ_32K / 512, + SZ_64K / 512, SZ_128K / 512, SZ_256K / 512, + SZ_512K / 512, SZ_1M / 512, SZ_2M / 512, + SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512, + SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, + SZ_64M / 512, + }; int err, i; struct mmc_cmd cmd; ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16); @@ -1431,7 +1432,7 @@ retry_ssr: return 0; } - +#endif /* frequency bases */ /* divided by 10 to be nice to platforms without floating point */ static const int fbase[] = { @@ -1689,12 +1690,14 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) } #endif +#if CONFIG_IS_ENABLED(MMC_WRITE) err = sd_read_ssr(mmc); + if (!err) + pr_warn("unable to read ssr\n"); +#endif if (!err) return 0; - pr_warn("bad ssr\n"); - error: /* revert to a safer bus speed */ mmc_select_mode(mmc, SD_LEGACY); diff --git a/include/mmc.h b/include/mmc.h index e89ba95e5d..f50c714428 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -588,7 +588,9 @@ struct mmc { uint write_bl_len; uint erase_grp_size; /* in 512-byte sectors */ uint hc_wp_grp_size; /* in 512-byte sectors */ +#if CONFIG_IS_ENABLED(MMC_WRITE) struct sd_ssr ssr; /* SD status register */ +#endif u64 capacity; u64 capacity_user; u64 capacity_boot; From e6fa5a546154a42c4f6662cd8890c238207ce21a Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:34 +0100 Subject: [PATCH 52/56] mmc: compile out erase and write mmc commands if write operations are not enabled Also remove erase_grp_size and write_bl_len from struct mmc as they are not used anymore. On ARM, removing them saves about 100 bytes of code space in SPL. Signed-off-by: Jean-Jacques Hiblot --- cmd/mmc.c | 8 ++++++++ drivers/mmc/mmc.c | 16 ++++++++++++++-- include/mmc.h | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/cmd/mmc.c b/cmd/mmc.c index 9a95293753..65601d82c3 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -45,8 +45,10 @@ static void print_mmcinfo(struct mmc *mmc) printf("Bus Width: %d-bit%s\n", mmc->bus_width, mmc->ddr_mode ? " DDR" : ""); +#if CONFIG_IS_ENABLED(MMC_WRITE) puts("Erase Group Size: "); print_size(((u64)mmc->erase_grp_size) << 9, "\n"); +#endif if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) { bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0; @@ -302,6 +304,8 @@ static int do_mmc_read(cmd_tbl_t *cmdtp, int flag, return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; } + +#if CONFIG_IS_ENABLED(MMC_WRITE) static int do_mmc_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -360,6 +364,8 @@ static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag, return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; } +#endif + static int do_mmc_rescan(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -792,8 +798,10 @@ static int do_mmc_bkops_enable(cmd_tbl_t *cmdtp, int flag, static cmd_tbl_t cmd_mmc[] = { U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""), U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""), +#if CONFIG_IS_ENABLED(MMC_WRITE) U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""), U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""), +#endif U_BOOT_CMD_MKENT(rescan, 1, 1, do_mmc_rescan, "", ""), U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""), U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""), diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index fd9498708c..3930aad13b 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2066,9 +2066,11 @@ static int mmc_startup_v4(struct mmc *mmc) } if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { +#if CONFIG_IS_ENABLED(MMC_WRITE) /* Read out group size from ext_csd */ mmc->erase_grp_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; +#endif /* * if high capacity and partition setting completed * SEC_COUNT is valid even if it is smaller than 2 GiB @@ -2082,7 +2084,9 @@ static int mmc_startup_v4(struct mmc *mmc) capacity *= MMC_MAX_BLOCK_LEN; mmc->capacity_user = capacity; } - } else { + } +#if CONFIG_IS_ENABLED(MMC_WRITE) + else { /* Calculate the group size from the csd value. */ int erase_gsz, erase_gmul; @@ -2091,7 +2095,7 @@ static int mmc_startup_v4(struct mmc *mmc) mmc->erase_grp_size = (erase_gsz + 1) * (erase_gmul + 1); } - +#endif mmc->hc_wp_grp_size = 1024 * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; @@ -2222,11 +2226,13 @@ static int mmc_startup(struct mmc *mmc) mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); +#if CONFIG_IS_ENABLED(MMC_WRITE) if (IS_SD(mmc)) mmc->write_bl_len = mmc->read_bl_len; else mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf); +#endif if (mmc->high_capacity) { csize = (mmc->csd[1] & 0x3f) << 16 @@ -2248,8 +2254,10 @@ static int mmc_startup(struct mmc *mmc) if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN) mmc->read_bl_len = MMC_MAX_BLOCK_LEN; +#if CONFIG_IS_ENABLED(MMC_WRITE) if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN) mmc->write_bl_len = MMC_MAX_BLOCK_LEN; +#endif if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) { cmd.cmdidx = MMC_CMD_SET_DSR; @@ -2273,7 +2281,9 @@ static int mmc_startup(struct mmc *mmc) /* * For SD, its erase group is always one sector */ +#if CONFIG_IS_ENABLED(MMC_WRITE) mmc->erase_grp_size = 1; +#endif mmc->part_config = MMCPART_NOAVAILABLE; err = mmc_startup_v4(mmc); @@ -2304,7 +2314,9 @@ static int mmc_startup(struct mmc *mmc) /* Fix the block length for DDR mode */ if (mmc->ddr_mode) { mmc->read_bl_len = MMC_MAX_BLOCK_LEN; +#if CONFIG_IS_ENABLED(MMC_WRITE) mmc->write_bl_len = MMC_MAX_BLOCK_LEN; +#endif } /* fill in device description */ diff --git a/include/mmc.h b/include/mmc.h index f50c714428..3abeb581ae 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -585,8 +585,10 @@ struct mmc { uint tran_speed; uint legacy_speed; /* speed for the legacy mode provided by the card */ uint read_bl_len; +#if CONFIG_IS_ENABLED(MMC_WRITE) uint write_bl_len; uint erase_grp_size; /* in 512-byte sectors */ +#endif uint hc_wp_grp_size; /* in 512-byte sectors */ #if CONFIG_IS_ENABLED(MMC_WRITE) struct sd_ssr ssr; /* SD status register */ From 173c06dfcc5419e38160d7eaf596256df0b4bdd5 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:35 +0100 Subject: [PATCH 53/56] mmc: don't read the size of eMMC enhanced user data area in SPL This information is only used by the "mmc info" command. On ARM removing this information from SPL saves about 140 of code space. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/mmc.c | 2 ++ include/mmc.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 3930aad13b..c1ba57f86d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2028,6 +2028,7 @@ static int mmc_startup_v4(struct mmc *mmc) mmc->capacity_gp[i] <<= 19; } +#ifndef CONFIG_SPL_BUILD if (part_completed) { mmc->enh_user_size = (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) + @@ -2044,6 +2045,7 @@ static int mmc_startup_v4(struct mmc *mmc) if (mmc->high_capacity) mmc->enh_user_start <<= 9; } +#endif /* * Host needs to enable ERASE_GRP_DEF bit if device is diff --git a/include/mmc.h b/include/mmc.h index 3abeb581ae..cd068b9429 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -598,8 +598,10 @@ struct mmc { u64 capacity_boot; u64 capacity_rpmb; u64 capacity_gp[4]; +#ifndef CONFIG_SPL_BUILD u64 enh_user_start; u64 enh_user_size; +#endif #if !CONFIG_IS_ENABLED(BLK) struct blk_desc block_dev; #endif From b7a6e2c9c396c35596f467f5187da937306ddeb8 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 4 Jan 2018 15:23:36 +0100 Subject: [PATCH 54/56] mmc: remove hc_wp_grp_size from struct mmc if not needed hc_wp_grp_size is needed only if hardware partitionning is used. On ARM removing it saves about 30 bytes of code space. Signed-off-by: Jean-Jacques Hiblot --- cmd/mmc.c | 2 ++ drivers/mmc/mmc.c | 2 ++ include/mmc.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/cmd/mmc.c b/cmd/mmc.c index 65601d82c3..58fdc36f08 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -54,8 +54,10 @@ static void print_mmcinfo(struct mmc *mmc) bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0; bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR); +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) puts("HC WP Group Size: "); print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n"); +#endif puts("User Capacity: "); print_size(mmc->capacity_user, usr_enh ? " ENH" : ""); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index c1ba57f86d..53c819187e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2098,9 +2098,11 @@ static int mmc_startup_v4(struct mmc *mmc) * (erase_gmul + 1); } #endif +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) mmc->hc_wp_grp_size = 1024 * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; +#endif mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; diff --git a/include/mmc.h b/include/mmc.h index cd068b9429..a46eaed746 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -589,7 +589,9 @@ struct mmc { uint write_bl_len; uint erase_grp_size; /* in 512-byte sectors */ #endif +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) uint hc_wp_grp_size; /* in 512-byte sectors */ +#endif #if CONFIG_IS_ENABLED(MMC_WRITE) struct sd_ssr ssr; /* SD status register */ #endif From 8c0bb8582459a739ac27eda3d738734e489d0465 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Fri, 22 Dec 2017 12:27:14 -0500 Subject: [PATCH 55/56] power: Rearrange code to guard power command with CONFIG_SPL_BUILD guard In order to discard this code when unused in SPL we need to guard the command with a check for CONFIG_SPL_BUILD and we rearrange the code slightly to make this cleaner. Cc: Jaehoon Chung Signed-off-by: Tom Rini --- drivers/power/power_core.c | 61 ++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/drivers/power/power_core.c b/drivers/power/power_core.c index b72286d429..46840a33e0 100644 --- a/drivers/power/power_core.c +++ b/drivers/power/power_core.c @@ -47,36 +47,6 @@ int pmic_set_output(struct pmic *p, u32 reg, int out, int on) return 0; } -static void pmic_show_info(struct pmic *p) -{ - printf("PMIC: %s\n", p->name); -} - -static int pmic_dump(struct pmic *p) -{ - int i, ret; - u32 val; - - if (!p) { - puts("Wrong PMIC name!\n"); - return -ENODEV; - } - - pmic_show_info(p); - for (i = 0; i < p->number_of_regs; i++) { - ret = pmic_reg_read(p, i, &val); - if (ret) - puts("PMIC: Registers dump failed\n"); - - if (!(i % 8)) - printf("\n0x%02x: ", i); - - printf("%08x ", val); - } - puts("\n"); - return 0; -} - struct pmic *pmic_alloc(void) { struct pmic *p; @@ -108,7 +78,33 @@ struct pmic *pmic_get(const char *s) return NULL; } -const char *power_get_interface(int interface) +#ifndef CONFIG_SPL_BUILD +static int pmic_dump(struct pmic *p) +{ + int i, ret; + u32 val; + + if (!p) { + puts("Wrong PMIC name!\n"); + return -ENODEV; + } + + printf("PMIC: %s\n", p->name); + for (i = 0; i < p->number_of_regs; i++) { + ret = pmic_reg_read(p, i, &val); + if (ret) + puts("PMIC: Registers dump failed\n"); + + if (!(i % 8)) + printf("\n0x%02x: ", i); + + printf("%08x ", val); + } + puts("\n"); + return 0; +} + +static const char *power_get_interface(int interface) { const char *power_interface[] = {"I2C", "SPI", "|+|-|"}; return power_interface[interface]; @@ -125,7 +121,7 @@ static void pmic_list_names(void) } } -int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { u32 ret, reg, val; char *cmd, *name; @@ -221,3 +217,4 @@ U_BOOT_CMD( "pmic name bat state - write register\n" "pmic name bat charge - write register\n" ); +#endif From db359efd5907edbeaea2e3eb3504729f60208cd8 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 2 Jan 2018 16:51:22 +0800 Subject: [PATCH 56/56] mmc: fsl_esdhc: Fix eMMC 1.8v setting issue Current USDHC driver will reset VSELECT to 0 (3.3v) during mmc init, then set to 1 for 1.8v eMMC I/O. When booting from eMMC, since ROM has already set VSELECT to 1.8v before running the u-boot. This reset in USDHC driver causes a short 2.2v pulse on CMD pin. Fix this issue by not reset VSELECT to 0 when 1.8v flag is set. Signed-off-by: Ye Li Signed-off-by: Peng Fan --- drivers/mmc/fsl_esdhc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index cca57f8a79..71c62f4233 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -647,7 +647,11 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) esdhc_write32(®s->clktunectrlstatus, 0x0); /* Put VEND_SPEC to default value */ - esdhc_write32(®s->vendorspec, VENDORSPEC_INIT); + if (priv->vs18_enable) + esdhc_write32(®s->vendorspec, (VENDORSPEC_INIT | + ESDHC_VENDORSPEC_VSELECT)); + else + esdhc_write32(®s->vendorspec, VENDORSPEC_INIT); /* Disable DLL_CTRL delay line */ esdhc_write32(®s->dllctrl, 0x0); @@ -676,9 +680,6 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) /* Set timout to the maximum value */ esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); - if (priv->vs18_enable) - esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); - return 0; }