public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration
@ 2026-03-04 10:26 Wei-Cheng Chen
  2026-03-24  9:11 ` Jon Hunter
  0 siblings, 1 reply; 5+ messages in thread
From: Wei-Cheng Chen @ 2026-03-04 10:26 UTC (permalink / raw)
  To: jckuo, vkoul, neil.armstrong, thierry.reding, jonathanh
  Cc: linux-phy, linux-tegra, linux-kernel, waynec, wtsai,
	Wei-Cheng Chen

From: Wayne Chang <waynec@nvidia.com>

The existing code reads a single hs_term_range_adj value from bit field
[10:7] of FUSE_SKU_CALIB_0 and applies it to all USB2 pads uniformly.
However, on SoCs that support per-pad termination, each pad has its own
hs_term_range_adj field: pad 0 in FUSE_SKU_CALIB_0[10:7], and pads 1-3
in FUSE_USB_CALIB_EXT_0 at bit offsets [8:5], [12:9], and [16:13]
respectively.

Fix the calibration by reading per-pad values from the appropriate fuse
registers. For SoCs that do not support per-pad termination, replicate
pad 0's value to all pads to maintain existing behavior.

Add a has_per_pad_term flag to the SoC data to indicate whether per-pad
termination values are available in FUSE_USB_CALIB_EXT_0.

Fixes: 1ef535c6ba8e ("phy: tegra: xusb: Add Tegra194 support")
Cc: stable@vger.kernel.org
Signed-off-by: Wayne Chang <waynec@nvidia.com>
Signed-off-by: Wei-Cheng Chen <weichengc@nvidia.com>
---
 drivers/phy/tegra/xusb-tegra186.c | 33 ++++++++++++++++++++++++-------
 drivers/phy/tegra/xusb.h          |  1 +
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
index bec9616c4a2..4452e73fb82 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -20,8 +20,8 @@
 /* FUSE USB_CALIB registers */
 #define HS_CURR_LEVEL_PADX_SHIFT(x)	((x) ? (11 + (x - 1) * 6) : 0)
 #define HS_CURR_LEVEL_PAD_MASK		0x3f
-#define HS_TERM_RANGE_ADJ_SHIFT		7
-#define HS_TERM_RANGE_ADJ_MASK		0xf
+#define HS_TERM_RANGE_ADJ_PADX_SHIFT(x)	((x) ? (5 + (x - 1) * 4) : 7)
+#define HS_TERM_RANGE_ADJ_PAD_MASK	0xf
 #define HS_SQUELCH_SHIFT		29
 #define HS_SQUELCH_MASK			0x7
 
@@ -253,7 +253,7 @@
 struct tegra_xusb_fuse_calibration {
 	u32 *hs_curr_level;
 	u32 hs_squelch;
-	u32 hs_term_range_adj;
+	u32 *hs_term_range_adj;
 	u32 rpd_ctrl;
 };
 
@@ -930,7 +930,7 @@ static int tegra186_utmi_phy_power_on(struct phy *phy)
 
 	value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
 	value &= ~TERM_RANGE_ADJ(~0);
-	value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj);
+	value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj[index]);
 	value &= ~RPD_CTRL(~0);
 	value |= RPD_CTRL(priv->calib.rpd_ctrl);
 	padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
@@ -1464,17 +1464,23 @@ static const char * const tegra186_usb3_functions[] = {
 static int
 tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
 {
+	const struct tegra_xusb_padctl_soc *soc = padctl->base.soc;
 	struct device *dev = padctl->base.dev;
 	unsigned int i, count;
 	u32 value, *level;
+	u32 *hs_term_range_adj;
 	int err;
 
-	count = padctl->base.soc->ports.usb2.count;
+	count = soc->ports.usb2.count;
 
 	level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
 	if (!level)
 		return -ENOMEM;
 
+	hs_term_range_adj = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
+	if (!hs_term_range_adj)
+		return -ENOMEM;
+
 	err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
 	if (err)
 		return dev_err_probe(dev, err,
@@ -1490,8 +1496,8 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
 
 	padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) &
 					HS_SQUELCH_MASK;
-	padctl->calib.hs_term_range_adj = (value >> HS_TERM_RANGE_ADJ_SHIFT) &
-						HS_TERM_RANGE_ADJ_MASK;
+	hs_term_range_adj[0] = (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(0)) &
+				HS_TERM_RANGE_ADJ_PAD_MASK;
 
 	err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
 	if (err) {
@@ -1503,6 +1509,17 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
 
 	padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK;
 
+	for (i = 1; i < count; i++) {
+		if (soc->has_per_pad_term)
+			hs_term_range_adj[i] =
+				(value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(i)) &
+				HS_TERM_RANGE_ADJ_PAD_MASK;
+		else
+			hs_term_range_adj[i] = hs_term_range_adj[0];
+	}
+
+	padctl->calib.hs_term_range_adj = hs_term_range_adj;
+
 	return 0;
 }
 
@@ -1708,6 +1725,7 @@ const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc = {
 	.num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
 	.supports_gen2 = true,
 	.poll_trk_completed = true,
+	.has_per_pad_term = true,
 };
 EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
 
@@ -1732,6 +1750,7 @@ const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc = {
 	.trk_hw_mode = false,
 	.trk_update_on_idle = true,
 	.supports_lp_cfg_en = true,
+	.has_per_pad_term = true,
 };
 EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
 #endif
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index d2b5f956513..810b410672f 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -436,6 +436,7 @@ struct tegra_xusb_padctl_soc {
 	bool trk_hw_mode;
 	bool trk_update_on_idle;
 	bool supports_lp_cfg_en;
+	bool has_per_pad_term;
 };
 
 struct tegra_xusb_padctl {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration
  2026-03-04 10:26 [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration Wei-Cheng Chen
@ 2026-03-24  9:11 ` Jon Hunter
  2026-04-17  8:23   ` Jon Hunter
  0 siblings, 1 reply; 5+ messages in thread
From: Jon Hunter @ 2026-03-24  9:11 UTC (permalink / raw)
  To: Wei-Cheng Chen, jckuo, vkoul, neil.armstrong, thierry.reding
  Cc: linux-phy, linux-tegra, linux-kernel, waynec, wtsai


On 04/03/2026 10:26, Wei-Cheng Chen wrote:
> From: Wayne Chang <waynec@nvidia.com>
> 
> The existing code reads a single hs_term_range_adj value from bit field
> [10:7] of FUSE_SKU_CALIB_0 and applies it to all USB2 pads uniformly.
> However, on SoCs that support per-pad termination, each pad has its own
> hs_term_range_adj field: pad 0 in FUSE_SKU_CALIB_0[10:7], and pads 1-3
> in FUSE_USB_CALIB_EXT_0 at bit offsets [8:5], [12:9], and [16:13]
> respectively.
> 
> Fix the calibration by reading per-pad values from the appropriate fuse
> registers. For SoCs that do not support per-pad termination, replicate
> pad 0's value to all pads to maintain existing behavior.
> 
> Add a has_per_pad_term flag to the SoC data to indicate whether per-pad
> termination values are available in FUSE_USB_CALIB_EXT_0.
> 
> Fixes: 1ef535c6ba8e ("phy: tegra: xusb: Add Tegra194 support")
> Cc: stable@vger.kernel.org
> Signed-off-by: Wayne Chang <waynec@nvidia.com>
> Signed-off-by: Wei-Cheng Chen <weichengc@nvidia.com>
> ---
>   drivers/phy/tegra/xusb-tegra186.c | 33 ++++++++++++++++++++++++-------
>   drivers/phy/tegra/xusb.h          |  1 +
>   2 files changed, 27 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
> index bec9616c4a2..4452e73fb82 100644
> --- a/drivers/phy/tegra/xusb-tegra186.c
> +++ b/drivers/phy/tegra/xusb-tegra186.c
> @@ -20,8 +20,8 @@
>   /* FUSE USB_CALIB registers */
>   #define HS_CURR_LEVEL_PADX_SHIFT(x)	((x) ? (11 + (x - 1) * 6) : 0)
>   #define HS_CURR_LEVEL_PAD_MASK		0x3f
> -#define HS_TERM_RANGE_ADJ_SHIFT		7
> -#define HS_TERM_RANGE_ADJ_MASK		0xf
> +#define HS_TERM_RANGE_ADJ_PADX_SHIFT(x)	((x) ? (5 + (x - 1) * 4) : 7)
> +#define HS_TERM_RANGE_ADJ_PAD_MASK	0xf
>   #define HS_SQUELCH_SHIFT		29
>   #define HS_SQUELCH_MASK			0x7
>   
> @@ -253,7 +253,7 @@
>   struct tegra_xusb_fuse_calibration {
>   	u32 *hs_curr_level;
>   	u32 hs_squelch;
> -	u32 hs_term_range_adj;
> +	u32 *hs_term_range_adj;
>   	u32 rpd_ctrl;
>   };
>   
> @@ -930,7 +930,7 @@ static int tegra186_utmi_phy_power_on(struct phy *phy)
>   
>   	value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
>   	value &= ~TERM_RANGE_ADJ(~0);
> -	value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj);
> +	value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj[index]);
>   	value &= ~RPD_CTRL(~0);
>   	value |= RPD_CTRL(priv->calib.rpd_ctrl);
>   	padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
> @@ -1464,17 +1464,23 @@ static const char * const tegra186_usb3_functions[] = {
>   static int
>   tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
>   {
> +	const struct tegra_xusb_padctl_soc *soc = padctl->base.soc;
>   	struct device *dev = padctl->base.dev;
>   	unsigned int i, count;
>   	u32 value, *level;
> +	u32 *hs_term_range_adj;
>   	int err;
>   
> -	count = padctl->base.soc->ports.usb2.count;
> +	count = soc->ports.usb2.count;
>   
>   	level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
>   	if (!level)
>   		return -ENOMEM;
>   
> +	hs_term_range_adj = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
> +	if (!hs_term_range_adj)
> +		return -ENOMEM;
> +
>   	err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
>   	if (err)
>   		return dev_err_probe(dev, err,
> @@ -1490,8 +1496,8 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
>   
>   	padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) &
>   					HS_SQUELCH_MASK;
> -	padctl->calib.hs_term_range_adj = (value >> HS_TERM_RANGE_ADJ_SHIFT) &
> -						HS_TERM_RANGE_ADJ_MASK;
> +	hs_term_range_adj[0] = (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(0)) &
> +				HS_TERM_RANGE_ADJ_PAD_MASK;
>   
>   	err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
>   	if (err) {
> @@ -1503,6 +1509,17 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
>   
>   	padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK;
>   
> +	for (i = 1; i < count; i++) {
> +		if (soc->has_per_pad_term)
> +			hs_term_range_adj[i] =
> +				(value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(i)) &
> +				HS_TERM_RANGE_ADJ_PAD_MASK;
> +		else
> +			hs_term_range_adj[i] = hs_term_range_adj[0];
> +	}
> +
> +	padctl->calib.hs_term_range_adj = hs_term_range_adj;
> +
>   	return 0;
>   }
>   
> @@ -1708,6 +1725,7 @@ const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc = {
>   	.num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
>   	.supports_gen2 = true,
>   	.poll_trk_completed = true,
> +	.has_per_pad_term = true,
>   };
>   EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
>   
> @@ -1732,6 +1750,7 @@ const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc = {
>   	.trk_hw_mode = false,
>   	.trk_update_on_idle = true,
>   	.supports_lp_cfg_en = true,
> +	.has_per_pad_term = true,
>   };
>   EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
>   #endif
> diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
> index d2b5f956513..810b410672f 100644
> --- a/drivers/phy/tegra/xusb.h
> +++ b/drivers/phy/tegra/xusb.h
> @@ -436,6 +436,7 @@ struct tegra_xusb_padctl_soc {
>   	bool trk_hw_mode;
>   	bool trk_update_on_idle;
>   	bool supports_lp_cfg_en;
> +	bool has_per_pad_term;
>   };
>   
>   struct tegra_xusb_padctl {

Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Jon Hunter <jonathanh@nvidia.com>

Thanks
Jon

-- 
nvpublic


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration
  2026-03-24  9:11 ` Jon Hunter
@ 2026-04-17  8:23   ` Jon Hunter
  2026-04-29  9:10     ` Jon Hunter
  0 siblings, 1 reply; 5+ messages in thread
From: Jon Hunter @ 2026-04-17  8:23 UTC (permalink / raw)
  To: Wei-Cheng Chen, jckuo, vkoul, neil.armstrong, thierry.reding
  Cc: linux-phy, linux-tegra, linux-kernel, waynec, wtsai

Hi Vinod,

On 24/03/2026 09:11, Jon Hunter wrote:
> 
> On 04/03/2026 10:26, Wei-Cheng Chen wrote:
>> From: Wayne Chang <waynec@nvidia.com>
>>
>> The existing code reads a single hs_term_range_adj value from bit field
>> [10:7] of FUSE_SKU_CALIB_0 and applies it to all USB2 pads uniformly.
>> However, on SoCs that support per-pad termination, each pad has its own
>> hs_term_range_adj field: pad 0 in FUSE_SKU_CALIB_0[10:7], and pads 1-3
>> in FUSE_USB_CALIB_EXT_0 at bit offsets [8:5], [12:9], and [16:13]
>> respectively.
>>
>> Fix the calibration by reading per-pad values from the appropriate fuse
>> registers. For SoCs that do not support per-pad termination, replicate
>> pad 0's value to all pads to maintain existing behavior.
>>
>> Add a has_per_pad_term flag to the SoC data to indicate whether per-pad
>> termination values are available in FUSE_USB_CALIB_EXT_0.
>>
>> Fixes: 1ef535c6ba8e ("phy: tegra: xusb: Add Tegra194 support")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Wayne Chang <waynec@nvidia.com>
>> Signed-off-by: Wei-Cheng Chen <weichengc@nvidia.com>
>> ---
>>   drivers/phy/tegra/xusb-tegra186.c | 33 ++++++++++++++++++++++++-------
>>   drivers/phy/tegra/xusb.h          |  1 +
>>   2 files changed, 27 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/ 
>> xusb-tegra186.c
>> index bec9616c4a2..4452e73fb82 100644
>> --- a/drivers/phy/tegra/xusb-tegra186.c
>> +++ b/drivers/phy/tegra/xusb-tegra186.c
>> @@ -20,8 +20,8 @@
>>   /* FUSE USB_CALIB registers */
>>   #define HS_CURR_LEVEL_PADX_SHIFT(x)    ((x) ? (11 + (x - 1) * 6) : 0)
>>   #define HS_CURR_LEVEL_PAD_MASK        0x3f
>> -#define HS_TERM_RANGE_ADJ_SHIFT        7
>> -#define HS_TERM_RANGE_ADJ_MASK        0xf
>> +#define HS_TERM_RANGE_ADJ_PADX_SHIFT(x)    ((x) ? (5 + (x - 1) * 4) : 7)
>> +#define HS_TERM_RANGE_ADJ_PAD_MASK    0xf
>>   #define HS_SQUELCH_SHIFT        29
>>   #define HS_SQUELCH_MASK            0x7
>> @@ -253,7 +253,7 @@
>>   struct tegra_xusb_fuse_calibration {
>>       u32 *hs_curr_level;
>>       u32 hs_squelch;
>> -    u32 hs_term_range_adj;
>> +    u32 *hs_term_range_adj;
>>       u32 rpd_ctrl;
>>   };
>> @@ -930,7 +930,7 @@ static int tegra186_utmi_phy_power_on(struct phy 
>> *phy)
>>       value = padctl_readl(padctl, 
>> XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
>>       value &= ~TERM_RANGE_ADJ(~0);
>> -    value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj);
>> +    value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj[index]);
>>       value &= ~RPD_CTRL(~0);
>>       value |= RPD_CTRL(priv->calib.rpd_ctrl);
>>       padctl_writel(padctl, value, 
>> XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
>> @@ -1464,17 +1464,23 @@ static const char * const 
>> tegra186_usb3_functions[] = {
>>   static int
>>   tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl 
>> *padctl)
>>   {
>> +    const struct tegra_xusb_padctl_soc *soc = padctl->base.soc;
>>       struct device *dev = padctl->base.dev;
>>       unsigned int i, count;
>>       u32 value, *level;
>> +    u32 *hs_term_range_adj;
>>       int err;
>> -    count = padctl->base.soc->ports.usb2.count;
>> +    count = soc->ports.usb2.count;
>>       level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
>>       if (!level)
>>           return -ENOMEM;
>> +    hs_term_range_adj = devm_kcalloc(dev, count, sizeof(u32), 
>> GFP_KERNEL);
>> +    if (!hs_term_range_adj)
>> +        return -ENOMEM;
>> +
>>       err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
>>       if (err)
>>           return dev_err_probe(dev, err,
>> @@ -1490,8 +1496,8 @@ tegra186_xusb_read_fuse_calibration(struct 
>> tegra186_xusb_padctl *padctl)
>>       padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) &
>>                       HS_SQUELCH_MASK;
>> -    padctl->calib.hs_term_range_adj = (value >> 
>> HS_TERM_RANGE_ADJ_SHIFT) &
>> -                        HS_TERM_RANGE_ADJ_MASK;
>> +    hs_term_range_adj[0] = (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(0)) &
>> +                HS_TERM_RANGE_ADJ_PAD_MASK;
>>       err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
>>       if (err) {
>> @@ -1503,6 +1509,17 @@ tegra186_xusb_read_fuse_calibration(struct 
>> tegra186_xusb_padctl *padctl)
>>       padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK;
>> +    for (i = 1; i < count; i++) {
>> +        if (soc->has_per_pad_term)
>> +            hs_term_range_adj[i] =
>> +                (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(i)) &
>> +                HS_TERM_RANGE_ADJ_PAD_MASK;
>> +        else
>> +            hs_term_range_adj[i] = hs_term_range_adj[0];
>> +    }
>> +
>> +    padctl->calib.hs_term_range_adj = hs_term_range_adj;
>> +
>>       return 0;
>>   }
>> @@ -1708,6 +1725,7 @@ const struct tegra_xusb_padctl_soc 
>> tegra194_xusb_padctl_soc = {
>>       .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
>>       .supports_gen2 = true,
>>       .poll_trk_completed = true,
>> +    .has_per_pad_term = true,
>>   };
>>   EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
>> @@ -1732,6 +1750,7 @@ const struct tegra_xusb_padctl_soc 
>> tegra234_xusb_padctl_soc = {
>>       .trk_hw_mode = false,
>>       .trk_update_on_idle = true,
>>       .supports_lp_cfg_en = true,
>> +    .has_per_pad_term = true,
>>   };
>>   EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
>>   #endif
>> diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
>> index d2b5f956513..810b410672f 100644
>> --- a/drivers/phy/tegra/xusb.h
>> +++ b/drivers/phy/tegra/xusb.h
>> @@ -436,6 +436,7 @@ struct tegra_xusb_padctl_soc {
>>       bool trk_hw_mode;
>>       bool trk_update_on_idle;
>>       bool supports_lp_cfg_en;
>> +    bool has_per_pad_term;
>>   };
>>   struct tegra_xusb_padctl {
> 
> Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
> Tested-by: Jon Hunter <jonathanh@nvidia.com>

I guess this was missed for Linux v7.1. If you don't have any concerns 
can you pick this up for v7.2 once v7.1-rc1 is out?

Thanks!
Jon

-- 
nvpublic


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration
  2026-04-17  8:23   ` Jon Hunter
@ 2026-04-29  9:10     ` Jon Hunter
  2026-05-03 16:40       ` Vinod Koul
  0 siblings, 1 reply; 5+ messages in thread
From: Jon Hunter @ 2026-04-29  9:10 UTC (permalink / raw)
  To: Wei-Cheng Chen, jckuo, vkoul, neil.armstrong, thierry.reding
  Cc: linux-phy, linux-tegra, linux-kernel, waynec, wtsai

Hi Vinod,

On 17/04/2026 09:23, Jon Hunter wrote:
> Hi Vinod,
> 
> On 24/03/2026 09:11, Jon Hunter wrote:
>>
>> On 04/03/2026 10:26, Wei-Cheng Chen wrote:
>>> From: Wayne Chang <waynec@nvidia.com>
>>>
>>> The existing code reads a single hs_term_range_adj value from bit field
>>> [10:7] of FUSE_SKU_CALIB_0 and applies it to all USB2 pads uniformly.
>>> However, on SoCs that support per-pad termination, each pad has its own
>>> hs_term_range_adj field: pad 0 in FUSE_SKU_CALIB_0[10:7], and pads 1-3
>>> in FUSE_USB_CALIB_EXT_0 at bit offsets [8:5], [12:9], and [16:13]
>>> respectively.
>>>
>>> Fix the calibration by reading per-pad values from the appropriate fuse
>>> registers. For SoCs that do not support per-pad termination, replicate
>>> pad 0's value to all pads to maintain existing behavior.
>>>
>>> Add a has_per_pad_term flag to the SoC data to indicate whether per-pad
>>> termination values are available in FUSE_USB_CALIB_EXT_0.
>>>
>>> Fixes: 1ef535c6ba8e ("phy: tegra: xusb: Add Tegra194 support")
>>> Cc: stable@vger.kernel.org
>>> Signed-off-by: Wayne Chang <waynec@nvidia.com>
>>> Signed-off-by: Wei-Cheng Chen <weichengc@nvidia.com>
>>> ---
>>>   drivers/phy/tegra/xusb-tegra186.c | 33 ++++++++++++++++++++++++-------
>>>   drivers/phy/tegra/xusb.h          |  1 +
>>>   2 files changed, 27 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/ 
>>> xusb-tegra186.c
>>> index bec9616c4a2..4452e73fb82 100644
>>> --- a/drivers/phy/tegra/xusb-tegra186.c
>>> +++ b/drivers/phy/tegra/xusb-tegra186.c
>>> @@ -20,8 +20,8 @@
>>>   /* FUSE USB_CALIB registers */
>>>   #define HS_CURR_LEVEL_PADX_SHIFT(x)    ((x) ? (11 + (x - 1) * 6) : 0)
>>>   #define HS_CURR_LEVEL_PAD_MASK        0x3f
>>> -#define HS_TERM_RANGE_ADJ_SHIFT        7
>>> -#define HS_TERM_RANGE_ADJ_MASK        0xf
>>> +#define HS_TERM_RANGE_ADJ_PADX_SHIFT(x)    ((x) ? (5 + (x - 1) * 
>>> 4) : 7)
>>> +#define HS_TERM_RANGE_ADJ_PAD_MASK    0xf
>>>   #define HS_SQUELCH_SHIFT        29
>>>   #define HS_SQUELCH_MASK            0x7
>>> @@ -253,7 +253,7 @@
>>>   struct tegra_xusb_fuse_calibration {
>>>       u32 *hs_curr_level;
>>>       u32 hs_squelch;
>>> -    u32 hs_term_range_adj;
>>> +    u32 *hs_term_range_adj;
>>>       u32 rpd_ctrl;
>>>   };
>>> @@ -930,7 +930,7 @@ static int tegra186_utmi_phy_power_on(struct phy 
>>> *phy)
>>>       value = padctl_readl(padctl, 
>>> XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
>>>       value &= ~TERM_RANGE_ADJ(~0);
>>> -    value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj);
>>> +    value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj[index]);
>>>       value &= ~RPD_CTRL(~0);
>>>       value |= RPD_CTRL(priv->calib.rpd_ctrl);
>>>       padctl_writel(padctl, value, 
>>> XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
>>> @@ -1464,17 +1464,23 @@ static const char * const 
>>> tegra186_usb3_functions[] = {
>>>   static int
>>>   tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl 
>>> *padctl)
>>>   {
>>> +    const struct tegra_xusb_padctl_soc *soc = padctl->base.soc;
>>>       struct device *dev = padctl->base.dev;
>>>       unsigned int i, count;
>>>       u32 value, *level;
>>> +    u32 *hs_term_range_adj;
>>>       int err;
>>> -    count = padctl->base.soc->ports.usb2.count;
>>> +    count = soc->ports.usb2.count;
>>>       level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
>>>       if (!level)
>>>           return -ENOMEM;
>>> +    hs_term_range_adj = devm_kcalloc(dev, count, sizeof(u32), 
>>> GFP_KERNEL);
>>> +    if (!hs_term_range_adj)
>>> +        return -ENOMEM;
>>> +
>>>       err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
>>>       if (err)
>>>           return dev_err_probe(dev, err,
>>> @@ -1490,8 +1496,8 @@ tegra186_xusb_read_fuse_calibration(struct 
>>> tegra186_xusb_padctl *padctl)
>>>       padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) &
>>>                       HS_SQUELCH_MASK;
>>> -    padctl->calib.hs_term_range_adj = (value >> 
>>> HS_TERM_RANGE_ADJ_SHIFT) &
>>> -                        HS_TERM_RANGE_ADJ_MASK;
>>> +    hs_term_range_adj[0] = (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(0)) &
>>> +                HS_TERM_RANGE_ADJ_PAD_MASK;
>>>       err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
>>>       if (err) {
>>> @@ -1503,6 +1509,17 @@ tegra186_xusb_read_fuse_calibration(struct 
>>> tegra186_xusb_padctl *padctl)
>>>       padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & 
>>> RPD_CTRL_MASK;
>>> +    for (i = 1; i < count; i++) {
>>> +        if (soc->has_per_pad_term)
>>> +            hs_term_range_adj[i] =
>>> +                (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(i)) &
>>> +                HS_TERM_RANGE_ADJ_PAD_MASK;
>>> +        else
>>> +            hs_term_range_adj[i] = hs_term_range_adj[0];
>>> +    }
>>> +
>>> +    padctl->calib.hs_term_range_adj = hs_term_range_adj;
>>> +
>>>       return 0;
>>>   }
>>> @@ -1708,6 +1725,7 @@ const struct tegra_xusb_padctl_soc 
>>> tegra194_xusb_padctl_soc = {
>>>       .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
>>>       .supports_gen2 = true,
>>>       .poll_trk_completed = true,
>>> +    .has_per_pad_term = true,
>>>   };
>>>   EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
>>> @@ -1732,6 +1750,7 @@ const struct tegra_xusb_padctl_soc 
>>> tegra234_xusb_padctl_soc = {
>>>       .trk_hw_mode = false,
>>>       .trk_update_on_idle = true,
>>>       .supports_lp_cfg_en = true,
>>> +    .has_per_pad_term = true,
>>>   };
>>>   EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
>>>   #endif
>>> diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
>>> index d2b5f956513..810b410672f 100644
>>> --- a/drivers/phy/tegra/xusb.h
>>> +++ b/drivers/phy/tegra/xusb.h
>>> @@ -436,6 +436,7 @@ struct tegra_xusb_padctl_soc {
>>>       bool trk_hw_mode;
>>>       bool trk_update_on_idle;
>>>       bool supports_lp_cfg_en;
>>> +    bool has_per_pad_term;
>>>   };
>>>   struct tegra_xusb_padctl {
>>
>> Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
>> Tested-by: Jon Hunter <jonathanh@nvidia.com>
> 
> I guess this was missed for Linux v7.1. If you don't have any concerns 
> can you pick this up for v7.2 once v7.1-rc1 is out?

OK to pick this up now? It would be great to get this into -next.

Thanks!
Jon

-- 
nvpublic


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration
  2026-04-29  9:10     ` Jon Hunter
@ 2026-05-03 16:40       ` Vinod Koul
  0 siblings, 0 replies; 5+ messages in thread
From: Vinod Koul @ 2026-05-03 16:40 UTC (permalink / raw)
  To: Jon Hunter
  Cc: Wei-Cheng Chen, jckuo, neil.armstrong, thierry.reding, linux-phy,
	linux-tegra, linux-kernel, waynec, wtsai

On 29-04-26, 10:10, Jon Hunter wrote:

> > > > diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
> > > > index d2b5f956513..810b410672f 100644
> > > > --- a/drivers/phy/tegra/xusb.h
> > > > +++ b/drivers/phy/tegra/xusb.h
> > > > @@ -436,6 +436,7 @@ struct tegra_xusb_padctl_soc {
> > > >       bool trk_hw_mode;
> > > >       bool trk_update_on_idle;
> > > >       bool supports_lp_cfg_en;
> > > > +    bool has_per_pad_term;
> > > >   };
> > > >   struct tegra_xusb_padctl {
> > > 
> > > Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
> > > Tested-by: Jon Hunter <jonathanh@nvidia.com>
> > 
> > I guess this was missed for Linux v7.1. If you don't have any concerns
> > can you pick this up for v7.2 once v7.1-rc1 is out?
> 
> OK to pick this up now? It would be great to get this into -next.

Sure I can do that, can Wei-Cheng please rebase this on rc1 and send

-- 
~Vinod

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-05-03 16:41 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-04 10:26 [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration Wei-Cheng Chen
2026-03-24  9:11 ` Jon Hunter
2026-04-17  8:23   ` Jon Hunter
2026-04-29  9:10     ` Jon Hunter
2026-05-03 16:40       ` Vinod Koul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox