From: Jon Hunter <jonathanh@nvidia.com>
To: Wei-Cheng Chen <weichengc@nvidia.com>,
jckuo@nvidia.com, vkoul@kernel.org, neil.armstrong@linaro.org,
thierry.reding@gmail.com
Cc: linux-phy@lists.infradead.org, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, waynec@nvidia.com,
wtsai@nvidia.com
Subject: Re: [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration
Date: Wed, 29 Apr 2026 10:10:50 +0100 [thread overview]
Message-ID: <77285dd6-e240-4944-a034-a4bc3acf4052@nvidia.com> (raw)
In-Reply-To: <fde997f6-c0a8-430b-8e45-38a490e683ad@nvidia.com>
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
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
WARNING: multiple messages have this Message-ID (diff)
From: Jon Hunter <jonathanh@nvidia.com>
To: Wei-Cheng Chen <weichengc@nvidia.com>,
jckuo@nvidia.com, vkoul@kernel.org, neil.armstrong@linaro.org,
thierry.reding@gmail.com
Cc: linux-phy@lists.infradead.org, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, waynec@nvidia.com,
wtsai@nvidia.com
Subject: Re: [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration
Date: Wed, 29 Apr 2026 10:10:50 +0100 [thread overview]
Message-ID: <77285dd6-e240-4944-a034-a4bc3acf4052@nvidia.com> (raw)
In-Reply-To: <fde997f6-c0a8-430b-8e45-38a490e683ad@nvidia.com>
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
next prev parent reply other threads:[~2026-04-29 9:11 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-04 10:26 [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration Wei-Cheng Chen
2026-03-04 10:26 ` Wei-Cheng Chen
2026-03-24 9:11 ` Jon Hunter
2026-03-24 9:11 ` Jon Hunter
2026-04-17 8:23 ` Jon Hunter
2026-04-17 8:23 ` Jon Hunter
2026-04-29 9:10 ` Jon Hunter [this message]
2026-04-29 9:10 ` Jon Hunter
2026-05-03 16:40 ` Vinod Koul
2026-05-03 16:40 ` Vinod Koul
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=77285dd6-e240-4944-a034-a4bc3acf4052@nvidia.com \
--to=jonathanh@nvidia.com \
--cc=jckuo@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-phy@lists.infradead.org \
--cc=linux-tegra@vger.kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=thierry.reding@gmail.com \
--cc=vkoul@kernel.org \
--cc=waynec@nvidia.com \
--cc=weichengc@nvidia.com \
--cc=wtsai@nvidia.com \
/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.