[PATCH v3 06/20] mmc: am654_sdhci: Add support for input tap delay
Jaehoon Chung
jh80.chung at samsung.com
Mon Jan 25 23:21:54 CET 2021
On 1/21/21 9:40 PM, Aswath Govindraju wrote:
> From: Faiz Abbas <faiz_abbas at ti.com>
>
> DLL need only be enabled for speed modes and clock frequencies at or
> above 50 MHz. For speed modes that don't enable the DLL, we need to
> configure a static input delay value. This involves reading an optional
> itap-del-sel-* value from the device tree and configuring it for the
> appropriate speed mode.
>
> Therefore, move all dll configurations to their own functions and gate it
> with 50 MHz speed and a minimum mode. If both these conditions are not
> satisfied then configure delay chain modes.
>
> Signed-off-by: Faiz Abbas <faiz_abbas at ti.com>
> Signed-off-by: Aswath Govindraju <a-govindraju at ti.com>
Reviewed-by: Jaehoon Chung <jh80.chung at samsung.com>
Best Regards,
Jaehoon Chung
> ---
> drivers/mmc/am654_sdhci.c | 241 +++++++++++++++++++++++++-------------
> 1 file changed, 161 insertions(+), 80 deletions(-)
>
> diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
> index 5790fa3d0dbf..b82558254ebb 100644
> --- a/drivers/mmc/am654_sdhci.c
> +++ b/drivers/mmc/am654_sdhci.c
> @@ -62,6 +62,16 @@
> #define CALDONE_MASK BIT(CALDONE_SHIFT)
> #define RETRIM_SHIFT 17
> #define RETRIM_MASK BIT(RETRIM_SHIFT)
> +#define SELDLYTXCLK_SHIFT 17
> +#define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT)
> +#define SELDLYRXCLK_SHIFT 16
> +#define SELDLYRXCLK_MASK BIT(SELDLYRXCLK_SHIFT)
> +#define ITAPDLYSEL_SHIFT 0
> +#define ITAPDLYSEL_MASK GENMASK(4, 0)
> +#define ITAPDLYENA_SHIFT 8
> +#define ITAPDLYENA_MASK BIT(ITAPDLYENA_SHIFT)
> +#define ITAPCHGWIN_SHIFT 9
> +#define ITAPCHGWIN_MASK BIT(ITAPCHGWIN_SHIFT)
>
> #define DRIVER_STRENGTH_50_OHM 0x0
> #define DRIVER_STRENGTH_33_OHM 0x1
> @@ -70,6 +80,7 @@
> #define DRIVER_STRENGTH_40_OHM 0x4
>
> #define AM654_SDHCI_MIN_FREQ 400000
> +#define CLOCK_TOO_SLOW_HZ 50000000
>
> struct am654_sdhci_plat {
> struct mmc_config cfg;
> @@ -77,6 +88,7 @@ struct am654_sdhci_plat {
> struct regmap *base;
> bool non_removable;
> u32 otap_del_sel[MMC_MODES_END];
> + u32 itap_del_sel[MMC_MODES_END];
> u32 trm_icp;
> u32 drv_strength;
> u32 strb_sel;
> @@ -89,22 +101,45 @@ struct am654_sdhci_plat {
> };
>
> struct timing_data {
> - const char *binding;
> + const char *otap_binding;
> + const char *itap_binding;
> u32 capability;
> };
>
> static const struct timing_data td[] = {
> - [MMC_LEGACY] = {"ti,otap-del-sel-legacy", 0},
> - [MMC_HS] = {"ti,otap-del-sel-mmc-hs", MMC_CAP(MMC_HS)},
> - [SD_HS] = {"ti,otap-del-sel-sd-hs", MMC_CAP(SD_HS)},
> - [UHS_SDR12] = {"ti,otap-del-sel-sdr12", MMC_CAP(UHS_SDR12)},
> - [UHS_SDR25] = {"ti,otap-del-sel-sdr25", MMC_CAP(UHS_SDR25)},
> - [UHS_SDR50] = {"ti,otap-del-sel-sdr50", MMC_CAP(UHS_SDR50)},
> - [UHS_SDR104] = {"ti,otap-del-sel-sdr104", MMC_CAP(UHS_SDR104)},
> - [UHS_DDR50] = {"ti,otap-del-sel-ddr50", MMC_CAP(UHS_DDR50)},
> - [MMC_DDR_52] = {"ti,otap-del-sel-ddr52", MMC_CAP(MMC_DDR_52)},
> - [MMC_HS_200] = {"ti,otap-del-sel-hs200", MMC_CAP(MMC_HS_200)},
> - [MMC_HS_400] = {"ti,otap-del-sel-hs400", MMC_CAP(MMC_HS_400)},
> + [MMC_LEGACY] = {"ti,otap-del-sel-legacy",
> + "ti,itap-del-sel-legacy",
> + 0},
> + [MMC_HS] = {"ti,otap-del-sel-mmc-hs",
> + "ti,itap-del-sel-mms-hs",
> + MMC_CAP(MMC_HS)},
> + [SD_HS] = {"ti,otap-del-sel-sd-hs",
> + "ti,itap-del-sel-sd-hs",
> + MMC_CAP(SD_HS)},
> + [UHS_SDR12] = {"ti,otap-del-sel-sdr12",
> + "ti,itap-del-sel-sdr12",
> + MMC_CAP(UHS_SDR12)},
> + [UHS_SDR25] = {"ti,otap-del-sel-sdr25",
> + "ti,itap-del-sel-sdr25",
> + MMC_CAP(UHS_SDR25)},
> + [UHS_SDR50] = {"ti,otap-del-sel-sdr50",
> + NULL,
> + MMC_CAP(UHS_SDR50)},
> + [UHS_SDR104] = {"ti,otap-del-sel-sdr104",
> + NULL,
> + MMC_CAP(UHS_SDR104)},
> + [UHS_DDR50] = {"ti,otap-del-sel-ddr50",
> + NULL,
> + MMC_CAP(UHS_DDR50)},
> + [MMC_DDR_52] = {"ti,otap-del-sel-ddr52",
> + "ti,itap-del-sel-ddr52",
> + MMC_CAP(MMC_DDR_52)},
> + [MMC_HS_200] = {"ti,otap-del-sel-hs200",
> + NULL,
> + MMC_CAP(MMC_HS_200)},
> + [MMC_HS_400] = {"ti,otap-del-sel-hs400",
> + NULL,
> + MMC_CAP(MMC_HS_400)},
> };
>
> struct am654_driver_data {
> @@ -127,12 +162,99 @@ static void am654_sdhci_set_control_reg(struct sdhci_host *host)
> sdhci_set_uhs_timing(host);
> }
>
> +static int am654_sdhci_setup_dll(struct am654_sdhci_plat *plat,
> + unsigned int speed)
> +{
> + int sel50, sel100, freqsel;
> + u32 mask, val;
> + int ret;
> +
> + /* Disable delay chain mode */
> + regmap_update_bits(plat->base, PHY_CTRL5,
> + SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
> +
> + if (plat->flags & FREQSEL_2_BIT) {
> + switch (speed) {
> + case 200000000:
> + sel50 = 0;
> + sel100 = 0;
> + break;
> + case 100000000:
> + sel50 = 0;
> + sel100 = 1;
> + break;
> + default:
> + sel50 = 1;
> + sel100 = 0;
> + }
> +
> + /* Configure PHY DLL frequency */
> + mask = SEL50_MASK | SEL100_MASK;
> + val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
> + regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
> + } else {
> + switch (speed) {
> + case 200000000:
> + freqsel = 0x0;
> + break;
> + default:
> + freqsel = 0x4;
> + }
> + regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
> + freqsel << FREQSEL_SHIFT);
> + }
> +
> + /* Configure DLL TRIM */
> + mask = DLL_TRIM_ICP_MASK;
> + val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
> +
> + /* Configure DLL driver strength */
> + mask |= DR_TY_MASK;
> + val |= plat->drv_strength << DR_TY_SHIFT;
> + regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
> +
> + /* Enable DLL */
> + regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
> + 0x1 << ENDLL_SHIFT);
> + /*
> + * Poll for DLL ready. Use a one second timeout.
> + * Works in all experiments done so far
> + */
> + ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
> + val & DLLRDY_MASK, 1000, 1000000);
> +
> + return ret;
> +}
> +
> +static void am654_sdhci_write_itapdly(struct am654_sdhci_plat *plat,
> + u32 itapdly)
> +{
> + /* Set ITAPCHGWIN before writing to ITAPDLY */
> + regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK,
> + 1 << ITAPCHGWIN_SHIFT);
> + regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYSEL_MASK,
> + itapdly << ITAPDLYSEL_SHIFT);
> + regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
> +}
> +
> +static void am654_sdhci_setup_delay_chain(struct am654_sdhci_plat *plat,
> + int mode)
> +{
> + u32 mask, val;
> +
> + val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
> + mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
> + regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
> +
> + am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode]);
> +}
> +
> static int am654_sdhci_set_ios_post(struct sdhci_host *host)
> {
> struct udevice *dev = host->mmc->dev;
> struct am654_sdhci_plat *plat = dev_get_plat(dev);
> unsigned int speed = host->mmc->clock;
> - int sel50, sel100, freqsel;
> + int mode = host->mmc->selected_mode;
> u32 otap_del_sel;
> u32 mask, val;
> int ret;
> @@ -148,75 +270,29 @@ static int am654_sdhci_set_ios_post(struct sdhci_host *host)
> sdhci_set_clock(host->mmc, speed);
>
> /* switch phy back on */
> - if (speed > AM654_SDHCI_MIN_FREQ) {
> - otap_del_sel = plat->otap_del_sel[host->mmc->selected_mode];
> - mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
> - val = (1 << OTAPDLYENA_SHIFT) |
> - (otap_del_sel << OTAPDLYSEL_SHIFT);
> -
> - /* Write to STRBSEL for HS400 speed mode */
> - if (host->mmc->selected_mode == MMC_HS_400) {
> - if (plat->flags & STRBSEL_4_BIT)
> - mask |= STRBSEL_4BIT_MASK;
> - else
> - mask |= STRBSEL_8BIT_MASK;
> -
> - val |= plat->strb_sel << STRBSEL_SHIFT;
> - }
> + otap_del_sel = plat->otap_del_sel[mode];
> + mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
> + val = (1 << OTAPDLYENA_SHIFT) |
> + (otap_del_sel << OTAPDLYSEL_SHIFT);
>
> - regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
> -
> - if (plat->flags & FREQSEL_2_BIT) {
> - switch (speed) {
> - case 200000000:
> - sel50 = 0;
> - sel100 = 0;
> - break;
> - case 100000000:
> - sel50 = 0;
> - sel100 = 1;
> - break;
> - default:
> - sel50 = 1;
> - sel100 = 0;
> - }
> -
> - /* Configure PHY DLL frequency */
> - mask = SEL50_MASK | SEL100_MASK;
> - val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
> - regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
> - } else {
> - switch (speed) {
> - case 200000000:
> - freqsel = 0x0;
> - break;
> - default:
> - freqsel = 0x4;
> - }
> - regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
> - freqsel << FREQSEL_SHIFT);
> - }
> + /* Write to STRBSEL for HS400 speed mode */
> + if (host->mmc->selected_mode == MMC_HS_400) {
> + if (plat->flags & STRBSEL_4_BIT)
> + mask |= STRBSEL_4BIT_MASK;
> + else
> + mask |= STRBSEL_8BIT_MASK;
>
> - /* Configure DLL TRIM */
> - mask = DLL_TRIM_ICP_MASK;
> - val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
> -
> - /* Configure DLL driver strength */
> - mask |= DR_TY_MASK;
> - val |= plat->drv_strength << DR_TY_SHIFT;
> - regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
> -
> - /* Enable DLL */
> - regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
> - 0x1 << ENDLL_SHIFT);
> - /*
> - * Poll for DLL ready. Use a one second timeout.
> - * Works in all experiments done so far
> - */
> - ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
> - val & DLLRDY_MASK, 1000, 1000000);
> + val |= plat->strb_sel << STRBSEL_SHIFT;
> + }
> +
> + regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
> +
> + if (mode > UHS_SDR25 && speed >= CLOCK_TOO_SLOW_HZ) {
> + ret = am654_sdhci_setup_dll(plat, speed);
> if (ret)
> return ret;
> + } else {
> + am654_sdhci_setup_delay_chain(plat, mode);
> }
>
> return 0;
> @@ -354,15 +430,20 @@ static int sdhci_am654_get_otap_delay(struct udevice *dev,
> * value is not found
> */
> for (i = MMC_HS; i <= MMC_HS_400; i++) {
> - ret = dev_read_u32(dev, td[i].binding, &plat->otap_del_sel[i]);
> + ret = dev_read_u32(dev, td[i].otap_binding,
> + &plat->otap_del_sel[i]);
> if (ret) {
> - dev_dbg(dev, "Couldn't find %s\n", td[i].binding);
> + dev_dbg(dev, "Couldn't find %s\n", td[i].otap_binding);
> /*
> * Remove the corresponding capability
> * if an otap-del-sel value is not found
> */
> cfg->host_caps &= ~td[i].capability;
> }
> +
> + if (td[i].itap_binding)
> + dev_read_u32(dev, td[i].itap_binding,
> + &plat->itap_del_sel[i]);
> }
>
> return 0;
>
More information about the U-Boot
mailing list