]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
phy: tegra: xusb: Fix per-pad high-speed termination calibration
authorWayne Chang <waynec@nvidia.com>
Mon, 4 May 2026 03:33:05 +0000 (11:33 +0800)
committerVinod Koul <vkoul@kernel.org>
Sun, 10 May 2026 11:31:18 +0000 (17:01 +0530)
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>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Jon Hunter <jonathanh@nvidia.com>
Link: https://patch.msgid.link/20260504033305.2283145-1-weichengc@nvidia.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/phy/tegra/xusb-tegra186.c
drivers/phy/tegra/xusb.h

index 1ddf112659744bbb9d3dc3cd025f399eea98c42e..60156aea27071e8a4b7f642bd489074209f140d8 100644 (file)
@@ -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
 
 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
index cd277d0ed9e184f48e82a7f3f21907fe4abc0356..77609e54de66a987161848085666bb02619429df 100644 (file)
@@ -435,6 +435,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 {