* [PATCH v3 0/2] phy: qcom: qmp-combo: keep the Type-C callbacks off unclocked registers
@ 2026-09-16 13:38 Oleg Keri
2026-09-16 13:38 ` [PATCH v3 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks Oleg Keri
2026-09-16 13:38 ` [PATCH v3 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() " Oleg Keri
0 siblings, 2 replies; 6+ messages in thread
From: Oleg Keri @ 2026-09-16 13:38 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.
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 v3:
- Patch 2: on a failed bringup restore the previously cached mode and
clear the cached orientation, so the next Type-C event retries instead
of being short-circuited as already configured (Sashiko review on v2).
The commit message now also says plainly that a consumer's later
phy_power_off() still reaches an unclocked PHY until that retry; that
is pre-existing and not addressed by this series.
- Patch 1: unchanged.
- Link to v2: https://lore.kernel.org/all/20260916125619.4587-1-okerixx@gmail.com/
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 | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
--
2.55.0
base-commit: a7728f5e1fc3d472a314acdabca6039f71ec3a9d
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v3 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks 2026-09-16 13:38 [PATCH v3 0/2] phy: qcom: qmp-combo: keep the Type-C callbacks off unclocked registers Oleg Keri @ 2026-09-16 13:38 ` Oleg Keri 2026-10-21 9:53 ` Konrad Dybcio 2026-09-16 13:38 ` [PATCH v3 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() " Oleg Keri 1 sibling, 1 reply; 6+ messages in thread From: Oleg Keri @ 2026-09-16 13:38 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 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks 2026-09-16 13:38 ` [PATCH v3 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks Oleg Keri @ 2026-10-21 9:53 ` Konrad Dybcio 0 siblings, 0 replies; 6+ messages in thread From: Konrad Dybcio @ 2026-10-21 9:53 UTC (permalink / raw) To: Oleg Keri, Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Johan Hovold, Bjorn Andersson Cc: linux-arm-msm, linux-phy, linux-kernel On 9/16/26 3:38 PM, Oleg Keri wrote: > 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. Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> Konrad ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() in the typec callbacks 2026-09-16 13:38 [PATCH v3 0/2] phy: qcom: qmp-combo: keep the Type-C callbacks off unclocked registers Oleg Keri 2026-09-16 13:38 ` [PATCH v3 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks Oleg Keri @ 2026-09-16 13:38 ` Oleg Keri 2026-10-21 9:58 ` Konrad Dybcio 1 sibling, 1 reply; 6+ messages in thread From: Oleg Keri @ 2026-09-16 13:38 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. Both callbacks record the requested mode or orientation before the bringup, and skip the hardware entirely when the next event asks for what is already recorded, so on failure put the previous mode back and forget the orientation: the next Type-C event for the same setting then runs the bringup again instead of being treated as done. The PHY is still left logically initialised with its clocks off until that retry, 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 here. 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 | 17 +++++++++++++++-- 1 file changed, 15 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..ccdf71730dc7 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -4601,6 +4601,9 @@ 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) { + enum qmpphy_mode old_mode = qmp->qmpphy_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 +4618,11 @@ 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) { + qmp->qmpphy_mode = old_mode; + return ret; + } if ((qmp->usb_init_count || qmp->usb4_init_count) && new_mode != QMPPHY_MODE_DP_ONLY) @@ -5166,6 +5173,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 +5194,12 @@ 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) { + qmp->orientation = TYPEC_ORIENTATION_NONE; + 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 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() in the typec callbacks 2026-09-16 13:38 ` [PATCH v3 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() " Oleg Keri @ 2026-10-21 9:58 ` Konrad Dybcio 2026-10-21 12:25 ` Oleg Keri 0 siblings, 1 reply; 6+ messages in thread From: Konrad Dybcio @ 2026-10-21 9:58 UTC (permalink / raw) To: Oleg Keri, Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Johan Hovold, Bjorn Andersson Cc: linux-arm-msm, linux-phy, linux-kernel On 9/16/26 3:38 PM, Oleg Keri wrote: > 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); Sashiko has some comments which seem valid Is this something you hit in real life, or is that a static finding? Konrad ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() in the typec callbacks 2026-10-21 9:58 ` Konrad Dybcio @ 2026-10-21 12:25 ` Oleg Keri 0 siblings, 0 replies; 6+ messages in thread From: Oleg Keri @ 2026-10-21 12:25 UTC (permalink / raw) To: Konrad Dybcio, Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Johan Hovold, Bjorn Andersson Cc: linux-arm-msm, linux-phy, linux-kernel Static, as the v3 cover letter says ("found by inspection. It needs the bringup to fail"). qmp_combo_com_init() can only fail in regulator_bulk_enable(), the reset assert/deassert or the clock enables, and none of those has ever failed on this machine across several hundred boots with the dock plugged, unplugged and flipped. What I did hit is patch 1, the "already disabled" warnings from the altmode worker once runtime PM is allowed from userspace. That log is in 1/2. To see what Sashiko's scenario actually does I made the forced bringup fail once by fault injection (return -EIO after the reset assert, i.e. the com_init() error path runs: regulators off, PHY in reset) and flipped the dock cable. On next-20261020 with 1/2 applied and the bringup path otherwise stock: dwc3's phy_power_on() immediately reports "phy initialization timed-out", and from then on USB3 and DP on that port are dead while USB2 keeps working. Unplugging the dock runs phy_power_off() on the unpowered PHY. The writes are swallowed, no SEA on this SoC, but nothing recovers either: a replug in the same orientation never re-initialises the PHY because the driver still counts it as up. So no panic, a silently dead port until the next orientation change. 2/2 doesn't fix that, it only stops the register writes in the callback itself. I have a v4 that drops 2/2 and instead stops the Type-C callbacks from releasing and re-acquiring the regulators and clocks on every orientation or mode change. They only reset and reprogram the common block, so a failed reconfiguration can no longer leave the PHY counted as up with its power off. With the same injection on that code the callback returns the error, the PHY stays powered, and the next Type-C event brings USB3 and DP back. Konrad, do you want v4 with that change, or .. ? Oleg ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-21 12:25 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-16 13:38 [PATCH v3 0/2] phy: qcom: qmp-combo: keep the Type-C callbacks off unclocked registers Oleg Keri 2026-09-16 13:38 ` [PATCH v3 1/2] phy: qcom: qmp-combo: hold a runtime PM reference in the typec callbacks Oleg Keri 2026-10-21 9:53 ` Konrad Dybcio 2026-09-16 13:38 ` [PATCH v3 2/2] phy: qcom: qmp-combo: check qmp_combo_com_init() " Oleg Keri 2026-10-21 9:58 ` Konrad Dybcio 2026-10-21 12:25 ` Oleg Keri
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®