[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