From: Kaustabh Chakraborty Date: Fri, 17 Oct 2025 15:24:12 +0000 (+0530) Subject: mmc: exynos_dw_mmc: add support for MMC HS200 and HS400 modes X-Git-Tag: v2026.01-rc2~55^2~12 X-Git-Url: http://git.openpandora.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bbe3b9fa09220c97b09c0d6e96a0d416865389da;p=pandora-u-boot.git mmc: exynos_dw_mmc: add support for MMC HS200 and HS400 modes MMC HS200 and HS400 modes are already supported by the Exynos DW-MMC driver in mainline Linux. Using that as reference, add support in the U-Boot driver. The maximum frequency was capped to 50000000, increase it to 200000000, which is the required frequency for HS200/HS400. Moreover, add MMC_MODE_HS200 and MMC_MODE_HS400 to host capailities. These changes allow both host and card to recognize support for HS200/HS400. This change also includes a new ops function, namely execute_tuning. Implementing it would mean that we can no longer rely on the default ops provided by dw_mmc.c, thus a new ops instance is created with proper fields. The execute_tuning function is modeled after the one available in the Linux driver. Signed-off-by: Kaustabh Chakraborty Reviewed-by: Peng Fan Signed-off-by: Peng Fan --- diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 0597cd7f0c7..66d2b8bdafd 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -18,13 +18,18 @@ #include #define DWMMC_MAX_CH_NUM 4 -#define DWMMC_MAX_FREQ 52000000 +#define DWMMC_MAX_FREQ 200000000 #define DWMMC_MIN_FREQ 400000 #define DWMMC_MMC0_SDR_TIMING_VAL 0x03030001 #define DWMMC_MMC2_SDR_TIMING_VAL 0x03020001 #define EXYNOS4412_FIXED_CIU_CLK_DIV 4 +/* CLKSEL register defines */ +#define CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) +#define CLKSEL_UP_SAMPLE(x, y) (((x) & ~CLKSEL_CCLK_SAMPLE(7)) |\ + CLKSEL_CCLK_SAMPLE(y)) + /* Quirks */ #define DWMCI_QUIRK_DISABLE_SMU BIT(0) @@ -166,8 +171,9 @@ static unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) u8 clk_div; int err; - /* Should be double rate for DDR mode */ - if (host->mmc->selected_mode == MMC_DDR_52 && host->mmc->bus_width == 8) + /* Should be double rate for DDR or HS mode */ + if ((host->mmc->selected_mode == MMC_DDR_52 && host->mmc->bus_width == 8) || + host->mmc->selected_mode == MMC_HS_400) freq *= 2; clk_div = exynos_dwmmc_get_ciu_div(host); @@ -282,6 +288,72 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) return 0; } +static int exynos_dwmmc_get_best_clksmpl(u8 candidates) +{ + u8 i; + + for (i = 0; i < 8; i++) { + candidates = (candidates >> 1) | (candidates << 7); /* ror */ + if ((candidates & 0xc7) == 0xc7) + return i; + } + + for (i = 0; i < 8; i++) { + candidates = (candidates >> 1) | (candidates << 7); /* ror */ + if ((candidates & 0x83) == 0x83) + return i; + } + + /* + * If no valid clock sample values are found, use the first + * canditate bit for clock sample value. + */ + for (i = 0; i < 8; i++) { + candidates = (candidates >> 1) | (candidates << 7); /* ror */ + if ((candidates & 0x1) == 0x1) + return i; + } + + return -EIO; +} + +static int exynos_dwmmc_execute_tuning(struct udevice *dev, u32 opcode) +{ + struct dwmci_exynos_priv_data *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + struct mmc *mmc = mmc_get_mmc_dev(dev); + u8 start_smpl, smpl, candidates = 0; + u32 clksel; + int ret; + + clksel = dwmci_readl(host, priv->chip->clksel); + start_smpl = CLKSEL_CCLK_SAMPLE(clksel); + + do { + dwmci_writel(host, DWMCI_TMOUT, ~0); + + /* move to the next clksmpl */ + smpl = (clksel + 1) & 0x7; + clksel = CLKSEL_UP_SAMPLE(clksel, smpl); + dwmci_writel(host, priv->chip->clksel, clksel); + + if (!mmc_send_tuning(mmc, opcode)) + candidates |= (1 << smpl); + + } while (start_smpl != smpl); + + ret = exynos_dwmmc_get_best_clksmpl(candidates); + if (ret < 0) { + printf("DWMMC%d: No candidates for clksmpl\n", host->dev_index); + return ret; + } + + dwmci_writel(host, priv->chip->clksel, + CLKSEL_UP_SAMPLE(clksel, ret)); + + return 0; +} + static int exynos_dwmmc_probe(struct udevice *dev) { struct exynos_mmc_plat *plat = dev_get_plat(dev); @@ -321,7 +393,7 @@ static int exynos_dwmmc_probe(struct udevice *dev) host->name = dev->name; host->board_init = exynos_dwmci_board_init; - host->caps = MMC_MODE_DDR_52MHz; + host->caps = MMC_MODE_DDR_52MHz | MMC_MODE_HS200 | MMC_MODE_HS400; host->clksel = exynos_dwmci_clksel; host->get_mmc_clk = exynos_dwmci_get_clk; @@ -391,6 +463,12 @@ static const struct udevice_id exynos_dwmmc_ids[] = { { } }; +struct dm_mmc_ops exynos_dwmmc_ops = { + .send_cmd = dwmci_send_cmd, + .set_ios = dwmci_set_ios, + .execute_tuning = exynos_dwmmc_execute_tuning, +}; + U_BOOT_DRIVER(exynos_dwmmc_drv) = { .name = "exynos_dwmmc", .id = UCLASS_MMC, @@ -398,7 +476,7 @@ U_BOOT_DRIVER(exynos_dwmmc_drv) = { .of_to_plat = exynos_dwmmc_of_to_plat, .bind = exynos_dwmmc_bind, .probe = exynos_dwmmc_probe, - .ops = &dm_dwmci_ops, + .ops = &exynos_dwmmc_ops, .priv_auto = sizeof(struct dwmci_exynos_priv_data), .plat_auto = sizeof(struct exynos_mmc_plat), };