All of lore.kernel.org
 help / color / mirror / Atom feed
From: Adrian Hunter <adrian.hunter@intel.com>
To: <tze.yee.ng@altera.com>, Ulf Hansson <ulfh@kernel.org>,
	Tanmay Kathpalia <tanmay.kathpalia@altera.com>,
	<linux-mmc@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	Rob Herring <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>, <devicetree@vger.kernel.org>
Subject: Re: [PATCH 4/4] mmc: sdhci-cadence: read SD6HC DDR50 tuning from device tree
Date: Thu, 24 Sep 2026 09:40:46 +0300	[thread overview]
Message-ID: <4aafce14-2be7-46a6-9d5b-8df35646dc0a@intel.com> (raw)
In-Reply-To: <9440b973966df50c9f6c79055ec83ef3b634335a.1790074790.git.tze.yee.ng@altera.com>

On 22/09/2026 14:12, tze.yee.ng@altera.com wrote:
> From: Tze Yee Ng <tze.yee.ng@altera.com>
> 
> DDR50 has no CMD19 tuning, so the SD6HC read path relies on static PHY
> settings that need board/SoC characterisation. Read the read-DQS delay,
> read-DQS source and phony DQS assertion timing from the new
> cdns,ddr50-read-dqs-delay, cdns,ddr50-use-lpbk-dqs and
> cdns,ddr50-phony-dqs-timing DT properties at PHY probe, range-check them,

Firmware values are expected to be correct, and so are not validated.

> and apply them only in DDR50. When a property is absent the existing
> driver default is kept - for the phony DQS timing, the derived
> REBAR_PULSE_CYCLES-1.
> 
> Signed-off-by: Tze Yee Ng <tze.yee.ng@altera.com>
> ---
>  drivers/mmc/host/sdhci-cadence-phy-v6.c | 72 ++++++++++++++++++++++++-
>  1 file changed, 71 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mmc/host/sdhci-cadence-phy-v6.c b/drivers/mmc/host/sdhci-cadence-phy-v6.c
> index 84592ae42762..0f47fa62d894 100644
> --- a/drivers/mmc/host/sdhci-cadence-phy-v6.c
> +++ b/drivers/mmc/host/sdhci-cadence-phy-v6.c
> @@ -129,6 +129,11 @@ struct sdhci_cdns6_phy {
>  	/* Active delay element (ps); doubled when one SDMCLK requires > 256 steps */
>  	u32 delay_element;
>  
> +	/* DDR read-path overrides (SoC-specific) */
> +	s32 ddr_read_dqs_delay;
> +	s32 ddr_use_lpbk_dqs;
> +	s32 ddr_phony_dqs_timing;
> +
>  	/* PHY_DLL_SLAVE_CTRL register fields */
>  	u8 cp_read_dqs_cmd_delay;	/* bits [31:24] */
>  	u8 cp_clk_wrdqs_delay;		/* bits [23:16] */
> @@ -145,6 +150,7 @@ struct sdhci_cdns6_phy {
>  	/* PHY_DQS_TIMING register fields */
>  	bool cp_use_phony_dqs;		/* bit [20] */
>  	bool cp_use_phony_dqs_cmd;	/* bit [19] */
> +	bool cp_use_lpbk_dqs;		/* bit [21] */
>  
>  	/* PHY_CTRL register fields */
>  	u32 cp_phony_dqs_timing;
> @@ -523,6 +529,12 @@ static void sdhci_cdns6_phy_calc_dat_in(struct sdhci_cdns6_phy *phy)
>  	if (phy->mode == MMC_TIMING_MMC_HS200)
>  		phy->cp_read_dqs_delay = phy->hs200_tune_val;
>  
> +	if (phy->mode == MMC_TIMING_UHS_DDR50 && phy->ddr_read_dqs_delay >= 0)
> +		phy->cp_read_dqs_delay = phy->ddr_read_dqs_delay &
> +			SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_DELAY;

So here is unnecessarily assuming DT is providing a bad value.  It could just be:

	if (phy->mode == MMC_TIMING_UHS_DDR50 && phy->ddr_read_dqs_delay >= 0)
		phy->cp_read_dqs_delay = phy->ddr_read_dqs_delay;

> +
> +	phy->cp_use_lpbk_dqs = 1;
> +
>  	if (phy->sdhc_extended_rd_mode &&
>  	    (phy->mode == MMC_TIMING_UHS_DDR50 ||
>  	     phy->mode == MMC_TIMING_MMC_DDR52))
> @@ -530,6 +542,18 @@ static void sdhci_cdns6_phy_calc_dat_in(struct sdhci_cdns6_phy *phy)
>  	else
>  		phy->cp_phony_dqs_timing = 0;
>  
> +	if (phy->mode == MMC_TIMING_UHS_DDR50 && phy->ddr_use_lpbk_dqs >= 0)
> +		phy->cp_use_lpbk_dqs = phy->ddr_use_lpbk_dqs &
> +			FIELD_MAX(SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS);

Ditto

> +
> +	/*
> +	 * The phony DQS timing derived above depends on the board's SD flight
> +	 * time, so allow a DT override to re-position the fabricated strobe.
> +	 */
> +	if (phy->mode == MMC_TIMING_UHS_DDR50 && phy->ddr_phony_dqs_timing >= 0)
> +		phy->cp_phony_dqs_timing = phy->ddr_phony_dqs_timing &
> +			FIELD_MAX(SDHCI_CDNS6_PHY_CTRL_PHONY_DQS_TIMING);

Ditto

> +
>  	if (strobe_dat) {
>  		/* dqs loopback input via IO cell */
>  		hcsdclkadj += phy->iocell_input_delay;
> @@ -693,10 +717,11 @@ int sdhci_cdns6_phy_init(struct sdhci_cdns_priv *priv)
>  	sdhci_cdns6_dll_reset(priv, true);
>  
>  	reg = sdhci_cdns6_read_phy_reg(priv, SDHCI_CDNS6_PHY_DQS_TIMING_REG);
> +	reg &= ~SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS;
>  	reg &= ~SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS;
>  	reg &= ~SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD;
>  	reg |= SDHCI_CDNS6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS;
> -	reg |= SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS;
> +	reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS, phy->cp_use_lpbk_dqs);
>  	reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS, phy->cp_use_phony_dqs);
>  	reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD, phy->cp_use_phony_dqs_cmd);
>  	sdhci_cdns6_write_phy_reg(priv, SDHCI_CDNS6_PHY_DQS_TIMING_REG, reg);
> @@ -881,6 +906,7 @@ int sdhci_cdns6_phy_probe(struct platform_device *pdev, struct sdhci_cdns_priv *
>  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>  	struct sdhci_cdns6_phy *phy;
>  	unsigned long val;
> +	u32 prop;
>  	int ret;
>  
>  	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
> @@ -919,6 +945,50 @@ int sdhci_cdns6_phy_probe(struct platform_device *pdev, struct sdhci_cdns_priv *
>  
>  	phy->delay_element_org = phy->delay_element;
>  
> +	/*
> +	 * Optional DDR50 read-path tuning. These are board/card-characterised
> +	 * values with no CMD19 tuning in DDR50; absence keeps the driver
> +	 * default (-1 => not overridden).
> +	 */
> +	phy->ddr_read_dqs_delay = -1;
> +	if (!of_property_read_u32(dev->of_node, "cdns,ddr50-read-dqs-delay",
> +				  &prop)) {
> +		if (prop > SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_DELAY) {
> +			dev_warn(dev,
> +				 "cdns,ddr50-read-dqs-delay %u out of range, clamping to %lu\n",
> +				 prop,
> +				 (unsigned long)SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_DELAY);
> +			prop = SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_DELAY;
> +		}
> +		phy->ddr_read_dqs_delay = prop;
> +	}

If the unnecessary validation is dropped:

	phy->ddr_read_dqs_delay = -1;
	of_property_read_u32(dev->of_node, "cdns,ddr50-read-dqs-delay", &phy->ddr_read_dqs_delay);

etc

> +
> +	phy->ddr_use_lpbk_dqs = -1;
> +	if (!of_property_read_u32(dev->of_node, "cdns,ddr50-use-lpbk-dqs",
> +				  &prop)) {
> +		if (prop > FIELD_MAX(SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS)) {
> +			dev_warn(dev,
> +				 "cdns,ddr50-use-lpbk-dqs %u out of range, clamping to %lu\n",
> +				 prop,
> +				 (unsigned long)FIELD_MAX(SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS));
> +			prop = FIELD_MAX(SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS);
> +		}
> +		phy->ddr_use_lpbk_dqs = prop;
> +	}
> +
> +	phy->ddr_phony_dqs_timing = -1;
> +	if (!of_property_read_u32(dev->of_node, "cdns,ddr50-phony-dqs-timing",
> +				  &prop)) {
> +		if (prop > FIELD_MAX(SDHCI_CDNS6_PHY_CTRL_PHONY_DQS_TIMING)) {
> +			dev_warn(dev,
> +				 "cdns,ddr50-phony-dqs-timing %u out of range, clamping to %lu\n",
> +				 prop,
> +				 (unsigned long)FIELD_MAX(SDHCI_CDNS6_PHY_CTRL_PHONY_DQS_TIMING));
> +			prop = FIELD_MAX(SDHCI_CDNS6_PHY_CTRL_PHONY_DQS_TIMING);
> +		}
> +		phy->ddr_phony_dqs_timing = prop;
> +	}
> +
>  	priv->phy = phy;
>  
>  	return 0;


  reply	other threads:[~2026-09-24  6:56 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-22 11:12 [PATCH 0/4] mmc: sdhci-cadence: SD6HC DDR50 read-path tuning and fixes tze.yee.ng
2026-09-22 11:12 ` [PATCH 1/4] mmc: sdhci-cadence6: add PHY settle delay after tuning DLL re-lock tze.yee.ng
2026-09-24  6:41   ` Adrian Hunter
2026-09-30 11:11     ` NG, TZE YEE
2026-09-24  8:49   ` Kathpalia, Tanmay
2026-09-30 11:11     ` NG, TZE YEE
2026-09-26 10:44   ` Kathpalia, Tanmay
2026-09-30 12:05     ` NG, TZE YEE
2026-09-22 11:12 ` [PATCH 2/4] mmc: sdhci-cadence6: program PHONY_DQS_TIMING for extended-read DDR tze.yee.ng
2026-09-24  6:41   ` Adrian Hunter
2026-09-26 10:45   ` Kathpalia, Tanmay
2026-09-30 10:24     ` NG, TZE YEE
2026-09-22 11:12 ` [PATCH 3/4] dt-bindings: mmc: cdns,sdhci: add SD6HC DDR50 read-path tuning tze.yee.ng
2026-09-26 10:48   ` Kathpalia, Tanmay
2026-09-30 12:31     ` NG, TZE YEE
2026-09-30 13:16       ` NG, TZE YEE
2026-09-28  8:05   ` Krzysztof Kozlowski
2026-09-30 13:13     ` NG, TZE YEE
2026-09-22 11:12 ` [PATCH 4/4] mmc: sdhci-cadence: read SD6HC DDR50 tuning from device tree tze.yee.ng
2026-09-24  6:40   ` Adrian Hunter [this message]
2026-09-30 13:24     ` NG, TZE YEE

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4aafce14-2be7-46a6-9d5b-8df35646dc0a@intel.com \
    --to=adrian.hunter@intel.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=tanmay.kathpalia@altera.com \
    --cc=tze.yee.ng@altera.com \
    --cc=ulfh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.