* [PATCH RFC v4 8/9] phy: qcom: qmp-pcie: Add Glymur Gen5x8 PHY config and match data
From: Qiang Yu @ 2026-05-19 5:47 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio
Cc: Qiang Yu, linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260518-link_mode_0519-v4-0-269cd73cc5d1@oss.qualcomm.com>
On Glymur, PCIe3 uses a single Gen5x8 QMP PHY hardware block that can
operate in two link topologies: x8 as one 8-lane PHY, or x4+x4 as two
independent 4-lane PHYs. Both topologies are served by the same DT node
since they share the same hardware block and TCSR mode register.
Per-topology reset and clock lists are introduced alongside the PHY
configs to reflect the different resource ownership in each mode. The
per-mode PHY configurations and match data are then added to wire the
two topologies into the link-mode infrastructure introduced in the
previous patch.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 80 +++++++++++++++++++++++++++++++-
1 file changed, 79 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index d78d57fb64d6..d4aeb3e00955 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -3399,7 +3399,7 @@ static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
/* list of clocks required by phy */
static const char * const qmp_pciephy_clk_l[] = {
- "aux", "cfg_ahb", "ref", "refgen", "rchng", "phy_aux",
+ "aux", "cfg_ahb", "ref", "refgen", "rchng", "phy_aux", "phy_b_aux",
};
static const char * const qmp_pciephy_secondary_clk_l[] = {
@@ -4746,6 +4746,81 @@ static const struct qmp_phy_cfg glymur_qmp_gen4x2_pciephy_cfg = {
.phy_status = PHYSTATUS_4_20,
};
+static const char * const qmp_pciephy_secondary_reset_l[] = {
+ "phy_b",
+};
+
+static const char * const qmp_pciephy_secondary_nocsr_reset_l[] = {
+ "phy_b_nocsr",
+};
+
+static const char * const glymur_pciephy_reset_l[] = {
+ "phy", "phy_b"
+};
+
+static const char * const glymur_pciephy_nocsr_reset_l[] = {
+ "phy_nocsr", "phy_b_nocsr",
+};
+
+static const struct qmp_phy_cfg glymur_qmp_gen5x4_secondary_pciephy_cfg = {
+ .lanes = 4,
+
+ .offsets = &qmp_pcie_offsets_v8_50,
+
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .reset_list = qmp_pciephy_secondary_reset_l,
+ .num_resets = ARRAY_SIZE(qmp_pciephy_secondary_reset_l),
+ .nocsr_reset_list = qmp_pciephy_secondary_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(qmp_pciephy_secondary_nocsr_reset_l),
+
+ .regs = pciephy_v8_50_regs_layout,
+
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS_4_20,
+};
+
+static const struct qmp_phy_cfg glymur_qmp_gen5x8_pciephy_cfg = {
+ .lanes = 8,
+
+ .offsets = &qmp_pcie_offsets_v8_50,
+
+ .reset_list = glymur_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(glymur_pciephy_reset_l),
+ .nocsr_reset_list = glymur_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(glymur_pciephy_nocsr_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+
+ .regs = pciephy_v8_50_regs_layout,
+
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS_4_20,
+};
+
+static const struct qmp_pcie_link_mode_cfg glymur_qmp_gen5x8_mode_cfgs[] = {
+ [QMP_PCIE_GLYMUR_MODE_X8] = {
+ /* x8 */
+ .cfgs = {
+ [QMP_PHY_SELECTOR_0] = &glymur_qmp_gen5x8_pciephy_cfg,
+ },
+ .num_phys = 1,
+ },
+ [QMP_PCIE_GLYMUR_MODE_X4X4] = {
+ /* x4 + x4 */
+ .cfgs = {
+ [QMP_PHY_SELECTOR_0] = &glymur_qmp_gen5x4_pciephy_cfg,
+ [QMP_PHY_SELECTOR_1] = &glymur_qmp_gen5x4_secondary_pciephy_cfg,
+ },
+ .num_phys = 2,
+ },
+};
+
+static const struct qmp_pcie_match_data glymur_qmp_gen5x8_match_data = {
+ .mode_cfgs = glymur_qmp_gen5x8_mode_cfgs,
+ .num_modes = ARRAY_SIZE(glymur_qmp_gen5x8_mode_cfgs),
+};
+
static void qmp_pcie_init_port_b(struct qmp_pcie *qmp, const struct qmp_phy_cfg_tbls *tbls)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
@@ -5797,6 +5872,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = {
}, {
.compatible = "qcom,glymur-qmp-gen5x4-pcie-phy",
.data = &glymur_qmp_gen5x4_pciephy_cfg,
+ }, {
+ .compatible = "qcom,glymur-qmp-gen5x8-pcie-phy",
+ .data = &glymur_qmp_gen5x8_match_data,
}, {
.compatible = "qcom,ipq6018-qmp-pcie-phy",
.data = &ipq6018_pciephy_cfg,
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RFC v4 4/9] phy: qcom: qmp-pcie: Support multiple nocsr resets
From: Qiang Yu @ 2026-05-19 5:47 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio
Cc: Qiang Yu, linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260518-link_mode_0519-v4-0-269cd73cc5d1@oss.qualcomm.com>
Refactor nocsr reset handling to support multiple nocsr resets required
for PHY configurations with bifurcated operation modes.
The Glymur SoC's 3rd PCIe instance supports 8-lane mode using two PHYs
in bifurcation, where each PHY requires its own nocsr reset to be
controlled simultaneously. The current implementation only supports a
single nocsr reset per PHY configuration.
Add num_nocsr and nocsr_list fields to struct qmp_phy_cfg to represent the
number and names of a group of nocsr reset names. Initialize these fields
for all PHYs that have nocsr resets, allowing the driver to correctly
acquire multiple nocsr resets during probe and control them as an array
by using reset_control_bulk APIs.
The refactoring maintains backward compatibility for existing single
nocsr reset configurations while enabling support for multi-PHY
scenarios like Glymur's 8-lane bifurcation mode.
Additionally, introduces x1e80100_qmp_gen3x2_pciephy_cfg as a separate
configuration from sm8550_qmp_gen3x2_pciephy_cfg since the x1e80100 Gen3x2
PHY requires nocsr reset support while the sm8550 Gen3x2 PHY does not.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 87 ++++++++++++++++++++++++++++----
1 file changed, 77 insertions(+), 10 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index 832b5d93105b..1dee4733d4f2 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -3281,6 +3281,11 @@ struct qmp_phy_cfg {
/* resets to be requested */
const char * const *reset_list;
int num_resets;
+
+ /* nocsr resets to be requested */
+ const char * const *nocsr_reset_list;
+ int num_nocsr_resets;
+
/* regulators to be requested */
const char * const *vreg_list;
int num_vregs;
@@ -3327,7 +3332,7 @@ struct qmp_pcie {
int num_pipe_clks;
struct reset_control_bulk_data *resets;
- struct reset_control *nocsr_reset;
+ struct reset_control_bulk_data *nocsr_reset;
struct regulator_bulk_data *vregs;
struct phy *phy;
@@ -3392,6 +3397,10 @@ static const char * const sdm845_pciephy_reset_l[] = {
"phy",
};
+static const char * const sm8550_pciephy_nocsr_reset_l[] = {
+ "phy_nocsr",
+};
+
static const struct qmp_pcie_offsets qmp_pcie_offsets_qhp = {
.serdes = 0,
.pcs = 0x1800,
@@ -4348,6 +4357,8 @@ static const struct qmp_phy_cfg sm8550_qmp_gen4x2_pciephy_cfg = {
},
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = sm8550_qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(sm8550_qmp_phy_vreg_l),
.regs = pciephy_v6_regs_layout,
@@ -4380,6 +4391,8 @@ static const struct qmp_phy_cfg sm8650_qmp_gen4x2_pciephy_cfg = {
},
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = sm8550_qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(sm8550_qmp_phy_vreg_l),
.regs = pciephy_v6_regs_layout,
@@ -4480,6 +4493,35 @@ static const struct qmp_phy_cfg sa8775p_qmp_gen4x4_pciephy_cfg = {
.phy_status = PHYSTATUS_4_20,
};
+static const struct qmp_phy_cfg x1e80100_qmp_gen3x2_pciephy_cfg = {
+ .lanes = 2,
+
+ .offsets = &qmp_pcie_offsets_v5,
+
+ .tbls = {
+ .serdes = sm8550_qmp_gen3x2_pcie_serdes_tbl,
+ .serdes_num = ARRAY_SIZE(sm8550_qmp_gen3x2_pcie_serdes_tbl),
+ .tx = sm8550_qmp_gen3x2_pcie_tx_tbl,
+ .tx_num = ARRAY_SIZE(sm8550_qmp_gen3x2_pcie_tx_tbl),
+ .rx = sm8550_qmp_gen3x2_pcie_rx_tbl,
+ .rx_num = ARRAY_SIZE(sm8550_qmp_gen3x2_pcie_rx_tbl),
+ .pcs = sm8550_qmp_gen3x2_pcie_pcs_tbl,
+ .pcs_num = ARRAY_SIZE(sm8550_qmp_gen3x2_pcie_pcs_tbl),
+ .pcs_misc = sm8550_qmp_gen3x2_pcie_pcs_misc_tbl,
+ .pcs_misc_num = ARRAY_SIZE(sm8550_qmp_gen3x2_pcie_pcs_misc_tbl),
+ },
+ .reset_list = sdm845_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = pciephy_v5_regs_layout,
+
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS,
+};
+
static const struct qmp_phy_cfg x1e80100_qmp_gen4x2_pciephy_cfg = {
.lanes = 2,
@@ -4502,6 +4544,8 @@ static const struct qmp_phy_cfg x1e80100_qmp_gen4x2_pciephy_cfg = {
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_v6_regs_layout,
@@ -4535,6 +4579,8 @@ static const struct qmp_phy_cfg x1e80100_qmp_gen4x4_pciephy_cfg = {
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_v6_regs_layout,
@@ -4566,6 +4612,8 @@ static const struct qmp_phy_cfg x1e80100_qmp_gen4x8_pciephy_cfg = {
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_v6_regs_layout,
@@ -4581,6 +4629,8 @@ static const struct qmp_phy_cfg qmp_v6_gen4x4_pciephy_cfg = {
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_v6_regs_layout,
@@ -4609,6 +4659,8 @@ static const struct qmp_phy_cfg qmp_v8_gen3x2_pciephy_cfg = {
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_v8_regs_layout,
@@ -4624,6 +4676,8 @@ static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_cfg = {
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
@@ -4640,6 +4694,8 @@ static const struct qmp_phy_cfg glymur_qmp_gen4x2_pciephy_cfg = {
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .nocsr_reset_list = sm8550_pciephy_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(sm8550_pciephy_nocsr_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
@@ -4768,7 +4824,7 @@ static int qmp_pcie_init(struct phy *phy)
}
}
- ret = reset_control_assert(qmp->nocsr_reset);
+ ret = reset_control_bulk_assert(cfg->num_nocsr_resets, qmp->nocsr_reset);
if (ret) {
dev_err(qmp->dev, "no-csr reset assert failed\n");
goto err_assert_reset;
@@ -4805,7 +4861,7 @@ static int qmp_pcie_exit(struct phy *phy)
const struct qmp_phy_cfg *cfg = qmp->cfg;
if (qmp->nocsr_reset)
- reset_control_assert(qmp->nocsr_reset);
+ reset_control_bulk_assert(cfg->num_nocsr_resets, qmp->nocsr_reset);
else
reset_control_bulk_assert(cfg->num_resets, qmp->resets);
@@ -4849,7 +4905,7 @@ static int qmp_pcie_power_on(struct phy *phy)
if (ret)
return ret;
- ret = reset_control_deassert(qmp->nocsr_reset);
+ ret = reset_control_bulk_deassert(cfg->num_nocsr_resets, qmp->nocsr_reset);
if (ret) {
dev_err(qmp->dev, "no-csr reset deassert failed\n");
goto err_disable_pipe_clk;
@@ -4998,14 +5054,25 @@ static int qmp_pcie_reset_init(struct qmp_pcie *qmp)
for (i = 0; i < cfg->num_resets; i++)
qmp->resets[i].id = cfg->reset_list[i];
- ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets);
+ ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets,
+ qmp->resets);
if (ret)
return dev_err_probe(dev, ret, "failed to get resets\n");
- qmp->nocsr_reset = devm_reset_control_get_optional_exclusive(dev, "phy_nocsr");
- if (IS_ERR(qmp->nocsr_reset))
- return dev_err_probe(dev, PTR_ERR(qmp->nocsr_reset),
- "failed to get no-csr reset\n");
+ if (!cfg->num_nocsr_resets)
+ return 0;
+ qmp->nocsr_reset = devm_kcalloc(dev, cfg->num_nocsr_resets,
+ sizeof(*qmp->nocsr_reset), GFP_KERNEL);
+ if (!qmp->nocsr_reset)
+ return -ENOMEM;
+
+ for (i = 0; i < cfg->num_nocsr_resets; i++)
+ qmp->nocsr_reset[i].id = cfg->nocsr_reset_list[i];
+
+ ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_nocsr_resets,
+ qmp->nocsr_reset);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get no-csr reset\n");
return 0;
}
@@ -5520,7 +5587,7 @@ static const struct of_device_id qmp_pcie_of_match_table[] = {
.data = &sm8750_qmp_gen3x2_pciephy_cfg,
}, {
.compatible = "qcom,x1e80100-qmp-gen3x2-pcie-phy",
- .data = &sm8550_qmp_gen3x2_pciephy_cfg,
+ .data = &x1e80100_qmp_gen3x2_pciephy_cfg,
}, {
.compatible = "qcom,x1e80100-qmp-gen4x2-pcie-phy",
.data = &x1e80100_qmp_gen4x2_pciephy_cfg,
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RFC v4 6/9] phy: qcom: qmp-pcie: Add clock and reset lists for secondary PHY selector
From: Qiang Yu @ 2026-05-19 5:47 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio
Cc: Qiang Yu, linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260518-link_mode_0519-v4-0-269cd73cc5d1@oss.qualcomm.com>
PHY instances sharing a single DT node each need their own clocks and
resets. The current driver uses a single hardcoded clock list with no
way to select per-instance resources. Add the infrastructure needed
before wiring up multi-PHY probe.
qmp_pciephy_secondary_clk_l[] and qmp_pcie_get_clk_list(id) are added
to select the appropriate clock list by PHY selector index.
qmp_pcie_num_clks() replaces the hard-coded ARRAY_SIZE(qmp_pciephy_clk_l)
in qmp_pcie_init(), qmp_pcie_exit(), and qmp_pcie_clk_init().
struct qmp_pcie::phy is replaced with an id field so each instance can
identify its index within a multi-PHY provider.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 49 ++++++++++++++++++++++++++------
1 file changed, 40 insertions(+), 9 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index 6332f15f78ca..b100302be12a 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -3335,7 +3335,7 @@ struct qmp_pcie {
struct reset_control_bulk_data *nocsr_reset;
struct regulator_bulk_data *vregs;
- struct phy *phy;
+ u32 id;
int mode;
struct clk_fixed_rate pipe_clk_fixed;
@@ -3379,6 +3379,24 @@ static const char * const qmp_pciephy_clk_l[] = {
"aux", "cfg_ahb", "ref", "refgen", "rchng", "phy_aux",
};
+static const char * const qmp_pciephy_secondary_clk_l[] = {
+ "ref", "phy_b_aux", "cfg_ahb_b", "rchng_b",
+};
+
+static int qmp_pcie_get_clk_list(u32 id, const char * const **clk_list)
+{
+ switch (id) {
+ case QMP_PHY_SELECTOR_0:
+ *clk_list = qmp_pciephy_clk_l;
+ return ARRAY_SIZE(qmp_pciephy_clk_l);
+ case QMP_PHY_SELECTOR_1:
+ *clk_list = qmp_pciephy_secondary_clk_l;
+ return ARRAY_SIZE(qmp_pciephy_secondary_clk_l);
+ default:
+ return -EINVAL;
+ }
+}
+
/* list of regulators */
static const char * const qmp_phy_vreg_l[] = {
"vdda-phy", "vdda-pll",
@@ -4781,6 +4799,13 @@ static void qmp_pcie_init_registers(struct qmp_pcie *qmp, const struct qmp_phy_c
qmp_configure(qmp->dev, ln_shrd, tbls->ln_shrd, tbls->ln_shrd_num);
}
+static int qmp_pcie_num_clks(const struct qmp_pcie *qmp)
+{
+ const char * const *clk_list;
+
+ return qmp_pcie_get_clk_list(qmp->id, &clk_list);
+}
+
static int qmp_pcie_init(struct phy *phy)
{
struct qmp_pcie *qmp = phy_get_drvdata(phy);
@@ -4840,7 +4865,7 @@ static int qmp_pcie_init(struct phy *phy)
}
}
- ret = clk_bulk_prepare_enable(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks);
+ ret = clk_bulk_prepare_enable(qmp_pcie_num_clks(qmp), qmp->clks);
if (ret)
goto err_assert_reset;
@@ -4865,7 +4890,7 @@ static int qmp_pcie_exit(struct phy *phy)
else
reset_control_bulk_assert(cfg->num_resets, qmp->resets);
- clk_bulk_disable_unprepare(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks);
+ clk_bulk_disable_unprepare(qmp_pcie_num_clks(qmp), qmp->clks);
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
@@ -5079,16 +5104,21 @@ static int qmp_pcie_reset_init(struct qmp_pcie *qmp)
static int qmp_pcie_clk_init(struct qmp_pcie *qmp)
{
+ const char * const *clk_list;
struct device *dev = qmp->dev;
- int num = ARRAY_SIZE(qmp_pciephy_clk_l);
+ int num;
int i;
+ num = qmp_pcie_get_clk_list(qmp->id, &clk_list);
+ if (num < 0)
+ return num;
+
qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
if (!qmp->clks)
return -ENOMEM;
for (i = 0; i < num; i++)
- qmp->clks[i].id = qmp_pciephy_clk_l[i];
+ qmp->clks[i].id = clk_list[i];
return devm_clk_bulk_get_optional(dev, num, qmp->clks);
}
@@ -5414,6 +5444,7 @@ static int qmp_pcie_probe(struct platform_device *pdev)
struct phy_provider *phy_provider;
struct device_node *np;
struct qmp_pcie *qmp;
+ struct phy *phy;
int ret;
qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
@@ -5468,14 +5499,14 @@ static int qmp_pcie_probe(struct platform_device *pdev)
qmp->mode = PHY_MODE_PCIE_RC;
- qmp->phy = devm_phy_create(dev, np, &qmp_pcie_phy_ops);
- if (IS_ERR(qmp->phy)) {
- ret = PTR_ERR(qmp->phy);
+ phy = devm_phy_create(dev, np, &qmp_pcie_phy_ops);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
dev_err(dev, "failed to create PHY: %d\n", ret);
goto err_node_put;
}
- phy_set_drvdata(qmp->phy, qmp);
+ phy_set_drvdata(phy, qmp);
of_node_put(np);
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RFC v4 1/9] dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Add glymur-qmp-gen5x8-pcie-phy compatible
From: Qiang Yu @ 2026-05-19 5:47 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio
Cc: Qiang Yu, linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260518-link_mode_0519-v4-0-269cd73cc5d1@oss.qualcomm.com>
The Glymur SoC uses a single PCIe Gen5 PHY hardware block for the
PCIe3a/PCIe3b controllers. This block supports two link modes:
1. x4+x4: two 4-lane PHY instances are exposed
2. x8: one 8-lane PHY instance is exposed
Add qcom,glymur-qmp-gen5x8-pcie-phy as a multi-mode PHY compatible and
document the new link-mode property, which selects the active link mode
via a TCSR syscon register.
Document the required clocks, resets, and power-domains for both PHY
instances active in x8 mode. Use #phy-cells = <1> for this compatible,
where the cell value is the PHY index within the active link mode.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
.../bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 140 ++++++++++++++++++---
1 file changed, 126 insertions(+), 14 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml
index 3a35120a77ec..5877e40244ba 100644
--- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml
@@ -18,6 +18,7 @@ properties:
enum:
- qcom,glymur-qmp-gen4x2-pcie-phy
- qcom,glymur-qmp-gen5x4-pcie-phy
+ - qcom,glymur-qmp-gen5x8-pcie-phy
- qcom,kaanapali-qmp-gen3x2-pcie-phy
- qcom,qcs615-qmp-gen3x1-pcie-phy
- qcom,qcs8300-qmp-gen4x2-pcie-phy
@@ -58,7 +59,7 @@ properties:
clocks:
minItems: 5
- maxItems: 6
+ maxItems: 10
clock-names:
minItems: 5
@@ -68,20 +69,29 @@ properties:
- const: ref
- enum: [rchng, refgen]
- const: pipe
- - const: pipediv2
+ - enum: [pipediv2, phy_b_aux]
+ - const: cfg_ahb_b
+ - const: rchng_b
+ - const: pipe_b
+ - const: pipediv2_b
power-domains:
- maxItems: 1
+ minItems: 1
+ items:
+ - description: PCIe PHY power domain.
+ - description: Additional PCIe PHY power domain (if present).
resets:
minItems: 1
- maxItems: 2
+ maxItems: 4
reset-names:
minItems: 1
items:
- const: phy
- const: phy_nocsr
+ - const: phy_b
+ - const: phy_b_nocsr
vdda-phy-supply: true
@@ -98,13 +108,29 @@ properties:
- description: offset of PCIe 4-lane configuration register
- description: offset of configuration bit for this PHY
+ qcom,link-mode:
+ description:
+ Configures the link mode of the PCIe PHY. Some PHYs support multiple
+ link modes, such as a single x8 link or two independent x4 links. The
+ link mode selection is performed by writing to a register in the TCSR
+ syscon, specified as a phandle to the syscon, the register offset, and
+ the link mode value.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: phandle of TCSR syscon
+ - description: offset of link mode register
+ - description: link mode value
+
"#clock-cells": true
clock-output-names:
- maxItems: 1
+ minItems: 1
+ items:
+ - description: Name of the first pipe clock output.
+ - description: Name of the second pipe clock output (if present).
- "#phy-cells":
- const: 0
+ "#phy-cells": true
required:
- compatible
@@ -130,19 +156,40 @@ allOf:
- qcom,sc8280xp-qmp-gen3x4-pcie-phy
- qcom,x1e80100-qmp-gen4x4-pcie-phy
- qcom,x1p42100-qmp-gen4x4-pcie-phy
+ - qcom,glymur-qmp-gen5x8-pcie-phy
then:
properties:
reg:
items:
- description: port a
- description: port b
- required:
- - qcom,4ln-config-sel
else:
properties:
reg:
maxItems: 1
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,sc8280xp-qmp-gen3x4-pcie-phy
+ - qcom,x1e80100-qmp-gen4x4-pcie-phy
+ - qcom,x1p42100-qmp-gen4x4-pcie-phy
+ then:
+ required:
+ - qcom,4ln-config-sel
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,glymur-qmp-gen5x8-pcie-phy
+ then:
+ required:
+ - qcom,link-mode
+
- if:
properties:
compatible:
@@ -198,8 +245,40 @@ allOf:
properties:
clocks:
minItems: 6
+ maxItems: 6
clock-names:
minItems: 6
+ maxItems: 6
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,glymur-qmp-gen5x8-pcie-phy
+ then:
+ properties:
+ clocks:
+ minItems: 10
+ maxItems: 10
+ clock-names:
+ items:
+ - const: aux
+ - const: cfg_ahb
+ - const: ref
+ - const: rchng
+ - const: pipe
+ - const: phy_b_aux
+ - const: cfg_ahb_b
+ - const: rchng_b
+ - const: pipe_b
+ - const: pipediv2_b
+ power-domains:
+ minItems: 2
+ else:
+ properties:
+ power-domains:
+ maxItems: 1
- if:
properties:
@@ -223,11 +302,24 @@ allOf:
reset-names:
minItems: 2
else:
- properties:
- resets:
- maxItems: 1
- reset-names:
- maxItems: 1
+ if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,glymur-qmp-gen5x8-pcie-phy
+ then:
+ properties:
+ resets:
+ minItems: 4
+ reset-names:
+ minItems: 4
+ else:
+ properties:
+ resets:
+ maxItems: 1
+ reset-names:
+ maxItems: 1
- if:
properties:
@@ -237,6 +329,7 @@ allOf:
- qcom,sm8450-qmp-gen4x2-pcie-phy
- qcom,sm8550-qmp-gen4x2-pcie-phy
- qcom,sm8650-qmp-gen4x2-pcie-phy
+ - qcom,glymur-qmp-gen5x8-pcie-phy
then:
properties:
"#clock-cells":
@@ -246,6 +339,25 @@ allOf:
"#clock-cells":
const: 0
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,glymur-qmp-gen5x8-pcie-phy
+ then:
+ properties:
+ clock-output-names:
+ minItems: 2
+ "#phy-cells":
+ const: 1
+ else:
+ properties:
+ clock-output-names:
+ maxItems: 1
+ "#phy-cells":
+ const: 0
+
examples:
- |
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RFC v4 5/9] phy: qcom: qmp-pcie: Refactor pipe clk register and parse_dt helpers
From: Qiang Yu @ 2026-05-19 5:47 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio
Cc: Qiang Yu, linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260518-link_mode_0519-v4-0-269cd73cc5d1@oss.qualcomm.com>
Some QMP PCIe PHY hardware blocks can be split into multiple sub-PHYs
under a single DT node, each requiring its own pipe clock registration and
DT resource mapping. The current helpers are tightly coupled to a single
qmp_pcie instance, which prevents reuse across sub-PHY instances.
Refactor __phy_pipe_clk_register() as a generic helper and reduce
phy_pipe_clk_register() to a thin wrapper around it. Similarly, extract
qmp_pcie_parse_dt_common() from qmp_pcie_parse_dt() to hold the register-
mapping and pipe-clock setup that will be shared between sub-PHY instances,
with pipe clock names parameterised per instance.
This is a preparatory step before adding multi-PHY support. No functional
change for existing platforms.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 76 ++++++++++++++++++--------------
1 file changed, 44 insertions(+), 32 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index 1dee4733d4f2..6332f15f78ca 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -5116,32 +5116,34 @@ static void phy_clk_release_provider(void *res)
* clk | +-------+ | +-----+
* +---------------+
*/
-static int phy_pipe_clk_register(struct qmp_pcie *qmp, struct device_node *np)
+static int __phy_pipe_clk_register(struct device *dev, struct device_node *np,
+ int clk_name_index, struct clk_fixed_rate *fixed)
{
- struct clk_fixed_rate *fixed = &qmp->pipe_clk_fixed;
struct clk_init_data init = { };
int ret;
- ret = of_property_read_string_index(np, "clock-output-names", 0, &init.name);
+ ret = of_property_read_string_index(np, "clock-output-names", clk_name_index,
+ &init.name);
if (ret) {
- dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
+ dev_err(dev, "%pOFn: No clock-output-names\n", np);
return ret;
}
init.ops = &clk_fixed_rate_ops;
- /*
- * Controllers using QMP PHY-s use 125MHz pipe clock interface
- * unless other frequency is specified in the PHY config.
- */
- if (qmp->cfg->pipe_clock_rate)
- fixed->fixed_rate = qmp->cfg->pipe_clock_rate;
- else
+ /* Default to 125MHz if caller did not pre-populate a rate. */
+ if (!fixed->fixed_rate)
fixed->fixed_rate = 125000000;
fixed->hw.init = &init;
- return devm_clk_hw_register(qmp->dev, &fixed->hw);
+ return devm_clk_hw_register(dev, &fixed->hw);
+}
+
+static int phy_pipe_clk_register(struct qmp_pcie *qmp, struct device_node *np)
+{
+ qmp->pipe_clk_fixed.fixed_rate = qmp->cfg->pipe_clock_rate;
+ return __phy_pipe_clk_register(qmp->dev, np, 0, &qmp->pipe_clk_fixed);
}
/*
@@ -5336,26 +5338,18 @@ static int qmp_pcie_get_4ln_config(struct qmp_pcie *qmp)
return 0;
}
-static int qmp_pcie_parse_dt(struct qmp_pcie *qmp)
+static int qmp_pcie_parse_dt_common(struct qmp_pcie *qmp, void __iomem *base,
+ const char *pipe_clk_name,
+ const char *pipediv2_clk_name)
{
- struct platform_device *pdev = to_platform_device(qmp->dev);
const struct qmp_phy_cfg *cfg = qmp->cfg;
const struct qmp_pcie_offsets *offs = cfg->offsets;
struct device *dev = qmp->dev;
- void __iomem *base;
int ret;
if (!offs)
return -EINVAL;
- ret = qmp_pcie_get_4ln_config(qmp);
- if (ret)
- return ret;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
qmp->serdes = base + offs->serdes;
qmp->pcs = base + offs->pcs;
qmp->pcs_misc = base + offs->pcs_misc;
@@ -5368,12 +5362,6 @@ static int qmp_pcie_parse_dt(struct qmp_pcie *qmp)
qmp->rx2 = base + offs->rx2;
}
- if (qmp->cfg->lanes >= 4 && qmp->tcsr_4ln_config) {
- qmp->port_b = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(qmp->port_b))
- return PTR_ERR(qmp->port_b);
- }
-
qmp->txz = base + offs->txz;
qmp->rxz = base + offs->rxz;
@@ -5381,17 +5369,41 @@ static int qmp_pcie_parse_dt(struct qmp_pcie *qmp)
qmp->ln_shrd = base + offs->ln_shrd;
qmp->num_pipe_clks = 2;
- qmp->pipe_clks[0].id = "pipe";
- qmp->pipe_clks[1].id = "pipediv2";
+ qmp->pipe_clks[0].id = pipe_clk_name;
+ qmp->pipe_clks[1].id = pipediv2_clk_name;
ret = devm_clk_bulk_get(dev, 1, qmp->pipe_clks);
if (ret)
return ret;
- ret = devm_clk_bulk_get_optional(dev, qmp->num_pipe_clks - 1, qmp->pipe_clks + 1);
+ return devm_clk_bulk_get_optional(dev, qmp->num_pipe_clks - 1,
+ qmp->pipe_clks + 1);
+}
+
+static int qmp_pcie_parse_dt(struct qmp_pcie *qmp)
+{
+ struct platform_device *pdev = to_platform_device(qmp->dev);
+ void __iomem *base;
+ int ret;
+
+ ret = qmp_pcie_get_4ln_config(qmp);
+ if (ret)
+ return ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ ret = qmp_pcie_parse_dt_common(qmp, base, "pipe", "pipediv2");
if (ret)
return ret;
+ if (qmp->cfg->lanes >= 4 && qmp->tcsr_4ln_config) {
+ qmp->port_b = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(qmp->port_b))
+ return PTR_ERR(qmp->port_b);
+ }
+
return 0;
}
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RFC v4 3/9] phy: qcom: qmp-pcie: Add multiple power-domains support
From: Qiang Yu @ 2026-05-19 5:47 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio
Cc: Qiang Yu, linux-arm-msm, linux-phy, devicetree, linux-kernel,
Dmitry Baryshkov
In-Reply-To: <20260518-link_mode_0519-v4-0-269cd73cc5d1@oss.qualcomm.com>
The Glymur SoC's 3rd PCIe instance supports 8-lane mode using two PHYs in
a bifurcated configuration. Each PHY has its own power domain (phy_gdsc)
that must be powered on before initialization per hardware requirements.
Current PHY power management assumes a single power domain per PHY,
preventing proper setup for this dual-PHY scenario. Add support for
multiple power domains by using devm_pm_domain_attach_list() to attach
power domains manually, while maintaining compatibility with single
power domain PHYs.
Enable runtime PM to allow power domain control when the PCIe driver
calls phy_power_on/phy_power_off:
- Single power domain: QMP PHY platform device directly attaches to
power domain and controls it during runtime resume/suspend
- Multiple power domains: devm_pm_domain_attach_list() creates virtual
devices as power domain suppliers, linked to the QMP PHY platform
device as consumer
This ensures power domains are properly attached and turned on/off
for both single and multiple power domain configurations.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index 75afbd15aaf4..832b5d93105b 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -17,6 +17,7 @@
#include <linux/phy/pcie.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@@ -5329,6 +5330,7 @@ static int qmp_pcie_parse_dt(struct qmp_pcie *qmp)
static int qmp_pcie_probe(struct platform_device *pdev)
{
+ struct dev_pm_domain_list *pd_list;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
struct device_node *np;
@@ -5348,6 +5350,16 @@ static int qmp_pcie_probe(struct platform_device *pdev)
WARN_ON_ONCE(!qmp->cfg->pwrdn_ctrl);
WARN_ON_ONCE(!qmp->cfg->phy_status);
+ ret = devm_pm_domain_attach_list(dev, NULL, &pd_list);
+ if (ret < 0 && ret != -EEXIST) {
+ dev_err(dev, "Failed to attach power domain\n");
+ return ret;
+ }
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
ret = qmp_pcie_clk_init(qmp);
if (ret)
return ret;
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RFC v4 0/9] phy: qcom: qmp-pcie: Add link-mode based support for Glymur Gen5x8 PHY
From: Qiang Yu @ 2026-05-19 5:47 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio
Cc: Qiang Yu, linux-arm-msm, linux-phy, devicetree, linux-kernel,
Dmitry Baryshkov
Hi all,
This RFC proposes a revised model for Glymur Gen5x8 PCIe PHY support.
On Glymur, PCIe3 uses one shared Gen5x8 QMP PHY hardware block that can
operate in different link topologies (x8 or x4+x4). The previous series
approach was not accepted, so this RFC reworks the design to better match
the hardware model and DT expectations.
This RFC focuses on the link-mode model and associated binding/API changes
while keeping prerequisite/independent patches separate.
What this RFC changes:
- Keep a single PHY provider node for the shared Gen5x8 hardware block.
- Select active topology on the provider side via `link-mode`.
- Use `#phy-cells = <1>` so consumers can pass logical PHY index to get
different phy structure.
- Keep legacy (non-link-mode) platforms on the existing path.
- Validate mode consistency at probe:
- if HW mode matches DT mode, continue;
- if mismatch, allow only when selected mode has complete PHY setting
tables for all logical PHYs; otherwise fail probe.
- Keep programming link-mode register during `power_on`, so DT-selected
mode is re-applied when TCSR register may be reset after low-power mode.
Mode/cfg selection model:
- Introduce mode-indexed match data:
- first dimension: `link_mode`
- second dimension: `logical_phy_index`
- Effective lookup is:
cfg = mode_cfgs[active_link_mode].cfgs[logical_phy_index]
This allows one shared provider to expose different logical PHY sets per
mode.
Impact:
- Keep existing qmp-pcie PHY operation flow (`phy_ops`) unchanged as much
as possible.
- Constrain the new design mainly to cfg selection and provider/xlate
wiring.
- Reuse existing init/power/reset flows with selected cfg, minimizing code
churn and behavioral risk.
- This RFC is implemented and validated on Glymur Gen5x8, it is designed
to be extensible to other multi-mode QMP PCIe PHYs, although additional
platform-specific requirements may still be needed.
This series depends on a prerequisite patch by Krzysztof Kozlowski:
https://lore.kernel.org/r/20260420133616.88740-2-krzysztof.kozlowski@oss.qualcomm.com
Thanks,
Qiang
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
Changes in v4:
- Replaced the static bifurcation probe model with a link-mode
architecture: the active topology (x8 or x4+x4) is selected via a
new "qcom,link-mode" DT property and written to a TCSR register at
power-on. This replaces v3 patches 4 and 5 with four new patches
(driver refactor, secondary PHY clock/reset lists, link-mode probe
infrastructure, and Glymur config).
- Updated dt-bindings to describe "qcom,link-mode", #phy-cells = <1>,
per-mode validation rules, and added a new header with
QMP_PHY_SELECTOR_* and QMP_PCIE_GLYMUR_MODE_* macros.
- Patches 2 and 3 (multiple power-domains, multiple nocsr resets) are
unchanged from v3.
- Link to v3: https://lore.kernel.org/r/20260412-glymur_gen5x8_phy_0413-v3-0-affcebc16b8b@oss.qualcomm.com
Changes in v3:
- Add description of each power-domain.
- Add 64bit prefetchable memory range required by some EPs eg. AI100 ultra.
- Move PCIe3a after PCIe3b and move PCIe3a PHY before PCIe3b PHY.
- Link to v2: https://lore.kernel.org/all/20260323-glymur_gen5x8_phy_0323-v2-0-ce0fc07f0e52@oss.qualcomm.com/
Changes in v2:
- Remove pd_list from qmp_pcie struct as it is not used in phy driver.
- align clk-names on "
- Link to v1: https://lore.kernel.org/all/20260304-glymur_gen5x8_phy-v1-0-849e9a72e125@oss.qualcomm.com/
To: Vinod Koul <vkoul@kernel.org>
To: Neil Armstrong <neil.armstrong@linaro.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Philipp Zabel <p.zabel@pengutronix.de>
To: Bjorn Andersson <andersson@kernel.org>
To: Konrad Dybcio <konradybcio@kernel.org>
Cc: linux-arm-msm@vger.kernel.org
Cc: linux-phy@lists.infradead.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
Qiang Yu (9):
dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Add glymur-qmp-gen5x8-pcie-phy compatible
dt-bindings: phy: qcom-qmp: Add PHY selector and Glymur link-mode macros
phy: qcom: qmp-pcie: Add multiple power-domains support
phy: qcom: qmp-pcie: Support multiple nocsr resets
phy: qcom: qmp-pcie: Refactor pipe clk register and parse_dt helpers
phy: qcom: qmp-pcie: Add clock and reset lists for secondary PHY selector
phy: qcom: qmp-pcie: Add link-mode multi-PHY probe infrastructure
phy: qcom: qmp-pcie: Add Glymur Gen5x8 PHY config and match data
arm64: dts: qcom: glymur: Wire PCIe3a/3b to shared Gen5x8 PHY
.../bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 140 ++++-
arch/arm64/boot/dts/qcom/glymur-crd.dtsi | 5 +
arch/arm64/boot/dts/qcom/glymur.dtsi | 333 ++++++++++-
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 631 ++++++++++++++++++---
include/dt-bindings/phy/phy-qcom-qmp.h | 8 +
5 files changed, 1021 insertions(+), 96 deletions(-)
---
base-commit: 4359f733f8edf6be944b9f28ccb6a6c6e9a24aaa
change-id: 20260518-link_mode_0519-708494cee2da
Best regards,
--
Qiang Yu <qiang.yu@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH RFC v4 2/9] dt-bindings: phy: qcom-qmp: Add PHY selector and Glymur link-mode macros
From: Qiang Yu @ 2026-05-19 5:47 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio
Cc: Qiang Yu, linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260518-link_mode_0519-v4-0-269cd73cc5d1@oss.qualcomm.com>
Add two sets of constants to phy-qcom-qmp.h to support upcoming multiple
link mode QMP PHY:
- QMP_PHY_SELECTOR_0 / QMP_PHY_SELECTOR_1: generic logical PHY index
values for QMP providers that expose multiple PHY instances under a
single DT node (i.e. #phy-cells = <1>).
- QMP_PCIE_GLYMUR_MODE_X8 / QMP_PCIE_GLYMUR_MODE_X4X4: link-mode
values for the Glymur Gen5x8 PCIe PHY "qcom,link-mode" syscon property,
selecting between the x8 single-PHY and x4+x4 dual-PHY topologies.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
include/dt-bindings/phy/phy-qcom-qmp.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/dt-bindings/phy/phy-qcom-qmp.h b/include/dt-bindings/phy/phy-qcom-qmp.h
index 6b43ea9e0051..befa76f8392f 100644
--- a/include/dt-bindings/phy/phy-qcom-qmp.h
+++ b/include/dt-bindings/phy/phy-qcom-qmp.h
@@ -21,4 +21,12 @@
#define QMP_PCIE_PIPE_CLK 0
#define QMP_PCIE_PHY_AUX_CLK 1
+/* Generic QMP logical PHY selectors */
+#define QMP_PHY_SELECTOR_0 0
+#define QMP_PHY_SELECTOR_1 1
+
+/* Glymur QMP PCIe link modes (for link-mode property value) */
+#define QMP_PCIE_GLYMUR_MODE_X8 0
+#define QMP_PCIE_GLYMUR_MODE_X4X4 1
+
#endif /* _DT_BINDINGS_PHY_QMP */
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH net-next v4] net: phy: sfp: probe for RollBall I2C-to-MDIO bridge in mdio-i2c
From: Petr Wozniak @ 2026-05-19 4:32 UTC (permalink / raw)
To: netdev; +Cc: bjorn, andrew, linux-phy, kuba, Petr Wozniak
The "OEM"/"SFP-10G-T" quirk entry in sfp_fixup_rollball_cc()
unconditionally forces MDIO_I2C_ROLLBALL for all modules matching that
vendor/part-number combination. This works for modules that genuinely
implement a RollBall I2C-to-MDIO bridge, but silently breaks modules
that share the same EEPROM strings without having such a bridge.
The Realtek RTL8261BE-CG is one such module: a pure copper 10G SFP+
media converter with no I2C-to-MDIO bridge. Its EEPROM reports
vendor="OEM", part="SFP-10G-T-I", and -- critically -- Vendor OUI
00:00:00, making OUI-based differentiation impossible. With
MDIO_I2C_ROLLBALL the kernel stalls waiting for a PHY that never
appears:
sfp sfp2: probing phy device through the [MDIO_I2C_ROLLBALL] protocol
Move the probe into i2c_mii_init_rollball() in mdio-i2c.c, where the
RollBall protocol constants are already defined. After sending the
unlock password, issue a CMD_READ and wait ~70 ms for CMD_DONE. A
genuine RollBall bridge asserts CMD_DONE within that window; modules
without a bridge never do, so i2c_mii_init_rollball() returns -ENODEV.
mdio_i2c_alloc() propagates -ENODEV to the caller without logging an
error. sfp_sm_add_mdio_bus() catches -ENODEV and transitions
sfp->mdio_protocol to MDIO_I2C_NONE so the rest of the state machine
skips PHY probing for this module.
Add "OEM"/"SFP-10G-T-I" to the quirk table so RTL8261BE modules enter
the probe path; genuine RollBall modules continue to work as before.
Signed-off-by: Petr Wozniak <petr.wozniak@gmail.com>
---
Changes since v3 (feedback from Jakub Kicinski):
- Drop spurious Tested-by: tag -- author and tester are the same person
- Use PATCH net-next subject prefix
- Move -ENODEV handling from sfp_i2c_mdiobus_create() into
sfp_sm_add_mdio_bus() so bus-creation code does not mutate
sfp->mdio_protocol; the state machine is the correct place for
protocol-state transitions
- Split combined variable declaration for clarity
Changes since v2:
- Compile-tested and hardware-tested on BPI-R4 (MT7988A, 6.12.87)
- RTL8261BE (OEM/SFP-10G-T-I): probes MDIO_I2C_NONE, link Up 10Gbps
- Genuine RollBall (OEM/SFP-10G-T): bridge detected, link Up 10Gbps
drivers/net/mdio/mdio-i2c.c | 57 +++++++++++++++++++++++++++++++-----
drivers/net/phy/sfp.c | 15 ++++++++--
2 files changed, 62 insertions(+), 10 deletions(-)
--- a/drivers/net/mdio/mdio-i2c.c
+++ b/drivers/net/mdio/mdio-i2c.c
@@ -419,6 +419,46 @@
return 0;
}
+static int i2c_mii_probe_rollball(struct i2c_adapter *i2c)
+{
+ u8 data_buf[] = { ROLLBALL_DATA_ADDR, 0x01, 0x00, 0x00 };
+ u8 cmd_buf[] = { ROLLBALL_CMD_ADDR, ROLLBALL_CMD_READ };
+ u8 cmd_addr = ROLLBALL_CMD_ADDR;
+ u8 result;
+ struct i2c_msg msgs[2];
+ int ret;
+
+ msgs[0].addr = ROLLBALL_PHY_I2C_ADDR;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(data_buf);
+ msgs[0].buf = data_buf;
+ msgs[1].addr = ROLLBALL_PHY_I2C_ADDR;
+ msgs[1].flags = 0;
+ msgs[1].len = sizeof(cmd_buf);
+ msgs[1].buf = cmd_buf;
+
+ ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
+ if (ret)
+ return ret;
+
+ msleep(70);
+
+ msgs[0].addr = ROLLBALL_PHY_I2C_ADDR;
+ msgs[0].flags = 0;
+ msgs[0].len = 1;
+ msgs[0].buf = &cmd_addr;
+ msgs[1].addr = ROLLBALL_PHY_I2C_ADDR;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = &result;
+
+ ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
+ if (ret)
+ return ret;
+
+ return result == ROLLBALL_CMD_DONE ? 0 : -ENODEV;
+}
+
static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
{
struct i2c_msg msg;
@@ -439,10 +479,10 @@
ret = i2c_transfer(i2c, &msg, 1);
if (ret < 0)
return ret;
- else if (ret != 1)
+ if (ret != 1)
return -EIO;
- else
- return 0;
+
+ return i2c_mii_probe_rollball(i2c);
}
static bool mdio_i2c_check_functionality(struct i2c_adapter *i2c,
@@ -487,9 +527,10 @@
case MDIO_I2C_ROLLBALL:
ret = i2c_mii_init_rollball(i2c);
if (ret < 0) {
- dev_err(parent,
- "Cannot initialize RollBall MDIO I2C protocol: %d\n",
- ret);
+ if (ret != -ENODEV)
+ dev_err(parent,
+ "Cannot initialize RollBall MDIO I2C protocol: %d\n",
+ ret);
mdiobus_free(mii);
return ERR_PTR(ret);
}
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -579,7 +579,8 @@
// OEM SFP-GE-T is a 1000Base-T module with broken TX_FAULT indicator
SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault),
- SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc),
+ SFP_QUIRK_F("OEM", "SFP-10G-T-I", sfp_fixup_rollball),
+ SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc),
SFP_QUIRK_S("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex),
SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex),
@@ -2022,10 +2023,17 @@
static int sfp_sm_add_mdio_bus(struct sfp *sfp)
{
- if (sfp->mdio_protocol != MDIO_I2C_NONE)
- return sfp_i2c_mdiobus_create(sfp);
+ int ret;
- return 0;
+ if (sfp->mdio_protocol == MDIO_I2C_NONE)
+ return 0;
+
+ ret = sfp_i2c_mdiobus_create(sfp);
+ if (ret == -ENODEV) {
+ sfp->mdio_protocol = MDIO_I2C_NONE;
+ return 0;
+ }
+ return ret;
}
/* Probe a SFP for a PHY device if the module supports copper - the PHY
--
2.51.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH V1 3/3] phy: qcom-qmp-ufs: Add UFS PHY support on Hawi
From: Palash Kambar @ 2026-05-19 4:18 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, mani, alim.akhtar,
avri.altman, bvanassche, andersson, abel.vesa, luca.weiss,
linux-arm-msm, linux-phy, devicetree, linux-kernel, linux-scsi
In-Reply-To: <c6hlbz44belq5l3ko23ijny22hxzei5fexk47hwselgn7onsbz@o7uwxlkvgtwt>
On 5/18/2026 10:39 PM, Dmitry Baryshkov wrote:
> On Mon, May 18, 2026 at 10:23:46PM +0530, palash.kambar@oss.qualcomm.com wrote:
>> From: Palash Kambar <palash.kambar@oss.qualcomm.com>
>>
>> Add the init sequence tables and config for the UFS QMP phy found in
>> the Hawi SoC.
>>
>> Signed-off-by: Palash Kambar <palash.kambar@oss.qualcomm.com>
>> ---
>> .../phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h | 22 +++
>> .../phy-qcom-qmp-qserdes-txrx-ufs-v8.h | 37 +++++
>> drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 140 ++++++++++++++++++
>> 3 files changed, 199 insertions(+)
>> create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
>> create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
>>
>> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
>> new file mode 100644
>> index 000000000000..bf914c752d22
>> --- /dev/null
>> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
>> @@ -0,0 +1,22 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2026, The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#ifndef QCOM_PHY_QMP_PCS_UFS_V7_H_
>> +#define QCOM_PHY_QMP_PCS_UFS_V7_H_
>> +
>> +/* Only for QMP V7 PHY - UFS PCS registers */
>> +
>> +#define QPHY_V7_PCS_UFS_PCS_CTRL1 0x01C
>> +#define QPHY_V7_PCS_UFS_PLL_CNTL 0x028
>> +#define QPHY_V7_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x02C
>> +#define QPHY_V7_PCS_UFS_TX_HSGEAR_CAPABILITY 0x060
>> +#define QPHY_V7_PCS_UFS_RX_HSGEAR_CAPABILITY 0x094
>> +#define QPHY_V7_PCS_UFS_LINECFG_DISABLE 0x140
>> +#define QPHY_V7_PCS_UFS_RX_SIGDET_CTRL2 0x150
>> +#define QPHY_V7_PCS_UFS_READY_STATUS 0x16c
>> +#define QPHY_V7_PCS_UFS_TX_MID_TERM_CTRL1 0x1b8
>> +#define QPHY_V7_PCS_UFS_MULTI_LANE_CTRL1 0x1c0
>> +
>> +#endif
>> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
>> new file mode 100644
>> index 000000000000..5f923c3e64ec
>> --- /dev/null
>> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
>> @@ -0,0 +1,37 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2026, The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_UFS_V8_H_
>> +#define QCOM_PHY_QMP_QSERDES_TXRX_UFS_V8_H_
>> +
>> +#define QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_TX (0x34)
>> +#define QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_RX (0x38)
>> +#define QSERDES_UFS_V8_TX_LANE_MODE_1 (0x80)
>> +#define QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE2 (0x1BC)
>> +#define QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE4 (0x1C4)
>> +#define QSERDES_UFS_V8_RX_UCDR_SO_GAIN_RATE4 (0x1DC)
>> +#define QSERDES_UFS_V8_RX_EQ_OFFSET_ADAPTOR_CNTRL1 (0x2C8)
>> +#define QSERDES_UFS_V8_RX_UCDR_PI_CONTROLS (0x1E4)
>> +#define QSERDES_UFS_V8_RX_OFFSET_ADAPTOR_CNTRL3 (0x2D0)
>> +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4 (0x120)
>> +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_FO_GAIN_RATE4 (0xD4)
>> +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_SO_GAIN_RATE4 (0xEC)
>> +#define QSERDES_UFS_V8_RX_VGA_CAL_MAN_VAL (0x288)
>> +#define QSERDES_UFS_V8_RX_EQU_ADAPTOR_CNTRL4 (0x2B0)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE_0_1_B4 (0x324)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE4_SA_B7 (0x3B4)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE4_SA_B9 (0x3BC)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE4_SB_B7 (0x3E0)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE4_SB_B9 (0x3E8)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE5_SA_B7 (0x40C)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE5_SA_B9 (0x414)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE5_SB_B7 (0x438)
>> +#define QSERDES_UFS_V8_RX_MODE_RATE5_SB_B9 (0x440)
>> +#define QSERDES_UFS_V8_RX_UCDR_SO_SATURATION (0xF4)
>> +#define QSERDES_UFS_V8_RX_TERM_BW_CTRL0 (0x1AC)
>> +#define QSERDES_UFS_V8_RX_DLL0_FTUNE_CTRL (0x498)
>> +#define QSERDES_UFS_V8_RX_SIGDET_CAL_TRIM (0x4d0)
>> +
>> +#endif
>> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
>> index 771bc7c2ab50..a4801cf4b0fe 100644
>> --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
>> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
>> @@ -29,9 +29,11 @@
>> #include "phy-qcom-qmp-pcs-ufs-v4.h"
>> #include "phy-qcom-qmp-pcs-ufs-v5.h"
>> #include "phy-qcom-qmp-pcs-ufs-v6.h"
>> +#include "phy-qcom-qmp-pcs-ufs-v7.h"
>>
>> #include "phy-qcom-qmp-qserdes-txrx-ufs-v6.h"
>> #include "phy-qcom-qmp-qserdes-txrx-ufs-v7.h"
>> +#include "phy-qcom-qmp-qserdes-txrx-ufs-v8.h"
>>
>> /* QPHY_PCS_READY_STATUS bit */
>> #define PCS_READY BIT(0)
>> @@ -84,6 +86,13 @@ static const unsigned int ufsphy_v6_regs_layout[QPHY_LAYOUT_SIZE] = {
>> [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL,
>> };
>>
>> +static const unsigned int ufsphy_v7_regs_layout[QPHY_LAYOUT_SIZE] = {
>> + [QPHY_START_CTRL] = QPHY_V6_PCS_UFS_PHY_START,
>> + [QPHY_PCS_READY_STATUS] = QPHY_V7_PCS_UFS_READY_STATUS,
>> + [QPHY_SW_RESET] = QPHY_V6_PCS_UFS_SW_RESET,
>> + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL,
>
> Don't mix V6 and V7 registers. And why is it v7? The rest of the
> registers point out a v8 PHY.
>
>> +};
>> +
>> static const struct qmp_phy_init_tbl milos_ufsphy_serdes[] = {
>> QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
>> QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
>> @@ -1844,6 +1868,119 @@ static const struct qmp_phy_cfg sm8750_ufsphy_cfg = {
>>
>> };
>>
>> +static const struct qmp_phy_init_tbl hawi_ufsphy_serdes[] = {
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_CFG, 0x60),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x1f),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO_MODE1, 0x1f),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IETRIM, 0x07),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x20),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_CTRL, 0x40),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_ADAPTIVE_ANALOG_CONFIG, 0x06),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x06),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE0, 0x06),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE0, 0x18),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE0, 0x14),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x7f),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x06),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x92),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x4c),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x06),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x14),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE1, 0x06),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE1, 0x18),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE1, 0x14),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x99),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x07),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xbe),
>> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
>
> Yep... If it is V8, use V8 registers. Even if they are they same.
>
>> +};
>> +
>> +static const struct qmp_phy_init_tbl hawi_ufsphy_tx[] = {
>> + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_LANE_MODE_1, 0x0c),
>> + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_TX, 0x07),
>> + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_RX, 0x17),
>
> And it's V8.
>
Sure Dmitry, will update. Thanks.
>> +};
>> +
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v3] net: phy: sfp: probe for RollBall I2C-to-MDIO bridge in mdio-i2c
From: Jakub Kicinski @ 2026-05-19 0:10 UTC (permalink / raw)
To: Petr Wozniak; +Cc: netdev, bjorn, andrew, linux-phy
In-Reply-To: <20260515174044.26036-1-petr.wozniak@gmail.com>
On Fri, 15 May 2026 19:40:44 +0200 Petr Wozniak wrote:
> The "OEM"/"SFP-10G-T" quirk entry in sfp_fixup_rollball_cc()
> unconditionally forces MDIO_I2C_ROLLBALL for all modules matching that
> vendor/part-number combination. This works for modules that genuinely
> implement a RollBall I2C-to-MDIO bridge, but silently breaks modules
> that share the same EEPROM strings without having such a bridge.
>
> The Realtek RTL8261BE-CG is one such module: a pure copper 10G SFP+
> media converter with no I2C-to-MDIO bridge. Its EEPROM reports
> vendor="OEM", part="SFP-10G-T-I", and -- critically -- Vendor OUI
> 00:00:00, making OUI-based differentiation impossible. With
> MDIO_I2C_ROLLBALL the kernel stalls waiting for a PHY that never
> appears:
>
> sfp sfp2: probing phy device through the [MDIO_I2C_ROLLBALL] protocol
>
> Move the probe into i2c_mii_init_rollball() in mdio-i2c.c, where the
> RollBall protocol constants are already defined. After sending the
> unlock password, issue a CMD_READ and wait ~70 ms for CMD_DONE. A
> genuine RollBall bridge asserts CMD_DONE within that window; modules
> without a bridge never do, so i2c_mii_init_rollball() returns -ENODEV.
> sfp_i2c_mdiobus_create() treats -ENODEV as no PHY and falls back to
> MDIO_I2C_NONE without creating an MDIO bus.
>
> Add "OEM"/"SFP-10G-T-I" to the quirk table so RTL8261BE modules enter
> the probe path; genuine RollBall modules continue to work as before.
>
> Signed-off-by: Petr Wozniak <petr.wozniak@gmail.com>
> Tested-by: Petr Wozniak <petr.wozniak@gmail.com>
Tested-by tag is for when the person testing is different than the
author of the patch. Please drop.
> Targeting: net-next
This usually means --subject-prefix="PATCH net-next vN"
But more importantly, this patch does not apply to netdev/net-next
> Changes since v2:
> - Compile-tested and hardware-tested on BPI-R4 (MT7988A, 6.12.87)
> - RTL8261BE (OEM/SFP-10G-T-I): probes MDIO_I2C_NONE, link Up 10Gbps
> - Genuine RollBall (OEM/SFP-10G-T): bridge detected, link Up 10Gbps
>
> drivers/net/mdio/mdio-i2c.c | 59 +++++++++++++++++++++++++++++++++----
> drivers/net/phy/sfp.c | 8 ++++-
> 2 files changed, 60 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/mdio/mdio-i2c.c b/drivers/net/mdio/mdio-i2c.c
> index da2001e..1973fda 100644
> --- a/drivers/net/mdio/mdio-i2c.c
> +++ b/drivers/net/mdio/mdio-i2c.c
> @@ -352,6 +352,52 @@ static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad,
> return 0;
> }
>
> +/*
> + * Probe for a RollBall I2C-to-MDIO bridge by issuing CMD_READ and waiting
> + * ~70 ms for CMD_DONE. Genuine RollBall bridges respond within that window.
> + * Modules that share the same EEPROM vendor/part strings but have no bridge
> + * (e.g. RTL8261BE pure copper media converter) never assert CMD_DONE, so
> + * -ENODEV is returned for them.
> + */
> +static int i2c_mii_probe_rollball(struct i2c_adapter *i2c)
> +{
> + u8 data_buf[] = { ROLLBALL_DATA_ADDR, 0x01, 0x00, 0x00 };
> + u8 cmd_buf[] = { ROLLBALL_CMD_ADDR, ROLLBALL_CMD_READ };
> + u8 cmd_addr = ROLLBALL_CMD_ADDR, result;
Is this code doing some magic or you're assigning to values to one
integer?
> + struct i2c_msg msgs[2];
> + int ret;
> +
> + msgs[0].addr = ROLLBALL_PHY_I2C_ADDR;
> + msgs[0].flags = 0;
> + msgs[0].len = sizeof(data_buf);
> + msgs[0].buf = data_buf;
> + msgs[1].addr = ROLLBALL_PHY_I2C_ADDR;
> + msgs[1].flags = 0;
> + msgs[1].len = sizeof(cmd_buf);
> + msgs[1].buf = cmd_buf;
> +
> + ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
> + if (ret)
> + return ret;
> +
> + msleep(70);
> +
> + msgs[0].addr = ROLLBALL_PHY_I2C_ADDR;
> + msgs[0].flags = 0;
> + msgs[0].len = 1;
> + msgs[0].buf = &cmd_addr;
> + msgs[1].addr = ROLLBALL_PHY_I2C_ADDR;
> + msgs[1].flags = I2C_M_RD;
> + msgs[1].len = 1;
> + msgs[1].buf = &result;
> +
> + ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
> + if (ret)
> + return ret;
> +
> + return result == ROLLBALL_CMD_DONE ? 0 : -ENODEV;
> +}
> +
> static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
> {
> struct i2c_msg msg;
> @@ -372,10 +418,10 @@ static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
> ret = i2c_transfer(i2c, &msg, 1);
> if (ret < 0)
> return ret;
> - else if (ret != 1)
> + if (ret != 1)
> return -EIO;
> - else
> - return 0;
> +
> + return i2c_mii_probe_rollball(i2c);
> }
>
> struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
> @@ -399,9 +445,10 @@ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
> case MDIO_I2C_ROLLBALL:
> ret = i2c_mii_init_rollball(i2c);
> if (ret < 0) {
> - dev_err(parent,
> - "Cannot initialize RollBall MDIO I2C protocol: %d\n",
> - ret);
> + if (ret != -ENODEV)
> + dev_err(parent,
> + "Cannot initialize RollBall MDIO I2C protocol: %d\n",
> + ret);
> mdiobus_free(mii);
> return ERR_PTR(ret);
> }
> diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
> index 218c775..ce745b6 100644
> --- a/drivers/net/phy/sfp.c
> +++ b/drivers/net/phy/sfp.c
> @@ -599,7 +599,8 @@ static const struct sfp_quirk sfp_quirks[] = {
> SFP_QUIRK_S("TP-LINK", "TL-SM410U", sfp_quirk_oem_2_5g),
>
> SFP_QUIRK_F("ETU", "ESP-T5-R", sfp_fixup_rollball_cc),
> - SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc),
> + SFP_QUIRK_F("OEM", "SFP-10G-T-I", sfp_fixup_rollball),
> + SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc),
> SFP_QUIRK_S("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
> SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex),
> SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex),
> @@ -802,6 +803,11 @@ static int sfp_i2c_mdiobus_create(struct sfp *sfp)
> int ret;
>
> i2c_mii = mdio_i2c_alloc(sfp->dev, sfp->i2c, sfp->mdio_protocol);
> + if (PTR_ERR_OR_ZERO(i2c_mii) == -ENODEV) {
> + /* RollBall probe found no bridge: no PHY on this module */
> + sfp->mdio_protocol = MDIO_I2C_NONE;
Uh, oh. Hope someone with SFP expertise will review v4.
> + return 0;
> + }
> if (IS_ERR(i2c_mii))
> return PTR_ERR(i2c_mii);
>
--
pw-bot: cr
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v4 1/2] phy: rockchip: inno-hdmi: Add configure() and validate() ops
From: sashiko-bot @ 2026-05-18 18:41 UTC (permalink / raw)
To: Jonas Karlman; +Cc: neil.armstrong, linux-phy, olteanv, vkoul
In-Reply-To: <20260518180722.2480799-2-jonas@kwiboo.se>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] inno_hdmi_phy_validate() performs an incomplete validation that allows unsupported (pixclock, tmdsclock) combinations to pass DRM's atomic_check, causing fatal failures during atomic_enable.
--
commit 09a1c2d66dc86a8ff3ab2a20274166576f00f813
Author: Jonas Karlman <jonas@kwiboo.se>
phy: rockchip: inno-hdmi: Add configure() and validate() ops
This commit introduces phy_configure() and phy_validate() operations for
the Inno HDMI PHY driver. It uses the TMDS character rate to validate and
configure the PHY, falling back to bus width if needed.
> diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
> --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
> +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
[ ... ]
> @@ -608,6 +608,57 @@ static irqreturn_t inno_hdmi_phy_rk3328_irq(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> +/*
> + * phy_validate() is expected to be called from encoder atomic_check(), before
> + * the hdmiphy pixel clock is known. Without knowing the actual pixel clock, we
> + * cannot do full validation of the configuration. Instead, we do a simple check
> + * that the pre-pll table contains an entry for the requested TMDS char rate.
> + */
> +static int inno_hdmi_phy_validate(struct phy *phy, enum phy_mode mode,
> + int submode, union phy_configure_opts *opts)
> +{
> + const struct pre_pll_config *cfg = pre_pll_cfg_table;
> + unsigned long tmdsclock;
> +
> + if (!(mode == PHY_MODE_HDMI && submode == PHY_HDMI_MODE_TMDS))
> + return -EINVAL;
> +
> + if (!opts)
> + return -EINVAL;
> +
> + if (!opts->hdmi.tmds_char_rate || opts->hdmi.tmds_char_rate > 594000000)
> + return -EINVAL;
> +
> + tmdsclock = opts->hdmi.tmds_char_rate;
> + for (; cfg->pixclock != 0; cfg++)
> + if (cfg->tmdsclock == tmdsclock)
> + return 0;
> +
> + return -EINVAL;
> +}
Could this incomplete validation allow unsupported pixclock and tmdsclock
combinations to pass DRM's atomic_check, leading to fatal failures later
during atomic_enable?
The DRM atomic API requires hardware constraints to be verified during
atomic_check. If pixclock is validated independently elsewhere (such as via
clk_round_rate), a mismatched pair might pass both checks.
For example, if a mode requests a pixclock of 54MHz with a 16-bit color depth
(which yields a tmdsclock of 108MHz), atomic_check would pass because 54MHz
exists in the pixclock column and 108MHz exists in the tmdsclock column.
However, later during atomic_enable, clk_set_rate() calls
inno_hdmi_phy_get_pre_pll_cfg() which searches for the exact pair:
inno_hdmi_phy_get_pre_pll_cfg() {
...
for (; cfg->pixclock != 0; cfg++)
if (cfg->pixclock == rate && cfg->tmdsclock == tmdsclock)
break;
if (cfg->pixclock == 0)
return ERR_PTR(-EINVAL);
...
}
Since the specific pair 54MHz and 108MHz does not exist in the table together,
it returns -EINVAL, causing the modeset to abort midway and leaving the
display pipeline broken.
Can the exact pixclock and tmdsclock combination be verified here instead,
perhaps by deriving pixclock from opts->hdmi.tmds_char_rate and the color
depth?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260518180722.2480799-1-jonas@kwiboo.se?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v4 2/2] phy: rockchip: inno-hdmi: Remove deprecated way to configure TMDS rate
From: Jonas Karlman @ 2026-05-18 18:07 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner
Cc: linux-phy, linux-rockchip, linux-arm-kernel, linux-kernel,
Jonas Karlman
In-Reply-To: <20260518180722.2480799-1-jonas@kwiboo.se>
The TMDS character rate of this PHY is configured using PHY bus width
in downstream vendor kernel and out-of-tree patches, however no in-tree
consumer of this PHY has ever called phy_set_bus_width() to change the
TMDS character rate as currently only 8-bit RGB output is supported by
the HDMI display driver.
The series "Split Generic PHY consumer and provider" clarifies that
phy_set_bus_width() is intended as a provider-only function.
Remove the deprecated unused fallback way to configure TMDS character
rate now that this HDMI PHY support using phy_configure() to configure
the TMDS character rate.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
v4: No change
v3: No change
v2: New patch, split from original patch
---
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 17 +----------------
1 file changed, 1 insertion(+), 16 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index 1c43ea700f0e..c3d257a0f4a6 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -555,24 +555,10 @@ static inline void inno_update_bits(struct inno_hdmi_phy *inno, u8 reg,
static unsigned long inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno,
unsigned long rate)
{
- int bus_width;
-
if (inno->opts_tmds_char_rate)
return inno->opts_tmds_char_rate;
- bus_width = phy_get_bus_width(inno->phy);
-
- switch (bus_width) {
- case 4:
- case 5:
- case 6:
- case 10:
- case 12:
- case 16:
- return (u64)rate * bus_width / 8;
- default:
- return rate;
- }
+ return rate;
}
static irqreturn_t inno_hdmi_phy_rk3328_hardirq(int irq, void *dev_id)
@@ -1452,7 +1438,6 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev)
phy_set_drvdata(inno->phy, inno);
phy_set_mode_ext(inno->phy, PHY_MODE_HDMI, PHY_HDMI_MODE_TMDS);
- phy_set_bus_width(inno->phy, 8);
if (inno->plat_data->ops->init) {
ret = inno->plat_data->ops->init(inno);
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v4 0/2] phy: rockchip: inno-hdmi: Change TMDS rate handling to configure() ops
From: Jonas Karlman @ 2026-05-18 18:07 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner
Cc: linux-phy, linux-rockchip, linux-arm-kernel, linux-kernel,
Jonas Karlman
This series adds support for using phy_validate() and phy_configure()
with this HDMI PHY as an alternative to current in-tree unused way of
using PHY bus width to configure the TMDS character rate.
The only known users that calls phy_set_bus_width() on this PHY are my
out-of-tree HDMI 2.0 patches for Rockchip RK3228/RK3328, i.e. those
originating from LibreELEC (also carried by other distros), the
downstream vendor kernel uses a different implementation that also calls
phy_set_bus_width() on this PHY.
Patch "drm/rockchip: dw_hdmi: Configure HDMI PHY in atomic_mode_set()"
that calls phy_validate() and phy_configure() on this PHY can be found
at [1].
[1] https://lore.kernel.org/dri-devel/20260510183114.1248840-10-jonas@kwiboo.se/
This series is part of a larger multi series effort to:
- phy: rockchip: inno-hdmi: Change TMDS rate handling to configure() ops [v4]
- drm/rockchip: dw_hdmi: Misc cleanup and propagate bus format [v2]
- drm: bridge: dw_hdmi: Misc enable/disable, CEC and EDID cleanup [v7]
- drm/bridge: dw-hdmi: Improve input/output bus format handling
- drm/bridge: dw-hdmi: Convert to a HDMI bridge and use of bridge connector
- drm/bridge: dw-hdmi: Add and use tmds_char_rate_valid() plat data ops
- drm/meson: hdmi: Misc cleanup and use CEC notifier helpers [v1]
- drm/rockchip: dw_hdmi: Enable YCbCr and Deep Color modes
Link to snapshot: https://github.com/Kwiboo/linux-rockchip/commits/next-20260518-rk-hdmi-v5/
Changes in v4:
- Add NULL opts check in validate()
- Only store the opts->hdmi.tmds_char_rate value for later use
- Move comments about expected consumer usage from inline to above the
functions
Link to v3: https://lore.kernel.org/linux-phy/20260515195512.1757363-1-jonas@kwiboo.se/
Changes in v3:
- Change validate() ops to only validate tmdsclock
- Add comments about expected consumer usage
- Update commit message with a typical call chain
Link to v2: https://lore.kernel.org/linux-phy/20260510095731.1222705-1-jonas@kwiboo.se/
Changes in v2:
- Split into two patches, one that adds new ops and a second that remove
the old and unused workaround
- Add validate() ops to validate that the TMDS rate is supported
Link to v1: https://lore.kernel.org/linux-phy/20260503172936.194003-1-jonas@kwiboo.se/
Jonas Karlman (2):
phy: rockchip: inno-hdmi: Add configure() and validate() ops
phy: rockchip: inno-hdmi: Remove deprecated way to configure TMDS rate
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 71 +++++++++++++++----
1 file changed, 58 insertions(+), 13 deletions(-)
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v4 1/2] phy: rockchip: inno-hdmi: Add configure() and validate() ops
From: Jonas Karlman @ 2026-05-18 18:07 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner
Cc: linux-phy, linux-rockchip, linux-arm-kernel, linux-kernel,
Jonas Karlman
In-Reply-To: <20260518180722.2480799-1-jonas@kwiboo.se>
The commit 10ed34d6eaaf ("phy: Add HDMI configuration options")
introduced a way for HDMI PHYs to be configured through the generic
phy_configure() function.
This driver derives the TMDS character rate from the pixel clock and the
PHY bus width setting. However, no in-tree consumer of this PHY has ever
called phy_set_bus_width() to change the TMDS character rate as only
8-bit RGB output is supported by the HDMI display driver.
Add configure() and validate() ops to allow consumers to configure the
TMDS character rate using phy_configure(). Fallback to the deprecated
way of using the PHY bus width to configure the TMDS character rate.
A typical call chain during DRM modeset on a RK3328 device:
dw_hdmi_rockchip_encoder_atomic_check():
- inno_hdmi_phy_validate(): pixclock 148500000 tmdsclock 594000000
dw_hdmi_rockchip_encoder_atomic_mode_set():
- inno_hdmi_phy_configure(): pixclock 148500000
- inno_hdmi_phy_validate(): pixclock 148500000 tmdsclock 594000000
vop_crtc_atomic_enable():
- inno_hdmi_phy_rk3328_clk_set_rate(): rate 594000000 tmdsclk 594000000
inno_hdmi_phy_rk3328_clk_set_rate(): pixclock 594000000 tmdsclock 594000000
- inno_hdmi_phy_rk3328_clk_recalc_rate(): pixclock 594000000 vco 594000000
dw_hdmi_rockchip_encoder_enable():
- inno_hdmi_phy_power_on(): Inno HDMI PHY Power On
- inno_hdmi_phy_rk3328_clk_set_rate(): rate 594000000 tmdsclk 594000000
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
Changes in v4:
- Add NULL opts check in validate()
- Only store the opts->hdmi.tmds_char_rate value for later use
- Move comments about expected consumer usage from inline to above the
functions
- Indent a set_rate() sub-call in the commit message call chain
Changes in v3:
- Change validate() ops to only validate tmdsclock
- Add comments about expected consumer usage
- Update commit message with a typical call chain
Changes in v2:
- Add validate() ops to validate that the TMDS rate is supported
- Split out parts that remove the old workaround into a separate patch
Patch "drm/rockchip: dw_hdmi: Configure HDMI PHY in atomic_mode_set()"
at [1] adds phy_validate() and phy_configure() calls for this HDMI PHY.
[1] https://lore.kernel.org/dri-devel/20260510183114.1248840-10-jonas@kwiboo.se/
---
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 62 ++++++++++++++++++-
1 file changed, 61 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index 1483907413fa..1c43ea700f0e 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -245,6 +245,7 @@ struct inno_hdmi_phy {
struct clk *phyclk;
unsigned long pixclock;
unsigned long tmdsclock;
+ unsigned long opts_tmds_char_rate;
};
struct pre_pll_config {
@@ -554,7 +555,12 @@ static inline void inno_update_bits(struct inno_hdmi_phy *inno, u8 reg,
static unsigned long inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno,
unsigned long rate)
{
- int bus_width = phy_get_bus_width(inno->phy);
+ int bus_width;
+
+ if (inno->opts_tmds_char_rate)
+ return inno->opts_tmds_char_rate;
+
+ bus_width = phy_get_bus_width(inno->phy);
switch (bus_width) {
case 4:
@@ -602,6 +608,57 @@ static irqreturn_t inno_hdmi_phy_rk3328_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/*
+ * phy_validate() is expected to be called from encoder atomic_check(), before
+ * the hdmiphy pixel clock is known. Without knowing the actual pixel clock, we
+ * cannot do full validation of the configuration. Instead, we do a simple check
+ * that the pre-pll table contains an entry for the requested TMDS char rate.
+ */
+static int inno_hdmi_phy_validate(struct phy *phy, enum phy_mode mode,
+ int submode, union phy_configure_opts *opts)
+{
+ const struct pre_pll_config *cfg = pre_pll_cfg_table;
+ unsigned long tmdsclock;
+
+ if (!(mode == PHY_MODE_HDMI && submode == PHY_HDMI_MODE_TMDS))
+ return -EINVAL;
+
+ if (!opts)
+ return -EINVAL;
+
+ if (!opts->hdmi.tmds_char_rate || opts->hdmi.tmds_char_rate > 594000000)
+ return -EINVAL;
+
+ tmdsclock = opts->hdmi.tmds_char_rate;
+ for (; cfg->pixclock != 0; cfg++)
+ if (cfg->tmdsclock == tmdsclock)
+ return 0;
+
+ return -EINVAL;
+}
+
+/*
+ * phy_configure() is expected to be called from encoder atomic_set_mode(),
+ * before the hdmiphy pixel clock is known. Store the requested TMDS character
+ * rate, so that it can be used later in power_on() and/or set_rate() when the
+ * pixel clock is known.
+ */
+static int inno_hdmi_phy_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct inno_hdmi_phy *inno = phy_get_drvdata(phy);
+ int ret;
+
+ ret = inno_hdmi_phy_validate(phy, phy_get_mode(phy),
+ PHY_HDMI_MODE_TMDS, opts);
+ if (ret)
+ return ret;
+
+ inno->opts_tmds_char_rate = opts->hdmi.tmds_char_rate;
+
+ return 0;
+}
+
static int inno_hdmi_phy_power_on(struct phy *phy)
{
struct inno_hdmi_phy *inno = phy_get_drvdata(phy);
@@ -670,6 +727,8 @@ static const struct phy_ops inno_hdmi_phy_ops = {
.owner = THIS_MODULE,
.power_on = inno_hdmi_phy_power_on,
.power_off = inno_hdmi_phy_power_off,
+ .configure = inno_hdmi_phy_configure,
+ .validate = inno_hdmi_phy_validate,
};
static const
@@ -1392,6 +1451,7 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev)
}
phy_set_drvdata(inno->phy, inno);
+ phy_set_mode_ext(inno->phy, PHY_MODE_HDMI, PHY_HDMI_MODE_TMDS);
phy_set_bus_width(inno->phy, 8);
if (inno->plat_data->ops->init) {
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH V1 3/3] phy: qcom-qmp-ufs: Add UFS PHY support on Hawi
From: sashiko-bot @ 2026-05-18 17:29 UTC (permalink / raw)
To: palash.kambar
Cc: robh, conor+dt, devicetree, vkoul, neil.armstrong, olteanv,
linux-phy
In-Reply-To: <20260518165346.1732548-4-palash.kambar@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing UFS HS-G4 gear configuration overlay for the Hawi PHY will leave critical hardware capabilities uninitialized during G4 negotiation.
--
commit a0d83dfe8a3203b506d45d5c8262f7f0b14d810a
Author: Palash Kambar <palash.kambar@oss.qualcomm.com>
phy: qcom-qmp-ufs: Add UFS PHY support on Hawi
Adds the initialization sequence tables and configuration for the UFS
QMP PHY found in the Hawi SoC.
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
[ ... ]
> @@ -1844,6 +1868,119 @@ static const struct qmp_phy_cfg sm8750_ufsphy_cfg = {
[ ... ]
> +static const struct qmp_phy_init_tbl hawi_ufsphy_g5_pcs[] = {
> + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_PLL_CNTL, 0x3b),
> + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x05),
> + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x05),
> +};
> +
> +static const struct qmp_phy_cfg hawi_ufsphy_cfg = {
> + .lanes = 2,
> +
> + .offsets = &qmp_ufs_offsets_v7,
> + .max_supported_gear = UFS_HS_G5,
> +
> + .tbls = {
> + .serdes = hawi_ufsphy_serdes,
> + .serdes_num = ARRAY_SIZE(hawi_ufsphy_serdes),
> + .tx = hawi_ufsphy_tx,
> + .tx_num = ARRAY_SIZE(hawi_ufsphy_tx),
> + .rx = hawi_ufsphy_rx,
> + .rx_num = ARRAY_SIZE(hawi_ufsphy_rx),
> + .pcs = hawi_ufsphy_pcs,
> + .pcs_num = ARRAY_SIZE(hawi_ufsphy_pcs),
> + },
> +
> + .tbls_hs_overlay[0] = {
> + .pcs = hawi_ufsphy_g5_pcs,
> + .pcs_num = ARRAY_SIZE(hawi_ufsphy_g5_pcs),
> + .max_gear = UFS_HS_G5,
> + },
> +
Is there a missing UFS HS-G4 gear configuration overlay for the Hawi PHY here?
When the UFS controller negotiates HS-G4 (e.g., connected to a G4-only
device or during fallback) and requests the PHY to operate at G4,
qmp_ufs_get_gear_overlay() will fail to find a matching configuration.
In qmp_ufs_get_gear_overlay(), it initializes floor_max_gear to 5.
It then checks the only available table where max_gear is 5.
Since submode (4) == max_gear (5) is false, and max_gear (5) <
floor_max_gear (5) is also false, it returns -EINVAL.
Because qmp_ufs_get_gear_overlay() returns -EINVAL,
qmp_ufs_init_registers() will skip applying any overlay. This means
capabilities like QPHY_V7_PCS_UFS_TX_HSGEAR_CAPABILITY are never
programmed during G4 operation.
Other G5-capable PHYs (like sm8750) seem to provide both G4 and G5 overlays:
static const struct qmp_phy_cfg sm8750_ufsphy_cfg = {
[ ... ]
.tbls_hs_overlay[0] = {
.pcs = sm8750_ufsphy_g4_pcs,
.pcs_num = ARRAY_SIZE(sm8750_ufsphy_g4_pcs),
.max_gear = UFS_HS_G4,
},
.tbls_hs_overlay[1] = {
.pcs = sm8650_ufsphy_g5_pcs,
.pcs_num = ARRAY_SIZE(sm8650_ufsphy_g5_pcs),
.max_gear = UFS_HS_G5,
},
[ ... ]
};
Will leaving these uninitialized cause link instability or initialization
failure on devices falling back to HS-G4?
> + .vreg_list = hawi_ufsphy_vreg_l,
> + .num_vregs = ARRAY_SIZE(hawi_ufsphy_vreg_l),
> + .regs = ufsphy_v7_regs_layout,
> +};
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260518165346.1732548-1-palash.kambar@oss.qualcomm.com?part=3
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH V1 2/3] dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Document the Hawi UFS controller
From: sashiko-bot @ 2026-05-18 17:09 UTC (permalink / raw)
To: palash.kambar
Cc: conor+dt, neil.armstrong, olteanv, devicetree, robh, vkoul,
linux-phy
In-Reply-To: <20260518165346.1732548-3-palash.kambar@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The commit title specifies the wrong subsystem and filename relative to the actual diff.
- [Medium] The new compatible string 'qcom,hawi-ufshc' is omitted from the `if-then` conditional block that enforces the MCQ register range constraints.
--
commit 1742ce0580b168eb19f1dc60a097e13ccbf8098d
Author: Palash Kambar <palash.kambar@oss.qualcomm.com>
dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Document the Hawi UFS controller
Document the UFS Controller on the Hawi Platform.
> dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Document the Hawi UFS controller
This isn't a bug, but the commit title specifies the wrong subsystem and
filename. It claims to modify the PHY binding qcom,sc8280xp-qmp-ufs-phy,
but the patch actually modifies the UFS host controller binding
Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml.
> diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
> index f28641c6e68fe..3de00affa4c64 100644
> --- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
> +++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
[ ... ]
> @@ -27,6 +28,7 @@ properties:
> items:
> - enum:
> - qcom,eliza-ufshc
> + - qcom,hawi-ufshc
> - qcom,kaanapali-ufshc
> - qcom,sm8650-ufshc
> - qcom,sm8750-ufshc
Does this code omit qcom,hawi-ufshc from the conditional block further down in
the file that enforces the MCQ register range constraints?
Looking at the allOf block in this file:
allOf:
- $ref: qcom,ufs-common.yaml
- if:
properties:
compatible:
contains:
enum:
- qcom,eliza-ufshc
then:
properties:
reg:
minItems: 2
reg-names:
minItems: 2
If the Hawi platform shares the same hardware constraints and requires the MCQ
registers, could this regression allow incomplete device trees missing the MCQ
registers to silently pass schema validation by being omitted from this block?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260518165346.1732548-1-palash.kambar@oss.qualcomm.com?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH V1 3/3] phy: qcom-qmp-ufs: Add UFS PHY support on Hawi
From: Dmitry Baryshkov @ 2026-05-18 17:09 UTC (permalink / raw)
To: palash.kambar
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, mani, alim.akhtar,
avri.altman, bvanassche, andersson, abel.vesa, luca.weiss,
linux-arm-msm, linux-phy, devicetree, linux-kernel, linux-scsi
In-Reply-To: <20260518165346.1732548-4-palash.kambar@oss.qualcomm.com>
On Mon, May 18, 2026 at 10:23:46PM +0530, palash.kambar@oss.qualcomm.com wrote:
> From: Palash Kambar <palash.kambar@oss.qualcomm.com>
>
> Add the init sequence tables and config for the UFS QMP phy found in
> the Hawi SoC.
>
> Signed-off-by: Palash Kambar <palash.kambar@oss.qualcomm.com>
> ---
> .../phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h | 22 +++
> .../phy-qcom-qmp-qserdes-txrx-ufs-v8.h | 37 +++++
> drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 140 ++++++++++++++++++
> 3 files changed, 199 insertions(+)
> create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
> create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
> new file mode 100644
> index 000000000000..bf914c752d22
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2026, The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef QCOM_PHY_QMP_PCS_UFS_V7_H_
> +#define QCOM_PHY_QMP_PCS_UFS_V7_H_
> +
> +/* Only for QMP V7 PHY - UFS PCS registers */
> +
> +#define QPHY_V7_PCS_UFS_PCS_CTRL1 0x01C
> +#define QPHY_V7_PCS_UFS_PLL_CNTL 0x028
> +#define QPHY_V7_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x02C
> +#define QPHY_V7_PCS_UFS_TX_HSGEAR_CAPABILITY 0x060
> +#define QPHY_V7_PCS_UFS_RX_HSGEAR_CAPABILITY 0x094
> +#define QPHY_V7_PCS_UFS_LINECFG_DISABLE 0x140
> +#define QPHY_V7_PCS_UFS_RX_SIGDET_CTRL2 0x150
> +#define QPHY_V7_PCS_UFS_READY_STATUS 0x16c
> +#define QPHY_V7_PCS_UFS_TX_MID_TERM_CTRL1 0x1b8
> +#define QPHY_V7_PCS_UFS_MULTI_LANE_CTRL1 0x1c0
> +
> +#endif
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
> new file mode 100644
> index 000000000000..5f923c3e64ec
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2026, The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_UFS_V8_H_
> +#define QCOM_PHY_QMP_QSERDES_TXRX_UFS_V8_H_
> +
> +#define QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_TX (0x34)
> +#define QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_RX (0x38)
> +#define QSERDES_UFS_V8_TX_LANE_MODE_1 (0x80)
> +#define QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE2 (0x1BC)
> +#define QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE4 (0x1C4)
> +#define QSERDES_UFS_V8_RX_UCDR_SO_GAIN_RATE4 (0x1DC)
> +#define QSERDES_UFS_V8_RX_EQ_OFFSET_ADAPTOR_CNTRL1 (0x2C8)
> +#define QSERDES_UFS_V8_RX_UCDR_PI_CONTROLS (0x1E4)
> +#define QSERDES_UFS_V8_RX_OFFSET_ADAPTOR_CNTRL3 (0x2D0)
> +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4 (0x120)
> +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_FO_GAIN_RATE4 (0xD4)
> +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_SO_GAIN_RATE4 (0xEC)
> +#define QSERDES_UFS_V8_RX_VGA_CAL_MAN_VAL (0x288)
> +#define QSERDES_UFS_V8_RX_EQU_ADAPTOR_CNTRL4 (0x2B0)
> +#define QSERDES_UFS_V8_RX_MODE_RATE_0_1_B4 (0x324)
> +#define QSERDES_UFS_V8_RX_MODE_RATE4_SA_B7 (0x3B4)
> +#define QSERDES_UFS_V8_RX_MODE_RATE4_SA_B9 (0x3BC)
> +#define QSERDES_UFS_V8_RX_MODE_RATE4_SB_B7 (0x3E0)
> +#define QSERDES_UFS_V8_RX_MODE_RATE4_SB_B9 (0x3E8)
> +#define QSERDES_UFS_V8_RX_MODE_RATE5_SA_B7 (0x40C)
> +#define QSERDES_UFS_V8_RX_MODE_RATE5_SA_B9 (0x414)
> +#define QSERDES_UFS_V8_RX_MODE_RATE5_SB_B7 (0x438)
> +#define QSERDES_UFS_V8_RX_MODE_RATE5_SB_B9 (0x440)
> +#define QSERDES_UFS_V8_RX_UCDR_SO_SATURATION (0xF4)
> +#define QSERDES_UFS_V8_RX_TERM_BW_CTRL0 (0x1AC)
> +#define QSERDES_UFS_V8_RX_DLL0_FTUNE_CTRL (0x498)
> +#define QSERDES_UFS_V8_RX_SIGDET_CAL_TRIM (0x4d0)
> +
> +#endif
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> index 771bc7c2ab50..a4801cf4b0fe 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> @@ -29,9 +29,11 @@
> #include "phy-qcom-qmp-pcs-ufs-v4.h"
> #include "phy-qcom-qmp-pcs-ufs-v5.h"
> #include "phy-qcom-qmp-pcs-ufs-v6.h"
> +#include "phy-qcom-qmp-pcs-ufs-v7.h"
>
> #include "phy-qcom-qmp-qserdes-txrx-ufs-v6.h"
> #include "phy-qcom-qmp-qserdes-txrx-ufs-v7.h"
> +#include "phy-qcom-qmp-qserdes-txrx-ufs-v8.h"
>
> /* QPHY_PCS_READY_STATUS bit */
> #define PCS_READY BIT(0)
> @@ -84,6 +86,13 @@ static const unsigned int ufsphy_v6_regs_layout[QPHY_LAYOUT_SIZE] = {
> [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL,
> };
>
> +static const unsigned int ufsphy_v7_regs_layout[QPHY_LAYOUT_SIZE] = {
> + [QPHY_START_CTRL] = QPHY_V6_PCS_UFS_PHY_START,
> + [QPHY_PCS_READY_STATUS] = QPHY_V7_PCS_UFS_READY_STATUS,
> + [QPHY_SW_RESET] = QPHY_V6_PCS_UFS_SW_RESET,
> + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL,
Don't mix V6 and V7 registers. And why is it v7? The rest of the
registers point out a v8 PHY.
> +};
> +
> static const struct qmp_phy_init_tbl milos_ufsphy_serdes[] = {
> QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
> QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
> @@ -1844,6 +1868,119 @@ static const struct qmp_phy_cfg sm8750_ufsphy_cfg = {
>
> };
>
> +static const struct qmp_phy_init_tbl hawi_ufsphy_serdes[] = {
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_CFG, 0x60),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x1f),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO_MODE1, 0x1f),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IETRIM, 0x07),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x20),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_CTRL, 0x40),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_ADAPTIVE_ANALOG_CONFIG, 0x06),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x06),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE0, 0x06),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE0, 0x18),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE0, 0x14),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x7f),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x06),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x92),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x4c),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x06),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x14),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE1, 0x06),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE1, 0x18),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE1, 0x14),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x99),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x07),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xbe),
> + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
Yep... If it is V8, use V8 registers. Even if they are they same.
> +};
> +
> +static const struct qmp_phy_init_tbl hawi_ufsphy_tx[] = {
> + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_LANE_MODE_1, 0x0c),
> + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_TX, 0x07),
> + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_RX, 0x17),
And it's V8.
> +};
> +
--
With best wishes
Dmitry
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH V1 1/3] dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Add Hawi UFS PHY compatible
From: sashiko-bot @ 2026-05-18 17:01 UTC (permalink / raw)
To: palash.kambar
Cc: robh, neil.armstrong, vkoul, olteanv, devicetree, linux-phy,
conor+dt
In-Reply-To: <20260518165346.1732548-2-palash.kambar@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `qcom,hawi-qmp-ufs-phy` compatible string is missing from the conditional blocks under `allOf`, leading to incomplete schema validation for clocks.
--
commit ef81b61a1b44f94e3b68451c007cd7fcd7dfb417
Author: Palash Kambar <palash.kambar@oss.qualcomm.com>
dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Add Hawi UFS PHY compatible
This commit documents the QMP UFS PHY compatible string for the Hawi SoC.
> diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
> index 9616c736b6d46..2326dcf38a467 100644
> --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
> @@ -37,6 +37,7 @@ properties:
> - qcom,kaanapali-qmp-ufs-phy
> - const: qcom,sm8750-qmp-ufs-phy
> - enum:
> + - qcom,hawi-qmp-ufs-phy
Should qcom,hawi-qmp-ufs-phy also be added to the conditional blocks under
allOf?
When a new compatible string is added to the top-level compatible enum but
omitted from the existing conditional blocks, it falls back to the top-level
schema validation.
This loose validation allows 2 or 3 clocks with any names, which means
device trees for the Hawi PHY with incorrect or missing clock properties
will silently pass validation.
> - qcom,milos-qmp-ufs-phy
> - qcom,msm8996-qmp-ufs-phy
> - qcom,msm8998-qmp-ufs-phy
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260518165346.1732548-1-palash.kambar@oss.qualcomm.com?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH V1 3/3] phy: qcom-qmp-ufs: Add UFS PHY support on Hawi
From: palash.kambar @ 2026-05-18 16:53 UTC (permalink / raw)
To: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, mani, alim.akhtar,
avri.altman, bvanassche, andersson, dmitry.baryshkov, abel.vesa,
luca.weiss
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel, linux-scsi,
Palash Kambar
In-Reply-To: <20260518165346.1732548-1-palash.kambar@oss.qualcomm.com>
From: Palash Kambar <palash.kambar@oss.qualcomm.com>
Add the init sequence tables and config for the UFS QMP phy found in
the Hawi SoC.
Signed-off-by: Palash Kambar <palash.kambar@oss.qualcomm.com>
---
.../phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h | 22 +++
.../phy-qcom-qmp-qserdes-txrx-ufs-v8.h | 37 +++++
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 140 ++++++++++++++++++
3 files changed, 199 insertions(+)
create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
new file mode 100644
index 000000000000..bf914c752d22
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_UFS_V7_H_
+#define QCOM_PHY_QMP_PCS_UFS_V7_H_
+
+/* Only for QMP V7 PHY - UFS PCS registers */
+
+#define QPHY_V7_PCS_UFS_PCS_CTRL1 0x01C
+#define QPHY_V7_PCS_UFS_PLL_CNTL 0x028
+#define QPHY_V7_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x02C
+#define QPHY_V7_PCS_UFS_TX_HSGEAR_CAPABILITY 0x060
+#define QPHY_V7_PCS_UFS_RX_HSGEAR_CAPABILITY 0x094
+#define QPHY_V7_PCS_UFS_LINECFG_DISABLE 0x140
+#define QPHY_V7_PCS_UFS_RX_SIGDET_CTRL2 0x150
+#define QPHY_V7_PCS_UFS_READY_STATUS 0x16c
+#define QPHY_V7_PCS_UFS_TX_MID_TERM_CTRL1 0x1b8
+#define QPHY_V7_PCS_UFS_MULTI_LANE_CTRL1 0x1c0
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
new file mode 100644
index 000000000000..5f923c3e64ec
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_UFS_V8_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_UFS_V8_H_
+
+#define QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_TX (0x34)
+#define QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_RX (0x38)
+#define QSERDES_UFS_V8_TX_LANE_MODE_1 (0x80)
+#define QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE2 (0x1BC)
+#define QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE4 (0x1C4)
+#define QSERDES_UFS_V8_RX_UCDR_SO_GAIN_RATE4 (0x1DC)
+#define QSERDES_UFS_V8_RX_EQ_OFFSET_ADAPTOR_CNTRL1 (0x2C8)
+#define QSERDES_UFS_V8_RX_UCDR_PI_CONTROLS (0x1E4)
+#define QSERDES_UFS_V8_RX_OFFSET_ADAPTOR_CNTRL3 (0x2D0)
+#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4 (0x120)
+#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_FO_GAIN_RATE4 (0xD4)
+#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_SO_GAIN_RATE4 (0xEC)
+#define QSERDES_UFS_V8_RX_VGA_CAL_MAN_VAL (0x288)
+#define QSERDES_UFS_V8_RX_EQU_ADAPTOR_CNTRL4 (0x2B0)
+#define QSERDES_UFS_V8_RX_MODE_RATE_0_1_B4 (0x324)
+#define QSERDES_UFS_V8_RX_MODE_RATE4_SA_B7 (0x3B4)
+#define QSERDES_UFS_V8_RX_MODE_RATE4_SA_B9 (0x3BC)
+#define QSERDES_UFS_V8_RX_MODE_RATE4_SB_B7 (0x3E0)
+#define QSERDES_UFS_V8_RX_MODE_RATE4_SB_B9 (0x3E8)
+#define QSERDES_UFS_V8_RX_MODE_RATE5_SA_B7 (0x40C)
+#define QSERDES_UFS_V8_RX_MODE_RATE5_SA_B9 (0x414)
+#define QSERDES_UFS_V8_RX_MODE_RATE5_SB_B7 (0x438)
+#define QSERDES_UFS_V8_RX_MODE_RATE5_SB_B9 (0x440)
+#define QSERDES_UFS_V8_RX_UCDR_SO_SATURATION (0xF4)
+#define QSERDES_UFS_V8_RX_TERM_BW_CTRL0 (0x1AC)
+#define QSERDES_UFS_V8_RX_DLL0_FTUNE_CTRL (0x498)
+#define QSERDES_UFS_V8_RX_SIGDET_CAL_TRIM (0x4d0)
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
index 771bc7c2ab50..a4801cf4b0fe 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -29,9 +29,11 @@
#include "phy-qcom-qmp-pcs-ufs-v4.h"
#include "phy-qcom-qmp-pcs-ufs-v5.h"
#include "phy-qcom-qmp-pcs-ufs-v6.h"
+#include "phy-qcom-qmp-pcs-ufs-v7.h"
#include "phy-qcom-qmp-qserdes-txrx-ufs-v6.h"
#include "phy-qcom-qmp-qserdes-txrx-ufs-v7.h"
+#include "phy-qcom-qmp-qserdes-txrx-ufs-v8.h"
/* QPHY_PCS_READY_STATUS bit */
#define PCS_READY BIT(0)
@@ -84,6 +86,13 @@ static const unsigned int ufsphy_v6_regs_layout[QPHY_LAYOUT_SIZE] = {
[QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL,
};
+static const unsigned int ufsphy_v7_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_START_CTRL] = QPHY_V6_PCS_UFS_PHY_START,
+ [QPHY_PCS_READY_STATUS] = QPHY_V7_PCS_UFS_READY_STATUS,
+ [QPHY_SW_RESET] = QPHY_V6_PCS_UFS_SW_RESET,
+ [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL,
+};
+
static const struct qmp_phy_init_tbl milos_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
@@ -1306,6 +1315,12 @@ static const struct regulator_bulk_data sm8750_ufsphy_vreg_l[] = {
{ .supply = "vdda-pll", .init_load_uA = 18300 },
};
+static const struct regulator_bulk_data hawi_ufsphy_vreg_l[] = {
+ { .supply = "vdda-phy", .init_load_uA = 324000 },
+ { .supply = "vdda-pll", .init_load_uA = 27000 },
+
+};
+
static const struct qmp_ufs_offsets qmp_ufs_offsets = {
.serdes = 0,
.pcs = 0xc00,
@@ -1324,6 +1339,15 @@ static const struct qmp_ufs_offsets qmp_ufs_offsets_v6 = {
.rx2 = 0x1a00,
};
+static const struct qmp_ufs_offsets qmp_ufs_offsets_v7 = {
+ .serdes = 0,
+ .pcs = 0x0400,
+ .tx = 0x2000,
+ .rx = 0x2000,
+ .tx2 = 0x3000,
+ .rx2 = 0x3000,
+};
+
static const struct qmp_phy_cfg milos_ufsphy_cfg = {
.lanes = 2,
@@ -1844,6 +1868,119 @@ static const struct qmp_phy_cfg sm8750_ufsphy_cfg = {
};
+static const struct qmp_phy_init_tbl hawi_ufsphy_serdes[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_CFG, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO_MODE1, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IETRIM, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_CTRL, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_ADAPTIVE_ANALOG_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x92),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x4c),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xbe),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
+};
+
+static const struct qmp_phy_init_tbl hawi_ufsphy_tx[] = {
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_LANE_MODE_1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_TX, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_RX, 0x17),
+};
+
+static const struct qmp_phy_init_tbl hawi_ufsphy_rx[] = {
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE4, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_SO_GAIN_RATE4, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_PI_CONTROLS, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_OFFSET_ADAPTOR_CNTRL3, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FASTLOCK_FO_GAIN_RATE4, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FASTLOCK_SO_GAIN_RATE4, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_VGA_CAL_MAN_VAL, 0x8e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_EQU_ADAPTOR_CNTRL4, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE_0_1_B4, 0xb8),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE4_SA_B7, 0x66),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE4_SA_B9, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE4_SB_B7, 0x66),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE4_SB_B9, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE5_SA_B7, 0x66),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE5_SA_B9, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE5_SB_B7, 0x66),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE5_SB_B9, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_SO_SATURATION, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_TERM_BW_CTRL0, 0xfa),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_DLL0_FTUNE_CTRL, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_SIGDET_CAL_TRIM, 0x77),
+};
+
+static const struct qmp_phy_init_tbl hawi_ufsphy_pcs[] = {
+ QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
+ QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_PCS_CTRL1, 0x42),
+ QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_RX_SIGDET_CTRL2, 0x68),
+ QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
+};
+
+static const struct qmp_phy_init_tbl hawi_ufsphy_g5_pcs[] = {
+ QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_PLL_CNTL, 0x3b),
+ QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x05),
+ QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x05),
+};
+
+static const struct qmp_phy_cfg hawi_ufsphy_cfg = {
+ .lanes = 2,
+
+ .offsets = &qmp_ufs_offsets_v7,
+ .max_supported_gear = UFS_HS_G5,
+
+ .tbls = {
+ .serdes = hawi_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(hawi_ufsphy_serdes),
+ .tx = hawi_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(hawi_ufsphy_tx),
+ .rx = hawi_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(hawi_ufsphy_rx),
+ .pcs = hawi_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(hawi_ufsphy_pcs),
+ },
+
+ .tbls_hs_overlay[0] = {
+ .pcs = hawi_ufsphy_g5_pcs,
+ .pcs_num = ARRAY_SIZE(hawi_ufsphy_g5_pcs),
+ .max_gear = UFS_HS_G5,
+ },
+
+ .vreg_list = hawi_ufsphy_vreg_l,
+ .num_vregs = ARRAY_SIZE(hawi_ufsphy_vreg_l),
+ .regs = ufsphy_v7_regs_layout,
+};
+
static void qmp_ufs_serdes_init(struct qmp_ufs *qmp, const struct qmp_phy_cfg_tbls *tbls)
{
void __iomem *serdes = qmp->serdes;
@@ -2258,6 +2395,9 @@ static int qmp_ufs_probe(struct platform_device *pdev)
static const struct of_device_id qmp_ufs_of_match_table[] = {
{
+ .compatible = "qcom,hawi-qmp-ufs-phy",
+ .data = &hawi_ufsphy_cfg,
+ }, {
.compatible = "qcom,milos-qmp-ufs-phy",
.data = &milos_ufsphy_cfg,
}, {
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH V1 2/3] dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Document the Hawi UFS controller
From: palash.kambar @ 2026-05-18 16:53 UTC (permalink / raw)
To: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, mani, alim.akhtar,
avri.altman, bvanassche, andersson, dmitry.baryshkov, abel.vesa,
luca.weiss
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel, linux-scsi,
Palash Kambar
In-Reply-To: <20260518165346.1732548-1-palash.kambar@oss.qualcomm.com>
From: Palash Kambar <palash.kambar@oss.qualcomm.com>
Document the UFS Controller on the Hawi Platform.
Signed-off-by: Palash Kambar <palash.kambar@oss.qualcomm.com>
---
Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
index f28641c6e68f..3de00affa4c6 100644
--- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
+++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
@@ -16,6 +16,7 @@ select:
contains:
enum:
- qcom,eliza-ufshc
+ - qcom,hawi-ufshc
- qcom,kaanapali-ufshc
- qcom,sm8650-ufshc
- qcom,sm8750-ufshc
@@ -27,6 +28,7 @@ properties:
items:
- enum:
- qcom,eliza-ufshc
+ - qcom,hawi-ufshc
- qcom,kaanapali-ufshc
- qcom,sm8650-ufshc
- qcom,sm8750-ufshc
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH V1 1/3] dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Add Hawi UFS PHY compatible
From: palash.kambar @ 2026-05-18 16:53 UTC (permalink / raw)
To: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, mani, alim.akhtar,
avri.altman, bvanassche, andersson, dmitry.baryshkov, abel.vesa,
luca.weiss
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel, linux-scsi,
Palash Kambar
In-Reply-To: <20260518165346.1732548-1-palash.kambar@oss.qualcomm.com>
From: Palash Kambar <palash.kambar@oss.qualcomm.com>
Document QMP UFS PHY compatible for Hawi SoC.
Signed-off-by: Palash Kambar <palash.kambar@oss.qualcomm.com>
---
.../devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
index 9616c736b6d4..2326dcf38a46 100644
--- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
@@ -37,6 +37,7 @@ properties:
- qcom,kaanapali-qmp-ufs-phy
- const: qcom,sm8750-qmp-ufs-phy
- enum:
+ - qcom,hawi-qmp-ufs-phy
- qcom,milos-qmp-ufs-phy
- qcom,msm8996-qmp-ufs-phy
- qcom,msm8998-qmp-ufs-phy
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH V1 0/3] Add Hawi UFS PHY and Controller support
From: palash.kambar @ 2026-05-18 16:53 UTC (permalink / raw)
To: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, mani, alim.akhtar,
avri.altman, bvanassche, andersson, dmitry.baryshkov, abel.vesa,
luca.weiss
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel, linux-scsi,
Palash Kambar
From: Palash Kambar <palash.kambar@oss.qualcomm.com>
This series introduces devicetree binding documentation and PHY
initialization support required to enable UFS on this platform.
1. Devicetree binding documentation for the QMP UFS PHY
used on Qualcomm Hawi.
2. Devicetree binding documentation for the UFS controller
instance present on the Hawi platform.
3. Initialization sequence tables and configuration required
for the QMP UFS PHY on Hawi SoC.
Palash Kambar (3):
dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Add Hawi UFS PHY
compatible
dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Document the Hawi UFS
controller
phy: qcom-qmp-ufs: Add UFS PHY support on Hawi
.../phy/qcom,sc8280xp-qmp-ufs-phy.yaml | 1 +
.../bindings/ufs/qcom,sm8650-ufshc.yaml | 2 +
.../phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h | 22 +++
.../phy-qcom-qmp-qserdes-txrx-ufs-v8.h | 37 +++++
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 140 ++++++++++++++++++
5 files changed, 202 insertions(+)
create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h
create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support
From: Dmitry Baryshkov @ 2026-05-18 15:38 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Konrad Dybcio, Vinod Koul, Neil Armstrong, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, linux-kernel,
linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter
In-Reply-To: <9aad8e45-b0a5-4c59-8793-8c0747d8fafa@oss.qualcomm.com>
On Mon, May 18, 2026 at 04:15:16PM +0200, Konrad Dybcio wrote:
> On 5/18/26 3:57 PM, Dmitry Baryshkov wrote:
> > On Mon, May 18, 2026 at 12:29:50PM +0200, Konrad Dybcio wrote:
> >> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> >>
> >> Some Combo PHYs (so far only on SC8280XP, X1E80100 and Glymur), come in
> >> a flavor called USB43DP, which as the name implies, features USB4, USB3
> >> and DP signal processing capabilities. In that architecture, USB3 and
> >> USB4 PHYs share the same USB_PLL while featuring separate logic spaces.
> >> The DP part is roughly the same as on the instances without USB4.
> >>
> >> The USB4 and USB3/DP operation modes of the PHY are mutually exclusive.
> >> Only one USB protocol (and flavor of pipe clock) can be active at a
> >> given moment (not to be confused with USB3 not being able to be
> >> tunneled as USB4 packets - that of course remains possible).
> >> The DP PLL is still used for clocking tunneled DP links. It may be
> >> turned off to save power when no tunnels are active, but that's left as
> >> a TODO item for now.
> >>
> >> Due to the nature of USB4, the Type-C handling happens entirely inside
> >> the Host Router, and as such the QMPPHY's mux_set() function is
> >> nullified for the period when USB4 PHY remains active. This is strictly
> >> necessary, as the Host Router driver is going to excercise manual
> >> control over the USB4 PHY's power state, which is needed by the suspend
> >> and resume flows. Failure to control that synchronously with other
> >> parts of the code results in a SoC crash by unlocked access.
> >>
> >> Because of that, a new struct phy is spawned to expose the USB4 mode,
> >> along with a .set_mode callback to allow toggling between USB4 and TBT3
> >> submodes.
> >>
> >> Thunderbolt 3, having a number of differences vs USB4, requires a
> >> couple specific overrides, pertaining to electrical characteristics,
> >> which are easily accommodated for.
> >>
> >> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> >> ---
> >> drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 392 ++++++++++++++++++++++++------
> >> 1 file changed, 322 insertions(+), 70 deletions(-)
> >>
> >
> > Overall it looks good. The major question (after looking at TODOs), do
> > we need a separate submode for USB+DP / TBT+DP?
>
> The problem space is as follows:
>
> After a TBT (collectively TBT3+ and USB4) link has been established and
> we have a link partner, we may (based on the HW capabilities and user
> config, such as kernel params but not only) start or stop a DP tunnel at
> runtime. On Qualcomm hardware, the PHY is kept in USB4 mode and its DP
> AUX lines are not used (instead, the encapsulated DP AUX packets are r/w
> entirely within the USB4 subsystem via a pair of FIFOs that Linux sees
> as a separate DP AUX host)
So far so good. But I still don't grok if having a DP-over-USB4 is a
separate submode or not. I.e. I see code (and TODOs) to detect and
handle DP going on and off. Would it be better if we specify that
explicitly?
> Then, on hamoa/glymur specifically, any of the 3 USB4-capable DP hosts
> can be muxed to either of the 2 DPIN ports on any of the 3 USB4 routers
> (and each of these routers is hardwired to one of the PHYs).
>
> To underline, we have 3 DP producers and 6 consumers. If there's e.g. a
> super high-res display at one of the physical ports, or a long
> daisy-chain, we may need to use 2 DPTXes to service 1 receptacle. Then,
> we would only need one of the PHYs (associated with the router that's
> wired to that port) to provide a DP clock.
>
> This, along with the normal (logical or physical) present/absent status
> can change at runtime. My plan is to use phy_set_opts(dp_tunelling=true)
> or something along those lines to toggle that bit as necessary
I don't see phy_set_opts(). So maybe a submode then...
--
With best wishes
Dmitry
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 2/5] phy: core: Define TBT phy_mode
From: Dmitry Baryshkov @ 2026-05-18 15:19 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Konrad Dybcio, Vinod Koul, Neil Armstrong, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, linux-kernel,
linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter
In-Reply-To: <0880f634-ea56-4c0e-822d-aa7c056fdcc3@oss.qualcomm.com>
On Mon, May 18, 2026 at 02:29:26PM +0200, Konrad Dybcio wrote:
> On 5/18/26 2:25 PM, Dmitry Baryshkov wrote:
> > On Mon, May 18, 2026 at 12:29:49PM +0200, Konrad Dybcio wrote:
> >> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> >>
> >> There exist OS-accessible USB4 and Thunderbolt PHYs that need specific
> >> configuration. Define a new phy_mode for them.
> >>
> >> Currently, USB4 and TBT3 are defined as submodes, because they're quite
> >> distinct, most visibly in the electrical/analog aspects (slightly
> >> different frequencies, timings, etc.). This results in a need to make
> >> the PHY aware of the actual mode needed (at least in the Qualcomm
> >> implementation, but I believe that'll be a general need).
> >>
> >> Newer versions of TBT are basically supersets of USB4 with higher
> >> host-side requirements, so these are not defined. This can always be
> >> changed as necessary.
> >>
> >> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> >> ---
> >> include/linux/phy/phy-tbt.h | 14 ++++++++++++++
> >> include/linux/phy/phy.h | 2 ++
> >> 2 files changed, 16 insertions(+)
> >>
> >> diff --git a/include/linux/phy/phy-tbt.h b/include/linux/phy/phy-tbt.h
> >> new file mode 100644
> >> index 000000000000..5f48059814e1
> >> --- /dev/null
> >> +++ b/include/linux/phy/phy-tbt.h
> >> @@ -0,0 +1,14 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> >> + */
> >> +
> >> +#ifndef __PHY_TBT_H_
> >> +#define __PHY_TBT_H_
> >> +
> >> +enum tbt_phy_submode {
> >> + PHY_SUBMODE_TBT3,
> >> + PHY_SUBMODE_USB4,
> >
> > Do we need TBT2 as a separate submode?
>
> I would like to avoid extending that unless there's a user for it
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox