mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
phy: tegra: xusb: Fix per-pad high-speed termination calibration
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: 1ef535c6ba ("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>
This commit is contained in:
parent
91ddf6f722
commit
da110228b5
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user