* [PATCH v2 0/2] phy: qcom: qmp-combo: keep the Type-C callbacks off unclocked registers
@ 2026-09-16 12:56 Oleg Keri
2026-09-16 12:56 ` [PATCH v2 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks Oleg Keri
2026-09-16 12:56 ` [PATCH v2 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() " Oleg Keri
0 siblings, 2 replies; 4+ messages in thread
From: Oleg Keri @ 2026-09-16 12:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Johan Hovold,
Bjorn Andersson
Cc: linux-arm-msm, linux-phy, linux-kernel, Konrad Dybcio
The typec_switch and typec_mux callbacks of the QMP combo PHY tear the
common block down and bring it back up with force=true on every USB-C
orientation or altmode change. Two things go wrong on that path.
Patch 1: the callbacks hold no runtime PM reference, so releasing the
clocks during the teardown can drop the device's last reference and
re-enter qmp_combo_runtime_suspend() from clk_core_unprepare(), which
then disables clocks that are already being torn down. Seen as
"gcc_usb3_prim_phy_pipe_clk already disabled" warnings from the
pmic_glink_altmode_worker on a Lenovo Yoga Slim 7x Gen 11 (Snapdragon X2
Elite) once runtime PM is allowed from userspace.
Patch 2: the result of the forced qmp_combo_com_init() is discarded, so a
failed bringup is followed by register writes with the clocks off. Found
by inspection; it needs the bringup to fail.
Both were posted separately on 2026-09-09, patch 1 on its own and patch 2
as 2/2 of "fix forced com_init() error handling", whose 1/2 was withdrawn
the same day and is not resent: its remedy was wrong, and the init_count
imbalance it described is left for a separate patch.
Tested on next-20260915 on the Yoga Slim 7x Gen 11 with a USB-C DisplayPort
display: plug, unplug and orientation changes, no clock warnings.
Changes in v2:
- Rebased on next-20260915, where "phy: qualcomm: qmp-combo: Add
preliminary USB4 support" moved the mux callback's teardown and bringup
into qmp_combo_reconfigure_phy(). The runtime PM reference is now
taken in both callbacks before that helper runs; the com_init() check
moved into the helper, so qmp_combo_usb4_init(), which already checks
the helper's return value, sees the failure too.
- Both patches carry a Fixes: tag for the commit that introduced the
forced re-init path.
- Link to patch 1 v1: https://lore.kernel.org/all/20260909140345.4239-1-okerixx@gmail.com/
- Link to patch 2 v1: https://lore.kernel.org/all/20260909151443.10276-3-okerixx@gmail.com/
Oleg Keri (2):
phy: qcom: qmp-combo: hold a runtime PM reference in the typec
callbacks
phy: qcom: qmp-combo: check qmp_combo_com_init() in the typec
callbacks
drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
--
2.55.0
base-commit: a7728f5e1fc3d472a314acdabca6039f71ec3a9d
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v2 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks 2026-09-16 12:56 [PATCH v2 0/2] phy: qcom: qmp-combo: keep the Type-C callbacks off unclocked registers Oleg Keri @ 2026-09-16 12:56 ` Oleg Keri 2026-09-16 12:56 ` [PATCH v2 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() " Oleg Keri 1 sibling, 0 replies; 4+ messages in thread From: Oleg Keri @ 2026-09-16 12:56 UTC (permalink / raw) To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Johan Hovold, Bjorn Andersson Cc: linux-arm-msm, linux-phy, linux-kernel, Konrad Dybcio qmp_combo_typec_switch_set() and qmp_combo_typec_mux_set() tear the common block down and bring it straight back up, calling qmp_combo_com_exit() and qmp_combo_com_init() with force=true. Both release and re-acquire the PHY clocks. Unlike the PHY operations, which the PHY core always invokes with a runtime PM reference held - phy_pm_runtime_get_sync() in phy_init(), phy_exit() and phy_power_on() - these two are typec_switch and typec_mux callbacks and hold no such reference. Releasing the clocks is then enough to drop the device's last reference, so clk_core_unprepare() runs pm_runtime_idle() and re-enters the driver through qmp_combo_runtime_suspend(), which disables the very clocks that are being torn down: gcc_usb3_prim_phy_pipe_clk already disabled WARNING: drivers/clk/clk.c:1259 at clk_core_disable+0x298/0x300 Workqueue: events_freezable pmic_glink_altmode_worker clk_disable qmp_combo_runtime_suspend pm_generic_runtime_suspend genpd_runtime_suspend rpm_suspend rpm_idle __pm_runtime_idle clk_core_unprepare clk_core_unprepare clk_core_unprepare qmp_combo_runtime_suspend() only checks init_count, which is still non-zero at that point, so it proceeds and the clock enable and prepare counts underflow. Runtime PM is forbidden at probe, so this only becomes reachable once userspace opts in through power/control - which is exactly what the comment above pm_runtime_forbid() invites it to do. Hold a runtime PM reference across the teardown and bringup so releasing the clocks cannot re-enter the driver's own suspend callback. Fixes: 2851117f8f42 ("phy: qcom-qmp-combo: Introduce orientation switching") Signed-off-by: Oleg Keri <okerixx@gmail.com> --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index fa1a91e67549..f6d3320a59d9 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -16,6 +16,7 @@ #include <linux/of_graph.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> @@ -5179,6 +5180,8 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw, qmp->orientation = orientation; if (qmp->init_count) { + guard(pm_runtime_active)(qmp->dev); + if (qmp->usb_init_count || qmp->usb4_init_count) qmp_combo_usb_power_off(qmp->usb_phy); qmp_combo_com_exit(qmp, true); @@ -5265,6 +5268,8 @@ static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_s if (!qmp->init_count) return 0; + guard(pm_runtime_active)(qmp->dev); + return qmp_combo_reconfigure_phy(qmp, new_mode); } -- 2.55.0 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() in the typec callbacks 2026-09-16 12:56 [PATCH v2 0/2] phy: qcom: qmp-combo: keep the Type-C callbacks off unclocked registers Oleg Keri 2026-09-16 12:56 ` [PATCH v2 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks Oleg Keri @ 2026-09-16 12:56 ` Oleg Keri 2026-09-16 13:07 ` sashiko-bot 1 sibling, 1 reply; 4+ messages in thread From: Oleg Keri @ 2026-09-16 12:56 UTC (permalink / raw) To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Johan Hovold, Bjorn Andersson Cc: linux-arm-msm, linux-phy, linux-kernel, Konrad Dybcio qmp_combo_typec_switch_set() and qmp_combo_reconfigure_phy(), which the typec mux callback and the USB4 init path use, tear the common block down and bring it straight back up on an orientation or altmode change, but they discard the result of the bringup: qmp_combo_com_exit(qmp, true); qmp->qmpphy_mode = new_mode; /* Now everything's powered down, power up the right PHYs */ qmp_combo_com_init(qmp, true); if ((qmp->usb_init_count || qmp->usb4_init_count) && new_mode != QMPPHY_MODE_DP_ONLY) qmp_combo_usb_power_on(qmp->usb_phy); If qmp_combo_com_init() fails - a regulator, a reset or clk_bulk_prepare_enable() - it unwinds what it had brought up and returns an error, leaving the clocks disabled. Both paths then carry on and qmp_combo_usb_power_on(), and dp_aux_init() after it, write PHY registers with no clock running. qmp_combo_usb4_init() already checks the return value of qmp_combo_reconfigure_phy(), but never sees a failure from it. Propagate the failure instead. The PHY is then left logically initialised with its clocks off, which is the state any forced bringup failure already produces and which these callbacks have no way to report to the PHY consumers; what changes is that the register writes that would fault no longer happen. Fixes: 2851117f8f42 ("phy: qcom-qmp-combo: Introduce orientation switching") Signed-off-by: Oleg Keri <okerixx@gmail.com> --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index f6d3320a59d9..ae70ebba36c4 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -4601,6 +4601,8 @@ static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submo static int qmp_combo_reconfigure_phy(struct qmp_combo *qmp, enum qmpphy_mode new_mode) { + int ret; + dev_dbg(qmp->dev, "qmp_combo_reconfigure_phy: switching from qmpphy mode %d to %d\n", qmp->qmpphy_mode, new_mode); @@ -4615,7 +4617,9 @@ static int qmp_combo_reconfigure_phy(struct qmp_combo *qmp, enum qmpphy_mode new qmp->qmpphy_mode = new_mode; /* Now everything's powered down, power up the right PHYs */ - qmp_combo_com_init(qmp, true); + ret = qmp_combo_com_init(qmp, true); + if (ret) + return ret; if ((qmp->usb_init_count || qmp->usb4_init_count) && new_mode != QMPPHY_MODE_DP_ONLY) @@ -5166,6 +5170,7 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw, { struct qmp_combo *qmp = typec_switch_get_drvdata(sw); const struct qmp_phy_cfg *cfg = qmp->cfg; + int ret; if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) { /* QMPPHY has no orientation handling in USB4 mode, don't cache the setting */ @@ -5186,7 +5191,10 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw, qmp_combo_usb_power_off(qmp->usb_phy); qmp_combo_com_exit(qmp, true); - qmp_combo_com_init(qmp, true); + ret = qmp_combo_com_init(qmp, true); + if (ret) + return ret; + if (qmp->usb_init_count || qmp->usb4_init_count) qmp_combo_usb_power_on(qmp->usb_phy); if (qmp->dp_init_count) -- 2.55.0 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() in the typec callbacks 2026-09-16 12:56 ` [PATCH v2 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() " Oleg Keri @ 2026-09-16 13:07 ` sashiko-bot 0 siblings, 0 replies; 4+ messages in thread From: sashiko-bot @ 2026-09-16 13:07 UTC (permalink / raw) To: Oleg Keri; +Cc: neil.armstrong, olteanv, vkoul, linux-phy Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] Deferred kernel panic in phy_power_off on com_init failure - [High] PHY remains permanently disabled on transient com_init failure -- commit 0e7d331798c0632ff5cbed4bc3728d8c7367506b Author: Oleg Keri <okerixx@gmail.com> phy: qcom: qmp-combo: check qmp_combo_com_init() in the typec callbacks This commit propagates the return value of qmp_combo_com_init() in qmp_combo_typec_switch_set() and qmp_combo_reconfigure_phy(). It aims to prevent writing to PHY registers with no clock running when the common block initialization fails. > diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c > index dad7c5cfcf0a6..50790a49f4cf4 100644 > --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c > +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c [ ... ] > @@ -4471,7 +4473,9 @@ static int qmp_combo_reconfigure_phy(struct qmp_combo *qmp, enum qmpphy_mode new > qmp->qmpphy_mode = new_mode; > > /* Now everything's powered down, power up the right PHYs */ > - qmp_combo_com_init(qmp, true); > + ret = qmp_combo_com_init(qmp, true); > + if (ret) > + return ret; [Severity: High] Could returning early here leave the PHY permanently disabled if a transient error occurs? Because qmp->qmpphy_mode is updated to new_mode just before this check, if com_init fails and returns early, future type-c events for the same mode will hit the cached mode check and skip hardware bringup entirely. Additionally, could this early return cause a deferred kernel panic? If com_init fails, the PHY clocks remain disabled but the PHY consumers (like USB or DP subsystems) maintain their internal logical power state. When the consumer later shuts down or suspends, it calls phy_power_off(), which executes qmp_combo_usb_power_off() or qmp_combo_dp_power_off(). For example, qmp_combo_usb_power_off() unconditionally accesses registers: qmp_combo_usb_power_off() qphy_setbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); Would this unclocked register access cause a synchronous external abort? > > if ((qmp->usb_init_count || qmp->usb4_init_count) && > new_mode != QMPPHY_MODE_DP_ONLY) [ ... ] > @@ -5042,7 +5047,10 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw, > qmp_combo_usb_power_off(qmp->usb_phy); > qmp_combo_com_exit(qmp, true); > > - qmp_combo_com_init(qmp, true); > + ret = qmp_combo_com_init(qmp, true); > + if (ret) > + return ret; > + [Severity: High] Similar to the issue above, could returning early here cause a deferred kernel panic when phy_power_off() is later called by a consumer? Also, earlier in this function (before this quoted context), qmp->orientation is updated: if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE) return 0; guard(mutex)(&qmp->phy_mutex); qmp->orientation = orientation; if (qmp->init_count) { ... ret = qmp_combo_com_init(qmp, true); if (ret) return ret; If we return early here due to a transient error, will future orientation switch events hit the cached orientation check and skip the hardware bringup, leaving the PHY silently broken for that orientation? > if (qmp->usb_init_count || qmp->usb4_init_count) > qmp_combo_usb_power_on(qmp->usb_phy); > if (qmp->dp_init_count) -- Sashiko AI review · https://sashiko.dev/#/patchset/20260916125619.4587-1-okerixx@gmail.com?part=2 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-16 13:07 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-16 12:56 [PATCH v2 0/2] phy: qcom: qmp-combo: keep the Type-C callbacks off unclocked registers Oleg Keri 2026-09-16 12:56 ` [PATCH v2 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks Oleg Keri 2026-09-16 12:56 ` [PATCH v2 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() " Oleg Keri 2026-09-16 13:07 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox