From cf1f7355aea0b4640a244a75b3c18835e7f7b2bb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 5 Jan 2023 15:19:08 +0100 Subject: cmd: mmc: Expand bkops handling Add more capable "bkops" command which allows enabling and disabling both manual and automatic bkops. The existing 'mmc bkops-enable' subcommand is poorly named to cover all the possibilities, hence the new-ish subcommand. Note that both commands are wrappers around the same common code. Signed-off-by: Marek Vasut Reviewed-by: Simon Glass Reviewed-by: Jaehoon Chung --- cmd/mmc.c | 49 +++++++++++++++++++++++++++++++++++++++++-------- drivers/mmc/mmc.c | 14 +++++++++----- include/mmc.h | 14 +++++++++++--- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/cmd/mmc.c b/cmd/mmc.c index c79d940..94deb9a 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -1020,16 +1020,12 @@ static int do_mmc_setdsr(struct cmd_tbl *cmdtp, int flag, } #ifdef CONFIG_CMD_BKOPS_ENABLE -static int do_mmc_bkops_enable(struct cmd_tbl *cmdtp, int flag, - int argc, char *const argv[]) +static int mmc_bkops_common(char *device, bool autobkops, bool enable) { - int dev; struct mmc *mmc; + int dev; - if (argc != 2) - return CMD_RET_USAGE; - - dev = dectoul(argv[1], NULL); + dev = dectoul(device, NULL); mmc = init_mmc_device(dev, false); if (!mmc) @@ -1040,7 +1036,41 @@ static int do_mmc_bkops_enable(struct cmd_tbl *cmdtp, int flag, return CMD_RET_FAILURE; } - return mmc_set_bkops_enable(mmc); + return mmc_set_bkops_enable(mmc, autobkops, enable); +} + +static int do_mmc_bkops(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + bool autobkops, enable; + + if (argc != 4) + return CMD_RET_USAGE; + + if (!strcmp(argv[2], "manual")) + autobkops = false; + else if (!strcmp(argv[2], "auto")) + autobkops = true; + else + return CMD_RET_FAILURE; + + if (!strcmp(argv[3], "disable")) + enable = false; + else if (!strcmp(argv[3], "enable")) + enable = true; + else + return CMD_RET_FAILURE; + + return mmc_bkops_common(argv[1], autobkops, enable); +} + +static int do_mmc_bkops_enable(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + if (argc != 2) + return CMD_RET_USAGE; + + return mmc_bkops_common(argv[1], false, true); } #endif @@ -1102,6 +1132,7 @@ static struct cmd_tbl cmd_mmc[] = { U_BOOT_CMD_MKENT(setdsr, 2, 0, do_mmc_setdsr, "", ""), #ifdef CONFIG_CMD_BKOPS_ENABLE U_BOOT_CMD_MKENT(bkops-enable, 2, 0, do_mmc_bkops_enable, "", ""), + U_BOOT_CMD_MKENT(bkops, 4, 0, do_mmc_bkops, "", ""), #endif }; @@ -1188,6 +1219,8 @@ U_BOOT_CMD( #ifdef CONFIG_CMD_BKOPS_ENABLE "mmc bkops-enable - enable background operations handshake on device\n" " WARNING: This is a write-once setting.\n" + "mmc bkops [auto|manual] [enable|disable]\n" + " - configure background operations handshake on device\n" #endif ); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 210703e..afbc497 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -3127,9 +3127,10 @@ int mmc_init_device(int num) #endif #ifdef CONFIG_CMD_BKOPS_ENABLE -int mmc_set_bkops_enable(struct mmc *mmc) +int mmc_set_bkops_enable(struct mmc *mmc, bool autobkops, bool enable) { int err; + u32 bit = autobkops ? BIT(1) : BIT(0); ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); err = mmc_send_ext_csd(mmc, ext_csd); @@ -3143,18 +3144,21 @@ int mmc_set_bkops_enable(struct mmc *mmc) return -EMEDIUMTYPE; } - if (ext_csd[EXT_CSD_BKOPS_EN] & 0x1) { + if (enable && (ext_csd[EXT_CSD_BKOPS_EN] & bit)) { puts("Background operations already enabled\n"); return 0; } - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, 1); + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, + enable ? bit : 0); if (err) { - puts("Failed to enable manual background operations\n"); + printf("Failed to %sable manual background operations\n", + enable ? "en" : "dis"); return err; } - puts("Enabled manual background operations\n"); + printf("%sabled %s background operations\n", + enable ? "En" : "Dis", autobkops ? "auto" : "manual"); return 0; } diff --git a/include/mmc.h b/include/mmc.h index 571fa62..36dd841 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -892,9 +892,17 @@ int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk, int mmc_rpmb_route_frames(struct mmc *mmc, void *req, unsigned long reqlen, void *rsp, unsigned long rsplen); -#ifdef CONFIG_CMD_BKOPS_ENABLE -int mmc_set_bkops_enable(struct mmc *mmc); -#endif +/** + * mmc_set_bkops_enable() - enable background operations + * @param mmc Pointer to a MMC device struct + * @param autobkops Enable automatic bkops, not manual bkops + * @param enable Enable bkops, not disable + * + * Enable or disable automatic or manual background operation of the eMMC. + * + * Return: 0 on success, <0 on error. + */ +int mmc_set_bkops_enable(struct mmc *mmc, bool autobkops, bool enable); /** * Start device initialization and return immediately; it does not block on -- cgit v1.1 From 14ef4c7be5b5881e2c50642ae15f0f12c28fdf60 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Tue, 10 Jan 2023 04:31:21 -0700 Subject: mmc: zynq_sdhci: Add support for eMMC5.1 for Versal NET platform Add support for eMMC 5.1 for Versal NET platform - Add new compatible string(xlnx,versal-net-5.1-emmc). - Add CONFIG_ARCH_VERSAL_NET condition wherever required. - Add DLL and Delay Chain mode support - Add input and output tap delays for eMMC. - Add Strobe select tap for HS400 mode. Signed-off-by: Ashok Reddy Soma --- drivers/mmc/zynq_sdhci.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 284 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index be4075c..4f0dde3 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * (C) Copyright 2013 - 2015 Xilinx, Inc. + * (C) Copyright 2013 - 2022, Xilinx, Inc. + * (C) Copyright 2022, Advanced Micro Devices, Inc. * * Xilinx Zynq SD Host Controller Interface */ @@ -16,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +50,41 @@ #define SD0_OTAPDLYSEL_MASK GENMASK(5, 0) #define SD1_OTAPDLYSEL_MASK GENMASK(21, 16) +#define MIN_PHY_CLK_HZ 50000000 + +#define PHY_CTRL_REG1 0x270 +#define PHY_CTRL_ITAPDLY_ENA_MASK BIT(0) +#define PHY_CTRL_ITAPDLY_SEL_MASK GENMASK(5, 1) +#define PHY_CTRL_ITAPDLY_SEL_SHIFT 1 +#define PHY_CTRL_ITAP_CHG_WIN_MASK BIT(6) +#define PHY_CTRL_OTAPDLY_ENA_MASK BIT(8) +#define PHY_CTRL_OTAPDLY_SEL_MASK GENMASK(15, 12) +#define PHY_CTRL_OTAPDLY_SEL_SHIFT 12 +#define PHY_CTRL_STRB_SEL_MASK GENMASK(23, 16) +#define PHY_CTRL_STRB_SEL_SHIFT 16 +#define PHY_CTRL_TEST_CTRL_MASK GENMASK(31, 24) + +#define PHY_CTRL_REG2 0x274 +#define PHY_CTRL_EN_DLL_MASK BIT(0) +#define PHY_CTRL_DLL_RDY_MASK BIT(1) +#define PHY_CTRL_FREQ_SEL_MASK GENMASK(6, 4) +#define PHY_CTRL_FREQ_SEL_SHIFT 4 +#define PHY_CTRL_SEL_DLY_TX_MASK BIT(16) +#define PHY_CTRL_SEL_DLY_RX_MASK BIT(17) +#define FREQSEL_200M_170M 0x0 +#define FREQSEL_170M_140M 0x1 +#define FREQSEL_140M_110M 0x2 +#define FREQSEL_110M_80M 0x3 +#define FREQSEL_80M_50M 0x4 +#define FREQSEL_275M_250M 0x5 +#define FREQSEL_250M_225M 0x6 +#define FREQSEL_225M_200M 0x7 +#define PHY_DLL_TIMEOUT_MS 100 + +#define VERSAL_NET_EMMC_ICLK_PHASE_DDR52_DLY_CHAIN 39 +#define VERSAL_NET_EMMC_ICLK_PHASE_DDR52_DLL 146 +#define VERSAL_NET_PHY_CTRL_STRB90_STRB180_VAL 0X77 + struct arasan_sdhci_clk_data { int clk_phase_in[MMC_TIMING_MMC_HS400 + 1]; int clk_phase_out[MMC_TIMING_MMC_HS400 + 1]; @@ -64,6 +101,7 @@ struct arasan_sdhci_priv { u32 node_id; u8 bank; u8 no_1p8; + bool internal_phy_reg; struct reset_ctl_bulk resets; }; @@ -84,7 +122,7 @@ __weak int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id) return 1; } -#if defined(CONFIG_ARCH_ZYNQMP) || defined(CONFIG_ARCH_VERSAL) +#if defined(CONFIG_ARCH_ZYNQMP) || defined(CONFIG_ARCH_VERSAL) || defined(CONFIG_ARCH_VERSAL_NET) /* Default settings for ZynqMP Clock Phases */ static const u32 zynqmp_iclk_phases[] = {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}; @@ -97,6 +135,12 @@ static const u32 versal_iclk_phases[] = {0, 132, 132, 0, 132, static const u32 versal_oclk_phases[] = {0, 60, 48, 0, 48, 72, 90, 36, 60, 90, 0}; +/* Default settings for versal-net eMMC Clock Phases */ +static const u32 versal_net_emmc_iclk_phases[] = {0, 0, 0, 0, 0, 0, 0, 0, 39, + 0, 0}; +static const u32 versal_net_emmc_oclk_phases[] = {0, 113, 0, 0, 0, 0, 0, 0, + 113, 79, 45}; + static const u8 mode2timing[] = { [MMC_LEGACY] = MMC_TIMING_LEGACY, [MMC_HS] = MMC_TIMING_MMC_HS, @@ -111,6 +155,121 @@ static const u8 mode2timing[] = { [MMC_HS_200] = MMC_TIMING_MMC_HS200, }; +#if defined(CONFIG_ARCH_VERSAL_NET) +/** + * arasan_phy_set_delaychain - Set eMMC delay chain based Input/Output clock + * + * @host: Pointer to the sdhci_host structure + * @enable: Enable or disable Delay chain based Tx and Rx clock + * Return: None + * + * Enable or disable eMMC delay chain based Input and Output clock in + * PHY_CTRL_REG2 + */ +static void arasan_phy_set_delaychain(struct sdhci_host *host, bool enable) +{ + u32 reg; + + reg = sdhci_readw(host, PHY_CTRL_REG2); + if (enable) + reg |= PHY_CTRL_SEL_DLY_TX_MASK | PHY_CTRL_SEL_DLY_RX_MASK; + else + reg &= ~(PHY_CTRL_SEL_DLY_TX_MASK | PHY_CTRL_SEL_DLY_RX_MASK); + + sdhci_writew(host, reg, PHY_CTRL_REG2); +} + +/** + * arasan_phy_set_dll - Set eMMC DLL clock + * + * @host: Pointer to the sdhci_host structure + * @enable: Enable or disable DLL clock + * Return: 0 if success or timeout error + * + * Enable or disable eMMC DLL clock in PHY_CTRL_REG2. When DLL enable is + * set, wait till DLL is locked + */ +static int arasan_phy_set_dll(struct sdhci_host *host, bool enable) +{ + u32 reg; + + reg = sdhci_readw(host, PHY_CTRL_REG2); + if (enable) + reg |= PHY_CTRL_EN_DLL_MASK; + else + reg &= ~PHY_CTRL_EN_DLL_MASK; + + sdhci_writew(host, reg, PHY_CTRL_REG2); + + /* If DLL is disabled return success */ + if (!enable) + return 0; + + /* If DLL is enabled wait till DLL loop is locked, which is + * indicated by dll_rdy bit(bit1) in PHY_CTRL_REG2 + */ + return readl_relaxed_poll_timeout(host->ioaddr + PHY_CTRL_REG2, reg, + (reg & PHY_CTRL_DLL_RDY_MASK), + 1000 * PHY_DLL_TIMEOUT_MS); +} + +/** + * arasan_phy_dll_set_freq - Select frequency range of DLL for eMMC + * + * @host: Pointer to the sdhci_host structure + * @clock: clock value + * Return: None + * + * Set frequency range bits based on the selected clock for eMMC + */ +static void arasan_phy_dll_set_freq(struct sdhci_host *host, int clock) +{ + u32 reg, freq_sel, freq; + + freq = DIV_ROUND_CLOSEST(clock, 1000000); + if (freq <= 200 && freq > 170) + freq_sel = FREQSEL_200M_170M; + else if (freq <= 170 && freq > 140) + freq_sel = FREQSEL_170M_140M; + else if (freq <= 140 && freq > 110) + freq_sel = FREQSEL_140M_110M; + else if (freq <= 110 && freq > 80) + freq_sel = FREQSEL_110M_80M; + else + freq_sel = FREQSEL_80M_50M; + + reg = sdhci_readw(host, PHY_CTRL_REG2); + reg &= ~PHY_CTRL_FREQ_SEL_MASK; + reg |= (freq_sel << PHY_CTRL_FREQ_SEL_SHIFT); + sdhci_writew(host, reg, PHY_CTRL_REG2); +} + +static int arasan_sdhci_config_dll(struct sdhci_host *host, unsigned int clock, bool enable) +{ + struct mmc *mmc = (struct mmc *)host->mmc; + struct arasan_sdhci_priv *priv = dev_get_priv(mmc->dev); + + if (enable) { + if (priv->internal_phy_reg && clock >= MIN_PHY_CLK_HZ && enable) + arasan_phy_set_dll(host, 1); + return 0; + } + + if (priv->internal_phy_reg && clock >= MIN_PHY_CLK_HZ) { + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + arasan_phy_set_dll(host, 0); + arasan_phy_set_delaychain(host, 0); + arasan_phy_dll_set_freq(host, clock); + return 0; + } + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + arasan_phy_set_delaychain(host, 1); + + return 0; +} +#endif + static inline int arasan_zynqmp_set_in_tapdelay(u32 node_id, u32 itap_delay) { int ret; @@ -585,6 +744,101 @@ static int sdhci_versal_sampleclk_set_phase(struct sdhci_host *host, return 0; } +/** + * sdhci_versal_net_emmc_sdcardclk_set_phase - Set eMMC Output Clock Tap Delays + * + * @host: Pointer to the sdhci_host structure. + * @degrees: The clock phase shift between 0 - 359. + * Return: 0 + * + * Set eMMC Output Clock Tap Delays for Output path + */ +static int sdhci_versal_net_emmc_sdcardclk_set_phase(struct sdhci_host *host, int degrees) +{ + struct mmc *mmc = (struct mmc *)host->mmc; + int timing = mode2timing[mmc->selected_mode]; + u8 tap_delay, tap_max = 0; + u32 regval; + + switch (timing) { + case MMC_TIMING_MMC_HS: + case MMC_TIMING_MMC_DDR52: + tap_max = 16; + break; + case MMC_TIMING_MMC_HS200: + case MMC_TIMING_MMC_HS400: + /* For 200MHz clock, 32 Taps are available */ + tap_max = 32; + break; + default: + break; + } + + tap_delay = (degrees * tap_max) / 360; + /* Set the Clock Phase */ + if (tap_delay) { + regval = sdhci_readl(host, PHY_CTRL_REG1); + regval |= PHY_CTRL_OTAPDLY_ENA_MASK; + sdhci_writel(host, regval, PHY_CTRL_REG1); + regval &= ~PHY_CTRL_OTAPDLY_SEL_MASK; + regval |= tap_delay << PHY_CTRL_OTAPDLY_SEL_SHIFT; + sdhci_writel(host, regval, PHY_CTRL_REG1); + } + + return 0; +} + +/** + * sdhci_versal_net_emmc_sampleclk_set_phase - Set eMMC Input Clock Tap Delays + * + * @host: Pointer to the sdhci_host structure. + * @degrees: The clock phase shift between 0 - 359. + * Return: 0 + * + * Set eMMC Input Clock Tap Delays for Input path. If HS400 is selected, + * set strobe90 and strobe180 in PHY_CTRL_REG1. + */ +static int sdhci_versal_net_emmc_sampleclk_set_phase(struct sdhci_host *host, int degrees) +{ + struct mmc *mmc = (struct mmc *)host->mmc; + int timing = mode2timing[mmc->selected_mode]; + u8 tap_delay, tap_max = 0; + u32 regval; + + switch (timing) { + case MMC_TIMING_MMC_HS: + case MMC_TIMING_MMC_DDR52: + tap_max = 32; + break; + case MMC_TIMING_MMC_HS400: + /* Strobe select tap point for strb90 and strb180 */ + regval = sdhci_readl(host, PHY_CTRL_REG1); + regval &= ~PHY_CTRL_STRB_SEL_MASK; + regval |= VERSAL_NET_PHY_CTRL_STRB90_STRB180_VAL << PHY_CTRL_STRB_SEL_SHIFT; + sdhci_writel(host, regval, PHY_CTRL_REG1); + break; + default: + break; + } + + tap_delay = (degrees * tap_max) / 360; + /* Set the Clock Phase */ + if (tap_delay) { + regval = sdhci_readl(host, PHY_CTRL_REG1); + regval |= PHY_CTRL_ITAP_CHG_WIN_MASK; + sdhci_writel(host, regval, PHY_CTRL_REG1); + regval |= PHY_CTRL_ITAPDLY_ENA_MASK; + sdhci_writel(host, regval, PHY_CTRL_REG1); + regval &= ~PHY_CTRL_ITAPDLY_SEL_MASK; + regval |= tap_delay << PHY_CTRL_ITAPDLY_SEL_SHIFT; + sdhci_writel(host, regval, PHY_CTRL_REG1); + regval &= ~PHY_CTRL_ITAP_CHG_WIN_MASK; + sdhci_writel(host, regval, PHY_CTRL_REG1); + } + + return 0; +} + static int arasan_sdhci_set_tapdelay(struct sdhci_host *host) { struct arasan_sdhci_priv *priv = dev_get_priv(host->mmc->dev); @@ -616,6 +870,19 @@ static int arasan_sdhci_set_tapdelay(struct sdhci_host *host) ret = sdhci_versal_sdcardclk_set_phase(host, oclk_phase); if (ret) return ret; + } else if (IS_ENABLED(CONFIG_ARCH_VERSAL_NET) && + device_is_compatible(dev, "xlnx,versal-net-5.1-emmc")) { + if (mmc->clock >= MIN_PHY_CLK_HZ) + if (iclk_phase == VERSAL_NET_EMMC_ICLK_PHASE_DDR52_DLY_CHAIN) + iclk_phase = VERSAL_NET_EMMC_ICLK_PHASE_DDR52_DLL; + + ret = sdhci_versal_net_emmc_sampleclk_set_phase(host, iclk_phase); + if (ret) + return ret; + + ret = sdhci_versal_net_emmc_sdcardclk_set_phase(host, oclk_phase); + if (ret) + return ret; } return 0; @@ -678,6 +945,14 @@ static void arasan_dt_parse_clk_phases(struct udevice *dev) } } + if (IS_ENABLED(CONFIG_ARCH_VERSAL_NET) && + device_is_compatible(dev, "xlnx,versal-net-5.1-emmc")) { + for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { + clk_data->clk_phase_in[i] = versal_net_emmc_iclk_phases[i]; + clk_data->clk_phase_out[i] = versal_net_emmc_oclk_phases[i]; + } + } + arasan_dt_read_clk_phase(dev, MMC_TIMING_LEGACY, "clk-phase-legacy"); arasan_dt_read_clk_phase(dev, MMC_TIMING_MMC_HS, @@ -706,6 +981,9 @@ static const struct sdhci_ops arasan_ops = { .platform_execute_tuning = &arasan_sdhci_execute_tuning, .set_delay = &arasan_sdhci_set_tapdelay, .set_control_reg = &sdhci_set_control_reg, +#if defined(CONFIG_ARCH_VERSAL_NET) + .config_dll = &arasan_sdhci_config_dll, +#endif }; #endif @@ -822,6 +1100,8 @@ static int arasan_sdhci_probe(struct udevice *dev) } } #endif + if (device_is_compatible(dev, "xlnx,versal-net-5.1-emmc")) + priv->internal_phy_reg = true; ret = clk_get_by_index(dev, 0, &clk); if (ret < 0) { @@ -905,7 +1185,7 @@ static int arasan_sdhci_of_to_plat(struct udevice *dev) priv->host->name = dev->name; -#if defined(CONFIG_ARCH_ZYNQMP) || defined(CONFIG_ARCH_VERSAL) +#if defined(CONFIG_ARCH_ZYNQMP) || defined(CONFIG_ARCH_VERSAL) || defined(CONFIG_ARCH_VERSAL_NET) priv->host->ops = &arasan_ops; arasan_dt_parse_clk_phases(dev); #endif @@ -933,6 +1213,7 @@ static int arasan_sdhci_bind(struct udevice *dev) static const struct udevice_id arasan_sdhci_ids[] = { { .compatible = "arasan,sdhci-8.9a" }, + { .compatible = "xlnx,versal-net-5.1-emmc" }, { } }; -- cgit v1.1 From 6f5bb9913ca54fe0a3e45f83458eb71103e22ef7 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Tue, 10 Jan 2023 04:31:22 -0700 Subject: mmc: sdhci: Check and call config_dll callback functions Check if the low level driver supports config_dll callback function and call it if it does. Call with dll disable before calling set_clock and with dll enable after it. Signed-off-by: Ashok Reddy Soma --- drivers/mmc/sdhci.c | 16 ++++++++++++++++ include/sdhci.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index a80ad83..181ab9b 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -396,6 +396,14 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock) } } + if (host->ops && host->ops->config_dll) { + ret = host->ops->config_dll(host, clock, false); + if (ret) { + printf("%s: Error while configuring dll\n", __func__); + return ret; + } + } + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { /* * Check if the Host Controller supports Programmable Clock @@ -439,6 +447,14 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if (host->ops && host->ops->set_clock) host->ops->set_clock(host, div); + if (host->ops && host->ops->config_dll) { + ret = host->ops->config_dll(host, clock, true); + if (ret) { + printf("%s: Error while configuring dll\n", __func__); + return ret; + } + } + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; diff --git a/include/sdhci.h b/include/sdhci.h index 24b4599..64a3d30 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -272,6 +272,8 @@ struct sdhci_ops { void (*set_clock)(struct sdhci_host *host, u32 div); int (*platform_execute_tuning)(struct mmc *host, u8 opcode); int (*set_delay)(struct sdhci_host *host); + /* Callback function to set DLL clock configuration */ + int (*config_dll)(struct sdhci_host *host, u32 clock, bool enable); int (*deferred_probe)(struct sdhci_host *host); /** -- cgit v1.1 From 386f5d367329a202abe71fd790e8ce4598b30e09 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Tue, 10 Jan 2023 04:31:23 -0700 Subject: mmc: sdhci: Enable HS400 support if available in caps HS400 is indicated in bit63 of capability register in few IP's. Add a quirk to check this and add HS400 to host capabilities. Signed-off-by: Ashok Reddy Soma --- drivers/mmc/sdhci.c | 4 ++++ include/sdhci.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 181ab9b..c6b250b 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -995,6 +995,10 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, cfg->host_caps |= MMC_CAP(UHS_SDR50); } + if ((host->quirks & SDHCI_QUIRK_CAPS_BIT63_FOR_HS400) && + (caps_1 & SDHCI_SUPPORT_HS400)) + cfg->host_caps |= MMC_CAP(MMC_HS_400); + if (caps_1 & SDHCI_SUPPORT_DDR50) cfg->host_caps |= MMC_CAP(UHS_DDR50); diff --git a/include/sdhci.h b/include/sdhci.h index 64a3d30..70fefca 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -189,6 +189,7 @@ #define SDHCI_SUPPORT_SDR50 0x00000001 #define SDHCI_SUPPORT_SDR104 0x00000002 #define SDHCI_SUPPORT_DDR50 0x00000004 +#define SDHCI_SUPPORT_HS400 BIT(31) #define SDHCI_USE_SDR50_TUNING 0x00002000 #define SDHCI_CLOCK_MUL_MASK 0x00FF0000 @@ -248,6 +249,8 @@ #define SDHCI_QUIRK_USE_WIDE8 (1 << 8) #define SDHCI_QUIRK_NO_1_8_V (1 << 9) #define SDHCI_QUIRK_SUPPORT_SINGLE (1 << 10) +/* Capability register bit-63 indicates HS400 support */ +#define SDHCI_QUIRK_CAPS_BIT63_FOR_HS400 BIT(11) /* to make gcc happy */ struct sdhci_host; -- cgit v1.1 From a1f8abf4686065f46ac840e956a1aeb68d90d969 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Tue, 10 Jan 2023 04:31:24 -0700 Subject: mmc: zynq_sdhci: Add support and quirk for HS400 Add support for HS400 in mode2timing array. Add a quirk for Versal NET platform to indicate that HS400 is supported through bit63 of capability register. Signed-off-by: Ashok Reddy Soma --- drivers/mmc/zynq_sdhci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index 4f0dde3..91e309d 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -153,6 +153,7 @@ static const u8 mode2timing[] = { [UHS_DDR50] = MMC_TIMING_UHS_DDR50, [UHS_SDR104] = MMC_TIMING_UHS_SDR104, [MMC_HS_200] = MMC_TIMING_MMC_HS200, + [MMC_HS_400] = MMC_TIMING_MMC_HS400, }; #if defined(CONFIG_ARCH_VERSAL_NET) @@ -1133,6 +1134,10 @@ static int arasan_sdhci_probe(struct udevice *dev) if (priv->no_1p8) host->quirks |= SDHCI_QUIRK_NO_1_8_V; + if (CONFIG_IS_ENABLED(ARCH_VERSAL_NET) && + device_is_compatible(dev, "xlnx,versal-net-5.1-emmc")) + host->quirks |= SDHCI_QUIRK_CAPS_BIT63_FOR_HS400; + plat->cfg.f_max = CONFIG_ZYNQ_SDHCI_MAX_FREQ; ret = mmc_of_parse(dev, &plat->cfg); -- cgit v1.1