* [PATCH v9 01/38] dt-bindings: phy: rockchip-usbdp: add improved ports scheme
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:54 ` sashiko-bot
2026-07-01 23:35 ` [PATCH v9 02/38] phy: rockchip: usbdp: Update mode_change after error handling Sebastian Reichel
` (36 subsequent siblings)
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Currently the Rockchip USBDP PHY is missing a documented port scheme.
Meanwhile upstream RK3588 DTS files are a bit messy and use different
port schemes. The upstream USBDP PHY Linux kernel driver does not yet
parse the ports at all and thus does not create any implicit ABI either.
But with the current mess it is not possible to properly support USB-C
DP AltMode. Thus this introduces a proper port scheme following roughly
the ports design of the Qualcomm QMP USB4-USB3-DP PHY controller binding
with a slight difference that there is an additional port for the
USB-C SBU port as the Rockchip USB-DP PHY also contains the SBU mux.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
.../bindings/phy/phy-rockchip-usbdp.yaml | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
index 8b7059d5b182..c1a1384118da 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
@@ -114,6 +114,29 @@ properties:
A port node to link the PHY to a TypeC controller for the purpose of
handling orientation switching.
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Output endpoint of the PHY for USB (or DP when configured into 4 lane
+ mode), which should point to the superspeed port of a USB connector.
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Incoming endpoint from the USB controller
+
+ port@2:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Incoming endpoint from the DisplayPort controller
+
+ port@3:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Output endpoint of the PHY for DP Auxiliary, which should either point to
+ the SBU port of a USB-C connector or a DisplayPort connector input port.
+
required:
- compatible
- reg
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 01/38] dt-bindings: phy: rockchip-usbdp: add improved ports scheme
2026-07-01 23:35 ` [PATCH v9 01/38] dt-bindings: phy: rockchip-usbdp: add improved ports scheme Sebastian Reichel
@ 2026-07-01 23:54 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-01 23:54 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, linux-phy, robh, conor+dt, vkoul, neil.armstrong,
olteanv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The schema introduces a new `ports` object to link to the USB-C connector but fails to remove or properly restrict the legacy `port` property, creating an ambiguous and conflicting configuration path.
--
Note: Due to recitation restrictions, the standard inline diff quoting format
has been altered. The following is a free-form summary of the review findings.
commit 097123c73fce6d7ce6f4755a83ce1362c8caefeb
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
dt-bindings: phy: rockchip-usbdp: add improved ports scheme
This commit introduces a proper port scheme for the Rockchip USBDP PHY,
following the Qualcomm QMP USB4-USB3-DP PHY controller binding. It adds an
additional port for the USB-C SBU port to support USB-C DP AltMode.
[Severity: Low]
The schema introduces a new ports object to link to the USB-C connector
but retains the legacy port property without marking it as deprecated.
Since both the legacy port and the new ports properties are present in
the schema properties, could a device tree define both simultaneously and
still pass validation?
Would it be appropriate to add a oneOf exclusion constraint to ensure these
mutually-exclusive graph paradigms cannot be defined at the same time?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=1
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 02/38] phy: rockchip: usbdp: Update mode_change after error handling
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 01/38] dt-bindings: phy: rockchip-usbdp: add improved ports scheme Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 03/38] phy: rockchip: usbdp: Do not lose USB3 PHY status Sebastian Reichel
` (35 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
If rk_udphy_init() or rk_udphy_setup() fails, the reinit will not be
tried again. Fix this by only updating the variable after all potential
errors have been handled.
Note, that no errors have been seen on real hardware and failures would
most likely be fatal and require at least a full reboot as the function
already asserts the PHY reset lines. So this is more of a theoretical
issue.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/linux-phy/20260612163835.8D5471F000E9@smtp.kernel.org/
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index fba35510d88c..a5ffdf7a6e76 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1000,15 +1000,14 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
}
if (udphy->status == UDPHY_MODE_NONE) {
- udphy->mode_change = false;
ret = rk_udphy_setup(udphy);
if (ret)
return ret;
if (udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
- } else if (udphy->mode_change) {
udphy->mode_change = false;
+ } else if (udphy->mode_change) {
udphy->status = UDPHY_MODE_NONE;
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
@@ -1017,6 +1016,7 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
ret = rk_udphy_setup(udphy);
if (ret)
return ret;
+ udphy->mode_change = false;
}
udphy->status |= mode;
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 03/38] phy: rockchip: usbdp: Do not lose USB3 PHY status
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 01/38] dt-bindings: phy: rockchip-usbdp: add improved ports scheme Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 02/38] phy: rockchip: usbdp: Update mode_change after error handling Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 04/38] phy: rockchip: usbdp: Fix devm_clk_bulk_get_all check Sebastian Reichel
` (34 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
By default (i.e. without manually enabling runtime PM) DWC3 requests the
USB3 PHY once and keeps it enabled all the time. When DisplayPort is
being requested later on, a mode change is needed. This re-initializes
the PHY. During re-initialization the status variable has incorrectly
been cleared, which means the tracking information for USB3 is lost.
This is not an immediate problem, since the DP side keeps the PHY
enabled. But once DP is toggled off, the whole PHY will be disabled.
This is a problem, because the USB side still needs it powered.
Fix things by not clearing the status flags.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index a5ffdf7a6e76..12dfdd470a78 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1008,7 +1008,6 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
rk_udphy_u3_port_disable(udphy, false);
udphy->mode_change = false;
} else if (udphy->mode_change) {
- udphy->status = UDPHY_MODE_NONE;
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 04/38] phy: rockchip: usbdp: Fix devm_clk_bulk_get_all check
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (2 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 03/38] phy: rockchip: usbdp: Do not lose USB3 PHY status Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 05/38] phy: rockchip: usbdp: Handle missing clock-names DT property gracefully Sebastian Reichel
` (33 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
If devm_clk_bulk_get_all() returns -EPROBE_DEFER, it is replaced with
-ENODEV, permanently failing the driver probe instead of allowing it to
defer. Avoid masking the error code to fix the issue.
This effectively drops returning -ENODEV in case no clocks are being
described in DT. This special case will now be handled by the follow-up
check searching for "refclk" and exit with -EINVAL.
None of this will be hit in practice, since the driver is only used by
RK3588 and RK3576 - on these platforms the DT is validated to contain
the clocks and the clock driver is force probed early. Thus there is
no need to backport this.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/linux-phy/20260612164107.C7DB21F000E9@smtp.kernel.org/
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 12dfdd470a78..2845a012eafc 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -427,8 +427,8 @@ static int rk_udphy_clk_init(struct rk_udphy *udphy, struct device *dev)
int i;
udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks);
- if (udphy->num_clks < 1)
- return -ENODEV;
+ if (udphy->num_clks < 0)
+ return udphy->num_clks;
/* used for configure phy reference clock frequency */
for (i = 0; i < udphy->num_clks; i++) {
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 05/38] phy: rockchip: usbdp: Handle missing clock-names DT property gracefully
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (3 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 04/38] phy: rockchip: usbdp: Fix devm_clk_bulk_get_all check Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 06/38] phy: rockchip: usbdp: Drop seamless DP takeover Sebastian Reichel
` (32 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
The rk_udphy_clk_init() function would currently try to do a strncmp for
a NULL pointer, if DT specifies 'clocks' property, but no 'clock-names'
property. Fix this by making sure the clock has an id string set.
Note that DT binding requires setting clock-names, so this is only a
problem when booting a non-compliant device tree.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260619154349.071321F000E9%40smtp.kernel.org
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 2845a012eafc..3fc8222fcaec 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -432,6 +432,9 @@ static int rk_udphy_clk_init(struct rk_udphy *udphy, struct device *dev)
/* used for configure phy reference clock frequency */
for (i = 0; i < udphy->num_clks; i++) {
+ if (!udphy->clks[i].id)
+ continue;
+
if (!strncmp(udphy->clks[i].id, "refclk", 6)) {
udphy->refclk = udphy->clks[i].clk;
break;
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 06/38] phy: rockchip: usbdp: Drop seamless DP takeover
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (4 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 05/38] phy: rockchip: usbdp: Handle missing clock-names DT property gracefully Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:59 ` sashiko-bot
2026-07-01 23:35 ` [PATCH v9 07/38] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert_all errors in init check Sebastian Reichel
` (31 subsequent siblings)
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
Right now the DRM drivers do not support seamless DP takeover and I'm
I'm not aware of any bootloader implementing this feature either.
In any case this feature would be limited to boards using the USBDP PHY
for a DP or eDP connection instead of the more commonly USB-C connector.
With USB-C's DP AltMode a seamless DP takeover requires handing over the
state of the TCPM state machine from the bootloader to the kernel. This
in turn requires a huge amount of work to keep the state machine
implementations synchronized. It's very unlikely we will see somebody
implementing that in the foreseeable future.
As the current code is obviously buggy and untested, let's simply drop
support for seamless DP takeover. It can be re-implemented cleanly once
somebody adds all missing bits.
Technically the VOP side should be disabled first to avoid data being
send to an unpowered PHY from the running pipeline. As a workaround this
force disables HPD. I'm not sure if this would be good enough, but cannot
test as there is no support in the bootloaders known to me and my test
boards route USBDP to USB-C connectors.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/linux-phy/20260612164107.C7DB21F000E9@smtp.kernel.org/
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 3fc8222fcaec..d73455370113 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -926,6 +926,7 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
{
+ const struct rk_udphy_cfg *cfg = udphy->cfgs;
int ret;
u32 value;
@@ -938,10 +939,14 @@ static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
rk_udphy_reset_deassert_all(udphy);
regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
- if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value))
- udphy->status = UDPHY_MODE_DP;
- else
- rk_udphy_disable(udphy);
+ if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value)) {
+ dev_info(udphy->dev, "Started with DP PHY pre-enabled; seamless takeover unsupported\n");
+ rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, false);
+ /* Wait for VOP side to shut up */
+ msleep(100);
+ }
+
+ rk_udphy_disable(udphy);
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 06/38] phy: rockchip: usbdp: Drop seamless DP takeover
2026-07-01 23:35 ` [PATCH v9 06/38] phy: rockchip: usbdp: Drop seamless DP takeover Sebastian Reichel
@ 2026-07-01 23:59 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-01 23:59 UTC (permalink / raw)
To: Sebastian Reichel
Cc: olteanv, conor+dt, neil.armstrong, linux-phy, vkoul, devicetree,
robh
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Permanently breaks native DP and eDP displays if the bootloader pre-enables the PHY.
- [High] Unconditionally disabling a pre-enabled DP PHY during probe without deterministic synchronization with the display controller (VOP) will cause a hardware lockup.
--
commit b62be7660327d19c538f4a7e04fe1f77ddb82d23
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Drop seamless DP takeover
This commit removes support for seamless DP takeover in the Rockchip USBDP PHY
driver. It attempts to work around active bootloader configurations by forcing
HPD low and waiting before shutting down the PHY.
Link: https://lore.kernel.org/linux-phy/20260612164107.C7DB21F000E9@smtp.kernel.org/
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 3fc8222fcaec2..d734553701134 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -938,10 +939,14 @@ static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
> rk_udphy_reset_deassert_all(udphy);
>
> regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
> - if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value))
> - udphy->status = UDPHY_MODE_DP;
> - else
> - rk_udphy_disable(udphy);
> + if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value)) {
> + dev_info(udphy->dev, "Started with DP PHY pre-enabled; seamless takeover unsupported\n");
> + rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, false);
[Severity: High]
Does this permanently break native DP and eDP displays if the bootloader
pre-enables the PHY?
By writing false to the hpd_trigger register, this forces a software override
of the physical HPD pin to a low state. Since this override is never reverted
anywhere in the driver, will the hardware DP controller permanently read HPD
as low, preventing native displays from link training?
> + /* Wait for VOP side to shut up */
> + msleep(100);
> + }
> +
> + rk_udphy_disable(udphy);
[Severity: High]
Can unconditionally disabling a pre-enabled DP PHY here cause a hardware lockup?
Since the PHY driver probes very early, the DRM driver has not yet bound,
meaning no interrupt handler is present to process the HPD drop, and the Video
Output Processor (VOP) continues streaming. After the 100ms delay,
rk_udphy_disable() abruptly turns off the PHY and its PLL.
Does halting the pixel clock while the VOP is actively transacting cause an
interconnect stall on Rockchip SoCs?
>
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=6
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 07/38] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert_all errors in init check
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (5 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 06/38] phy: rockchip: usbdp: Drop seamless DP takeover Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 08/38] phy: rockchip: usbdp: Keep clocks running on PHY re-init Sebastian Reichel
` (30 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
Fix theoretical issue, that failure to deassert the reset lines would
result in an SError in follow-up register access. Note, that this cannot
happen with current code, since the Rockchip reset driver never returns
any errors for (de)asserting reset lines.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260619154903.2225A1F000E9%40smtp.kernel.org
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index d73455370113..5d0e4f82de2f 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -936,7 +936,9 @@ static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
return ret;
}
- rk_udphy_reset_deassert_all(udphy);
+ ret = rk_udphy_reset_deassert_all(udphy);
+ if (ret)
+ goto exit;
regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value)) {
@@ -946,9 +948,10 @@ static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
msleep(100);
}
+exit:
rk_udphy_disable(udphy);
- return 0;
+ return ret;
}
static int rk_udphy_parse_dt(struct rk_udphy *udphy)
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 08/38] phy: rockchip: usbdp: Keep clocks running on PHY re-init
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (6 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 07/38] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert_all errors in init check Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:57 ` sashiko-bot
2026-07-01 23:35 ` [PATCH v9 09/38] phy: rockchip: usbdp: Amend SSC modulation deviation Sebastian Reichel
` (29 subsequent siblings)
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
When a mode change is required rk_udphy_power_on() disables
the clocks and then calls rk_udphy_setup(), which then enables
all the clocks again before continuing with rk_udphy_init().
Considering that rk_udphy_init() does assert the reset lines,
re-enabling the clocks is just delaying things. Avoid it by
directly calling rk_udphy_init().
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 5d0e4f82de2f..2b27fd1c15d8 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1022,8 +1022,7 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
- rk_udphy_disable(udphy);
- ret = rk_udphy_setup(udphy);
+ ret = rk_udphy_init(udphy);
if (ret)
return ret;
udphy->mode_change = false;
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 08/38] phy: rockchip: usbdp: Keep clocks running on PHY re-init
2026-07-01 23:35 ` [PATCH v9 08/38] phy: rockchip: usbdp: Keep clocks running on PHY re-init Sebastian Reichel
@ 2026-07-01 23:57 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-01 23:57 UTC (permalink / raw)
To: Sebastian Reichel
Cc: robh, olteanv, conor+dt, linux-phy, vkoul, devicetree,
neil.armstrong
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but `rk_udphy_dp_phy_power_on()` returns 0 (success) when the DP mode is unsupported by the current Type-C state, leading callers to access clock-gated MMIO registers and causing a Synchronous External Abort (SError).
--
commit 1135135471c21f67a83c25aab8254a6056976c31
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Keep clocks running on PHY re-init
This commit modifies the PHY mode change logic in rk_udphy_power_on() to avoid
unnecessarily toggling the clocks off and on again. It skips the clock disable
and setup steps, directly calling rk_udphy_init() instead.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 5d0e4f82de2f8..2b27fd1c15d8d 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -1022,8 +1022,7 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
> if (udphy->mode == UDPHY_MODE_DP)
> rk_udphy_u3_port_disable(udphy, true);
>
> - rk_udphy_disable(udphy);
> - ret = rk_udphy_setup(udphy);
> + ret = rk_udphy_init(udphy);
[Severity: High]
This isn't a bug introduced by this patch, but rk_udphy_power_on() returns 0
(success) when the requested mode is not supported by the current Type-C
state, which can lead to a Synchronous External Abort (SError).
If rk_udphy_dp_phy_power_on() calls rk_udphy_power_on() before a Type-C Alt
Mode is negotiated, rk_udphy_power_on() returns 0 without enabling the APB
clocks:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_power_on() {
if (!(udphy->mode & mode)) {
dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
return 0;
}
...
}
Because rk_udphy_power_on() returns success, rk_udphy_dp_phy_power_on()
assumes the PHY is powered on and proceeds to access clock-gated MMIO
registers:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_dp_phy_power_on() {
...
ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
if (ret)
goto unlock;
rk_udphy_dplane_enable(udphy, dp_lanes);
...
}
Here, rk_udphy_dplane_enable() performs read/modify/write operations on
udphy->pma_regmap. Accessing these registers with disabled APB clocks
triggers an SError system crash.
The same vulnerability exists in rk_udphy_dp_phy_power_off() and
rk_udphy_dp_phy_configure(), which unconditionally access the PMA registers
before updating or checking the power state.
Could rk_udphy_power_on() return an error code instead of 0 when the mode
is unsupported, or could the callers verify if the PHY actually powered on
before accessing the registers?
> if (ret)
> return ret;
> udphy->mode_change = false;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=8
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 09/38] phy: rockchip: usbdp: Amend SSC modulation deviation
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (7 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 08/38] phy: rockchip: usbdp: Keep clocks running on PHY re-init Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 10/38] phy: rockchip: usbdp: Fix LFPS detect threshold control Sebastian Reichel
` (28 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
From: Frank Wang <frank.wang@rock-chips.com>
Move SSC modulation deviation into private config of clock
- 24M: 0x00d4[5:0] = 0x30
- 26M: 0x00d4[5:0] = 0x33
Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
[Taken over from rockchip's kernel tree; register 0x00d4 is not
described in the TRM]
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 2b27fd1c15d8..923279ed7d77 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -350,7 +350,8 @@ static const struct reg_sequence rk_udphy_24m_refclk_cfg[] = {
{0x0a64, 0xa8}, {0x1a3c, 0xd0},
{0x1a44, 0xd0}, {0x1a48, 0x01},
{0x1a4c, 0x0d}, {0x1a54, 0xe0},
- {0x1a5c, 0xe0}, {0x1a64, 0xa8}
+ {0x1a5c, 0xe0}, {0x1a64, 0xa8},
+ {0x00d4, 0x30}
};
static const struct reg_sequence rk_udphy_26m_refclk_cfg[] = {
@@ -377,7 +378,7 @@ static const struct reg_sequence rk_udphy_26m_refclk_cfg[] = {
{0x0c30, 0x0e}, {0x0c48, 0x06},
{0x1c30, 0x0e}, {0x1c48, 0x06},
{0x028c, 0x18}, {0x0af0, 0x00},
- {0x1af0, 0x00}
+ {0x1af0, 0x00}, {0x00d4, 0x33}
};
static const struct reg_sequence rk_udphy_init_sequence[] = {
@@ -412,8 +413,7 @@ static const struct reg_sequence rk_udphy_init_sequence[] = {
{0x0070, 0x7d}, {0x0074, 0x68},
{0x0af4, 0x1a}, {0x1af4, 0x1a},
{0x0440, 0x3f}, {0x10d4, 0x08},
- {0x20d4, 0x08}, {0x00d4, 0x30},
- {0x0024, 0x6e},
+ {0x20d4, 0x08}, {0x0024, 0x6e}
};
static inline int rk_udphy_grfreg_write(struct regmap *base,
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 10/38] phy: rockchip: usbdp: Fix LFPS detect threshold control
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (8 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 09/38] phy: rockchip: usbdp: Amend SSC modulation deviation Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 11/38] phy: rockchip: usbdp: Add missing mode_change update Sebastian Reichel
` (27 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, William Wu
From: William Wu <william.wu@rock-chips.com>
According to the LFPS Tx Low Power/LFPS Rx Detect Threshold [1],
the device under test(DUT) must not respond if LFPS below the
minimum LFPS Rx Detect Threshold 100mV. Test fail on Rockchip
platforms, because the default LFPS detect threshold is set to
65mV.
The USBDP PHY LFPS detect threshold voltage could be set to
30mV ~ 140mV, and since there could be 10-20% PVT variation,
we set LFPS detect threshold voltage to 110mV.
[1] https://compliance.usb.org/resources/LFPS_Rx_Tx_Low_Power_Compliance_Update_Rev5.pdf
Signed-off-by: William Wu <william.wu@rock-chips.com>
[Taken over from rockchip's kernel tree; the registers are not described
in the TRM]
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 923279ed7d77..8202abab4274 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -413,7 +413,8 @@ static const struct reg_sequence rk_udphy_init_sequence[] = {
{0x0070, 0x7d}, {0x0074, 0x68},
{0x0af4, 0x1a}, {0x1af4, 0x1a},
{0x0440, 0x3f}, {0x10d4, 0x08},
- {0x20d4, 0x08}, {0x0024, 0x6e}
+ {0x20d4, 0x08}, {0x0024, 0x6e},
+ {0x09c0, 0x0a}, {0x19c0, 0x0a}
};
static inline int rk_udphy_grfreg_write(struct regmap *base,
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 11/38] phy: rockchip: usbdp: Add missing mode_change update
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (9 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 10/38] phy: rockchip: usbdp: Fix LFPS detect threshold control Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 12/38] phy: rockchip: usbdp: Support single-lane DP Sebastian Reichel
` (26 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
rk_udphy_set_typec_default_mapping() updates the available modes,
but does not set the mode_change as required. This results in
missing re-initialization and thus non-working DisplayPort.
Fix this issue by introducing a new helper to update the available
modes.
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 8202abab4274..6333b5cc7151 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -622,6 +622,15 @@ static void rk_udphy_dp_hpd_event_trigger(struct rk_udphy *udphy, bool hpd)
rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
}
+static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
+{
+ if (udphy->mode == mode)
+ return;
+
+ udphy->mode_change = true;
+ udphy->mode = mode;
+}
+
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
{
if (udphy->flip) {
@@ -652,7 +661,7 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
}
- udphy->mode = UDPHY_MODE_DP_USB;
+ rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB);
}
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
@@ -1396,10 +1405,7 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
usleep_range(750, 800);
rk_udphy_dp_hpd_event_trigger(udphy, true);
} else if (data->status & DP_STATUS_HPD_STATE) {
- if (udphy->mode != mode) {
- udphy->mode = mode;
- udphy->mode_change = true;
- }
+ rk_udphy_mode_set(udphy, mode);
rk_udphy_dp_hpd_event_trigger(udphy, true);
} else {
rk_udphy_dp_hpd_event_trigger(udphy, false);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 12/38] phy: rockchip: usbdp: Support single-lane DP
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (10 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 11/38] phy: rockchip: usbdp: Add missing mode_change update Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 13/38] phy: rockchip: usbdp: Limit DP lane count to muxed lanes Sebastian Reichel
` (25 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
From: Zhang Yubing <yubing.zhang@rock-chips.com>
Implement support for using just a single DisplayPort line.
Signed-off-by: Zhang Yubing <yubing.zhang@rock-chips.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 64 +++++++++++++------------------
1 file changed, 27 insertions(+), 37 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 6333b5cc7151..da15178d7f16 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -193,6 +193,7 @@ struct rk_udphy {
int id;
bool dp_in_use;
+ int dp_lanes;
/* PHY const config */
const struct rk_udphy_cfg *cfgs;
@@ -540,6 +541,13 @@ static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
* <0 1> dpln0 dpln1 usbrx usbtx
* <2 3> usbrx usbtx dpln0 dpln1
* ---------------------------------------------------------------------------
+ * if 1 lane for dp function, 2 lane for usb function, define rockchip,dp-lane-mux = <x>;
+ * sample as follow:
+ * ---------------------------------------------------------------------------
+ * B11-B10 A2-A3 A11-A10 B2-B3
+ * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
+ * <0> dpln0 \ usbrx usbtx
+ * ---------------------------------------------------------------------------
*/
static void rk_udphy_dplane_select(struct rk_udphy *udphy)
@@ -547,18 +555,18 @@ static void rk_udphy_dplane_select(struct rk_udphy *udphy)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
u32 value = 0;
- switch (udphy->mode) {
- case UDPHY_MODE_DP:
- value |= 2 << udphy->dp_lane_sel[2] * 2;
+ switch (udphy->dp_lanes) {
+ case 4:
value |= 3 << udphy->dp_lane_sel[3] * 2;
+ value |= 2 << udphy->dp_lane_sel[2] * 2;
fallthrough;
- case UDPHY_MODE_DP_USB:
- value |= 0 << udphy->dp_lane_sel[0] * 2;
+ case 2:
value |= 1 << udphy->dp_lane_sel[1] * 2;
- break;
+ fallthrough;
- case UDPHY_MODE_USB:
+ case 1:
+ value |= 0 << udphy->dp_lane_sel[0] * 2;
break;
default:
@@ -571,28 +579,6 @@ static void rk_udphy_dplane_select(struct rk_udphy *udphy)
FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
}
-static int rk_udphy_dplane_get(struct rk_udphy *udphy)
-{
- int dp_lanes;
-
- switch (udphy->mode) {
- case UDPHY_MODE_DP:
- dp_lanes = 4;
- break;
-
- case UDPHY_MODE_DP_USB:
- dp_lanes = 2;
- break;
-
- case UDPHY_MODE_USB:
- default:
- dp_lanes = 0;
- break;
- }
-
- return dp_lanes;
-}
-
static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
{
u32 val = 0;
@@ -662,6 +648,7 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
}
rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB);
+ udphy->dp_lanes = 2;
}
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
@@ -900,7 +887,7 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
return 0;
}
- if (num_lanes != 2 && num_lanes != 4)
+ if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4)
return dev_err_probe(udphy->dev, -EINVAL,
"invalid number of lane mux\n");
@@ -926,9 +913,11 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
}
udphy->mode = UDPHY_MODE_DP;
- if (num_lanes == 2) {
+ udphy->dp_lanes = num_lanes;
+ if (num_lanes == 1 || num_lanes == 2) {
udphy->mode |= UDPHY_MODE_USB;
- udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP);
+ udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP) ||
+ (udphy->lane_mux_sel[1] == PHY_LANE_MUX_DP);
}
return 0;
@@ -1085,18 +1074,17 @@ static int rk_udphy_dp_phy_exit(struct phy *phy)
static int rk_udphy_dp_phy_power_on(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- int ret, dp_lanes;
+ int ret;
mutex_lock(&udphy->mutex);
- dp_lanes = rk_udphy_dplane_get(udphy);
- phy_set_bus_width(phy, dp_lanes);
+ phy_set_bus_width(phy, udphy->dp_lanes);
ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
if (ret)
goto unlock;
- rk_udphy_dplane_enable(udphy, dp_lanes);
+ rk_udphy_dplane_enable(udphy, udphy->dp_lanes);
rk_udphy_dplane_select(udphy);
@@ -1376,6 +1364,7 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
mode = UDPHY_MODE_DP;
+ udphy->dp_lanes = 4;
break;
case TYPEC_DP_STATE_D:
@@ -1392,6 +1381,7 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
}
mode = UDPHY_MODE_DP_USB;
+ udphy->dp_lanes = 2;
break;
}
@@ -1540,7 +1530,7 @@ static int rk_udphy_probe(struct platform_device *pdev)
ret = PTR_ERR(udphy->phy_dp);
return dev_err_probe(dev, ret, "failed to create DP phy\n");
}
- phy_set_bus_width(udphy->phy_dp, rk_udphy_dplane_get(udphy));
+ phy_set_bus_width(udphy->phy_dp, udphy->dp_lanes);
udphy->phy_dp->attrs.max_link_rate = 8100;
phy_set_drvdata(udphy->phy_dp, udphy);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 13/38] phy: rockchip: usbdp: Limit DP lane count to muxed lanes
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (11 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 12/38] phy: rockchip: usbdp: Support single-lane DP Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:59 ` sashiko-bot
2026-07-01 23:35 ` [PATCH v9 14/38] phy: rockchip: usbdp: Rename DP lane functions Sebastian Reichel
` (24 subsequent siblings)
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
In theory the DP controller could request 4 lanes when the PHY is
restricted to 2 lanes as the other half is used by USB3.
With the current user (DW-DP) this cannot happen, but as the check is
cheap and users might change in the future protect things accordingly.
Not doing so would corrupt USB3 usage by the following code configuring
the voltages.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/linux-phy/20260612165546.98E1F1F000E9@smtp.kernel.org/
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index da15178d7f16..0cfd23a405cb 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1134,6 +1134,9 @@ static int rk_udphy_dp_phy_verify_link_rate(struct rk_udphy *udphy,
static int rk_udphy_dp_phy_verify_lanes(struct rk_udphy *udphy,
struct phy_configure_opts_dp *dp)
{
+ if (dp->lanes > udphy->dp_lanes)
+ return -EINVAL;
+
switch (dp->lanes) {
case 1:
case 2:
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 13/38] phy: rockchip: usbdp: Limit DP lane count to muxed lanes
2026-07-01 23:35 ` [PATCH v9 13/38] phy: rockchip: usbdp: Limit DP lane count to muxed lanes Sebastian Reichel
@ 2026-07-01 23:59 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-01 23:59 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, olteanv, neil.armstrong, vkoul, robh, linux-phy,
conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Incomplete fix: the added DP lane count validation is bypassed when updating voltages, leading to USB3 PHY corruption.
--
commit e3930e8c2271673fb1869d7c43a33a65c3e10987
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Limit DP lane count to muxed lanes
This commit adds a check to limit the DisplayPort lane count to the number
of muxed lanes. This ensures the DP controller cannot configure 4 lanes
when the PHY is restricted to 2 lanes, protecting USB3 functionality.
Link: https://lore.kernel.org/linux-phy/20260612165546.98E1F1F000E9@smtp.kernel.org/
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index da15178d7f16e..0cfd23a405cb0 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -1134,6 +1134,9 @@ static int rk_udphy_dp_phy_verify_link_rate(struct rk_udphy *udphy,
> static int rk_udphy_dp_phy_verify_lanes(struct rk_udphy *udphy,
> struct phy_configure_opts_dp *dp)
> {
> + if (dp->lanes > udphy->dp_lanes)
> + return -EINVAL;
> +
[Severity: High]
This is a pre-existing issue, but does this validation fully protect against
USB3 lane corruption?
This check is placed in rk_udphy_dp_phy_verify_lanes(), which is only
executed when dp->set_lanes is true.
If a Type-C mux switch reduces udphy->dp_lanes from 4 to 2, but an ongoing
DP link training loop subsequently calls phy_configure() with
dp->set_voltages = 1 and dp->set_lanes = 0, this new check is skipped.
The voltage configuration loop in rk_udphy_dp_phy_configure() will then
iterate using the stale udphy->lanes count:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_dp_phy_configure() {
...
if (dp->set_voltages) {
for (i = 0; i < udphy->lanes; i++) {
...
}
Could this still write DP configurations into PMA registers corresponding to
lanes 2 and 3, which are actively assigned to USB3? Even after later commits
add a mutex to rk_udphy_dp_phy_configure(), udphy->lanes doesn't appear to be
clamped to match udphy->dp_lanes during voltage-only updates.
> switch (dp->lanes) {
> case 1:
> case 2:
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=13
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 14/38] phy: rockchip: usbdp: Rename DP lane functions
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (12 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 13/38] phy: rockchip: usbdp: Limit DP lane count to muxed lanes Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 15/38] phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST Sebastian Reichel
` (23 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
The common prefix for DisplayPort related functions is rk_udphy_dp_
(with a final _), so update the two DP lane functions to follow that
scheme.
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 0cfd23a405cb..ee7ec86bda9e 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -550,7 +550,7 @@ static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
* ---------------------------------------------------------------------------
*/
-static void rk_udphy_dplane_select(struct rk_udphy *udphy)
+static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
u32 value = 0;
@@ -579,7 +579,7 @@ static void rk_udphy_dplane_select(struct rk_udphy *udphy)
FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
}
-static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
+static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
{
u32 val = 0;
int i;
@@ -1084,9 +1084,9 @@ static int rk_udphy_dp_phy_power_on(struct phy *phy)
if (ret)
goto unlock;
- rk_udphy_dplane_enable(udphy, udphy->dp_lanes);
+ rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
- rk_udphy_dplane_select(udphy);
+ rk_udphy_dp_lane_select(udphy);
unlock:
mutex_unlock(&udphy->mutex);
@@ -1104,7 +1104,7 @@ static int rk_udphy_dp_phy_power_off(struct phy *phy)
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
- rk_udphy_dplane_enable(udphy, 0);
+ rk_udphy_dp_lane_enable(udphy, 0);
rk_udphy_power_off(udphy, UDPHY_MODE_DP);
mutex_unlock(&udphy->mutex);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 15/38] phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (13 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 14/38] phy: rockchip: usbdp: Rename DP lane functions Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 16/38] phy: rockchip: usbdp: Cleanup DP lane selection function Sebastian Reichel
` (22 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Cleanup code by replacing open-coded version of FIELD_PREP_WM16_CONST
with the existing helper macro.
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index ee7ec86bda9e..3124bcfed031 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -12,6 +12,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
+#include <linux/hw_bitfield.h>
#include <linux/mfd/syscon.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
@@ -75,7 +76,6 @@
#define TRSV_LN2_MON_RX_CDR_DONE_OFFSET 0x1b84 /* trsv_reg06E1 */
#define TRSV_LN2_MON_RX_CDR_LOCK_DONE BIT(0)
-#define BIT_WRITEABLE_SHIFT 16
#define PHY_AUX_DP_DATA_POL_NORMAL 0
#define PHY_AUX_DP_DATA_POL_INVERT 1
#define PHY_LANE_MUX_USB 0
@@ -104,8 +104,8 @@ struct rk_udphy_grf_reg {
#define _RK_UDPHY_GEN_GRF_REG(offset, mask, disable, enable) \
{\
offset, \
- FIELD_PREP_CONST(mask, disable) | (mask << BIT_WRITEABLE_SHIFT), \
- FIELD_PREP_CONST(mask, enable) | (mask << BIT_WRITEABLE_SHIFT), \
+ FIELD_PREP_WM16_CONST(mask, disable), \
+ FIELD_PREP_WM16_CONST(mask, enable), \
}
#define RK_UDPHY_GEN_GRF_REG(offset, bitend, bitstart, disable, enable) \
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 16/38] phy: rockchip: usbdp: Cleanup DP lane selection function
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (14 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 15/38] phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 17/38] phy: rockchip: usbdp: Register DP aux bridge Sebastian Reichel
` (21 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Use FIELD_PREP_WM16() helpers to simplify the DP lane selection
logic.
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 28 +++++++---------------------
1 file changed, 7 insertions(+), 21 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 3124bcfed031..5d3db696d51f 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -553,30 +553,16 @@ static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
- u32 value = 0;
-
- switch (udphy->dp_lanes) {
- case 4:
- value |= 3 << udphy->dp_lane_sel[3] * 2;
- value |= 2 << udphy->dp_lane_sel[2] * 2;
- fallthrough;
-
- case 2:
- value |= 1 << udphy->dp_lane_sel[1] * 2;
- fallthrough;
+ u32 value = FIELD_PREP_WM16(DP_LANE_SEL_ALL, 0);
+ int i;
- case 1:
- value |= 0 << udphy->dp_lane_sel[0] * 2;
- break;
+ for (i = 0; i < udphy->dp_lanes; i++)
+ value |= field_prep(DP_LANE_SEL_N(udphy->dp_lane_sel[i]), i);
- default:
- break;
- }
+ value |= FIELD_PREP_WM16(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel);
+ value |= FIELD_PREP_WM16(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel);
- regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg,
- ((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) |
- FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) |
- FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
+ regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg, value);
}
static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 17/38] phy: rockchip: usbdp: Register DP aux bridge
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (15 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 16/38] phy: rockchip: usbdp: Cleanup DP lane selection function Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 18/38] phy: rockchip: usbdp: Drop DP HPD handling Sebastian Reichel
` (20 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Add support to use USB-C connectors with the DP altmode helper code on
devicetree based platforms. To get this working there must be a DRM
bridge chain from the DisplayPort controller to the USB-C connector.
E.g. on Rockchip RK3576:
root@rk3576 # cat /sys/kernel/debug/dri/0/encoder-0/bridges
bridge[0]: dw_dp_bridge_funcs
refcount: 7
type: [10] DP
OF: /soc/dp@27e40000:rockchip,rk3576-dp
ops: [0x47] detect edid hpd
bridge[1]: drm_aux_bridge_funcs
refcount: 4
type: [0] Unknown
OF: /soc/phy@2b010000:rockchip,rk3576-usbdp-phy
ops: [0x0]
bridge[2]: drm_aux_hpd_bridge_funcs
refcount: 5
type: [10] DP
OF: /soc/i2c@2ac50000/typec-portc@22/connector:usb-c-connector
ops: [0x4] hpd
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/Kconfig | 2 ++
drivers/phy/rockchip/phy-rockchip-usbdp.c | 17 +++++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 14698571b607..39759bb2fa1d 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -136,8 +136,10 @@ config PHY_ROCKCHIP_USBDP
tristate "Rockchip USBDP COMBO PHY Driver"
depends on ARCH_ROCKCHIP && OF
depends on TYPEC
+ depends on DRM || DRM=n
select GENERIC_PHY
select USB_COMMON
+ select DRM_AUX_BRIDGE if DRM_BRIDGE
help
Enable this to support the Rockchip USB3.0/DP combo PHY with
Samsung IP block. This is required for USB3 support on RK3588.
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 5d3db696d51f..b3de16557c32 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -6,6 +6,7 @@
* Copyright (C) 2024 Collabora Ltd
*/
+#include <drm/bridge/aux-bridge.h>
#include <dt-bindings/phy/phy.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
@@ -1449,6 +1450,7 @@ static int rk_udphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
+ struct fwnode_handle *dp_aux_ep;
struct resource *res;
struct rk_udphy *udphy;
void __iomem *base;
@@ -1507,6 +1509,21 @@ static int rk_udphy_probe(struct platform_device *pdev)
return ret;
}
+ /*
+ * Only register the DRM bridge, if the DP aux channel is connected.
+ * Some boards use the USBDP PHY only for its USB3 capabilities. The
+ * aux bridge itself will be registered using port 0, endpoint 0, which
+ * is fine as that is the actual superspeed data connection shared by
+ * USB3 and DP based on the mux config.
+ */
+ dp_aux_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 3, 0, 0);
+ if (dp_aux_ep) {
+ ret = drm_aux_bridge_register(dev);
+ fwnode_handle_put(dp_aux_ep);
+ if (ret)
+ return ret;
+ }
+
udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops);
if (IS_ERR(udphy->phy_u3)) {
ret = PTR_ERR(udphy->phy_u3);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 18/38] phy: rockchip: usbdp: Drop DP HPD handling
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (16 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 17/38] phy: rockchip: usbdp: Register DP aux bridge Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-02 0:00 ` sashiko-bot
2026-07-01 23:35 ` [PATCH v9 19/38] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit Sebastian Reichel
` (19 subsequent siblings)
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Drop the HPD handling logic from the USBDP PHY. The registers involved
require the display controller power domain being enabled and thus the
HPD signal should be handled by the displayport controller itself.
Apart from that the HPD handling as it is done here is incorrect and
misses hotplug events happening after the USB-C connector (e.g. when
a USB-C to HDMI adapter is involved and the HDMI cable is replugged).
Proper USB-C DP HPD support requires some restructuring of the DP
controller driver, which will happen independent of this patch. The
mainline kernel does not yet support USB-C DP AltMode on RK3588 and
RK3576, so it is fine to drop this code without adding the counterpart
in the DRM in an atomic change.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 81 ++++---------------------------
1 file changed, 9 insertions(+), 72 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index b3de16557c32..928466659486 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -186,14 +186,11 @@ struct rk_udphy {
u32 dp_lane_sel[4];
u32 dp_aux_dout_sel;
u32 dp_aux_din_sel;
- bool dp_sink_hpd_sel;
- bool dp_sink_hpd_cfg;
unsigned int link_rate;
unsigned int lanes;
u8 bw;
int id;
- bool dp_in_use;
int dp_lanes;
/* PHY const config */
@@ -582,19 +579,6 @@ static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
}
-static void rk_udphy_dp_hpd_event_trigger(struct rk_udphy *udphy, bool hpd)
-{
- const struct rk_udphy_cfg *cfg = udphy->cfgs;
-
- udphy->dp_sink_hpd_sel = true;
- udphy->dp_sink_hpd_cfg = hpd;
-
- if (!udphy->dp_in_use)
- return;
-
- rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
-}
-
static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
{
if (udphy->mode == mode)
@@ -1035,29 +1019,6 @@ static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
rk_udphy_disable(udphy);
}
-static int rk_udphy_dp_phy_init(struct phy *phy)
-{
- struct rk_udphy *udphy = phy_get_drvdata(phy);
-
- mutex_lock(&udphy->mutex);
-
- udphy->dp_in_use = true;
-
- mutex_unlock(&udphy->mutex);
-
- return 0;
-}
-
-static int rk_udphy_dp_phy_exit(struct phy *phy)
-{
- struct rk_udphy *udphy = phy_get_drvdata(phy);
-
- mutex_lock(&udphy->mutex);
- udphy->dp_in_use = false;
- mutex_unlock(&udphy->mutex);
- return 0;
-}
-
static int rk_udphy_dp_phy_power_on(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
@@ -1289,8 +1250,6 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
}
static const struct phy_ops rk_udphy_dp_phy_ops = {
- .init = rk_udphy_dp_phy_init,
- .exit = rk_udphy_dp_phy_exit,
.power_on = rk_udphy_dp_phy_power_on,
.power_off = rk_udphy_dp_phy_power_off,
.configure = rk_udphy_dp_phy_configure,
@@ -1344,6 +1303,14 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
u8 mode;
+ /*
+ * Ignore mux events not involving DP AltMode, because
+ * the mode field is being reused, e.g. state->mode == 4
+ * could be either TYPEC_MODE_USB4 or TYPEC_DP_STATE_C.
+ */
+ if (!state->alt || state->alt->svid != USB_TYPEC_DP_SID)
+ return 0;
+
mutex_lock(&udphy->mutex);
switch (state->mode) {
@@ -1375,22 +1342,7 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
break;
}
- if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
- struct typec_displayport_data *data = state->data;
-
- if (!data) {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- } else if (data->status & DP_STATUS_IRQ_HPD) {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- usleep_range(750, 800);
- rk_udphy_dp_hpd_event_trigger(udphy, true);
- } else if (data->status & DP_STATUS_HPD_STATE) {
- rk_udphy_mode_set(udphy, mode);
- rk_udphy_dp_hpd_event_trigger(udphy, true);
- } else {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- }
- }
+ rk_udphy_mode_set(udphy, mode);
mutex_unlock(&udphy->mutex);
return 0;
@@ -1549,20 +1501,6 @@ static int rk_udphy_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused rk_udphy_resume(struct device *dev)
-{
- struct rk_udphy *udphy = dev_get_drvdata(dev);
-
- if (udphy->dp_sink_hpd_sel)
- rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
-
- return 0;
-}
-
-static const struct dev_pm_ops rk_udphy_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, rk_udphy_resume)
-};
-
static const char * const rk_udphy_rst_list[] = {
"init", "cmn", "lane", "pcs_apb", "pma_apb"
};
@@ -1667,7 +1605,6 @@ static struct platform_driver rk_udphy_driver = {
.driver = {
.name = "rockchip-usbdp-phy",
.of_match_table = rk_udphy_dt_match,
- .pm = &rk_udphy_pm_ops,
},
};
module_platform_driver(rk_udphy_driver);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 18/38] phy: rockchip: usbdp: Drop DP HPD handling
2026-07-01 23:35 ` [PATCH v9 18/38] phy: rockchip: usbdp: Drop DP HPD handling Sebastian Reichel
@ 2026-07-02 0:00 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-02 0:00 UTC (permalink / raw)
To: Sebastian Reichel
Cc: linux-phy, devicetree, neil.armstrong, vkoul, olteanv, robh,
conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Incomplete cleanup of DP HPD logic leaves a dangerous register write in the probe path, violating power domain constraints and risking a bus hang (SError).
--
Note: The format of this report has been altered to a free-form summary due
to restrictions on quoting the original patch code.
Commit: 985925db83ee1c14865f1eaca6f60b9245941678
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
Subject: phy: rockchip: usbdp: Drop DP HPD handling
This commit drops the HPD handling logic from the USBDP PHY driver. The
commit message notes that the registers involved require the display
controller power domain to be enabled, and that HPD should be handled
by the displayport controller itself.
[Severity: High]
This is a pre-existing issue, but it appears the cleanup of the DP HPD logic
might be incomplete, leaving a potentially dangerous register write in the
probe path.
In rk_udphy_get_initial_status() in drivers/phy/rockchip/phy-rockchip-usbdp.c,
there is a check to see if the DP PHY was pre-enabled by the bootloader. If
it was, the driver writes false to the hpd_trigger register via
rk_udphy_grfreg_write() and then sleeps for 100ms.
Since the commit message explicitly acknowledges that these registers require
the display controller (VOP) power domain to be enabled, does this write
violate those power domain constraints?
If the kernel disables the VOP power domain before this driver probes (for
example, due to asynchronous probing and the display driver suspending or
resetting its PM domain), could this write to hpd_trigger cause an
asynchronous abort (SError) and a subsequent kernel panic during boot?
Would it make sense to remove this remaining HPD trigger write in the initial
status check as well, to fully decouple the PHY driver from the display
controller power domain?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=18
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 19/38] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (17 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 18/38] phy: rockchip: usbdp: Drop DP HPD handling Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 20/38] phy: rockchip: usbdp: Re-init the PHY on orientation change Sebastian Reichel
` (18 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Right now the mode_change property is set whenever the mode changes
between USB-only, DP-only and USB-DP. It is needed, because on any
mode change the PHY needs to be re-initialized. Apparently at least
DP also requires a re-init when the cable orientation is changed,
which is currently not being done (except when the orientation switch
also involves a mode change). Prepare for this by renaming mode_change
to phy_needs_reinit.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 928466659486..8f06bb9b1fcb 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -172,7 +172,7 @@ struct rk_udphy {
/* PHY status management */
bool flip;
- bool mode_change;
+ bool phy_needs_reinit;
u8 mode;
u8 status;
@@ -584,7 +584,7 @@ static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
if (udphy->mode == mode)
return;
- udphy->mode_change = true;
+ udphy->phy_needs_reinit = true;
udphy->mode = mode;
}
@@ -987,15 +987,15 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
if (udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
- udphy->mode_change = false;
- } else if (udphy->mode_change) {
+ udphy->phy_needs_reinit = false;
+ } else if (udphy->phy_needs_reinit) {
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
ret = rk_udphy_init(udphy);
if (ret)
return ret;
- udphy->mode_change = false;
+ udphy->phy_needs_reinit = false;
}
udphy->status |= mode;
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 20/38] phy: rockchip: usbdp: Re-init the PHY on orientation change
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (18 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 19/38] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:35 ` [PATCH v9 21/38] phy: rockchip: usbdp: Factor out lane_mux_sel setup Sebastian Reichel
` (17 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Changing the cable orientation reconfigures the lane muxing, which
requires re-initializing the PHY. Without this DP functionality
breaks, if the cable is re-plugged with swapped orientation.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 8f06bb9b1fcb..eab9e82dbfe4 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -626,6 +626,7 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
enum typec_orientation orien)
{
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
+ bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
mutex_lock(&udphy->mutex);
@@ -637,7 +638,10 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
goto unlock_ret;
}
- udphy->flip = orien == TYPEC_ORIENTATION_REVERSE;
+ if (udphy->flip != flipped)
+ udphy->phy_needs_reinit = true;
+
+ udphy->flip = flipped;
rk_udphy_set_typec_default_mapping(udphy);
rk_udphy_usb_bvalid_enable(udphy, true);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 21/38] phy: rockchip: usbdp: Factor out lane_mux_sel setup
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (19 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 20/38] phy: rockchip: usbdp: Re-init the PHY on orientation change Sebastian Reichel
@ 2026-07-01 23:35 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 22/38] phy: rockchip: usbdp: Properly handle TYPEC_STATE_SAFE and TYPEC_STATE_USB Sebastian Reichel
` (16 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:35 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Avoid describing the USB+DP lane_mux_sel logic twice by introducing
a helper function to reduce code duplication.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 81 +++++++++++++++----------------
1 file changed, 40 insertions(+), 41 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index eab9e82dbfe4..70ef02c4a187 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -588,6 +588,42 @@ static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
udphy->mode = mode;
}
+static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
+{
+ u8 mode;
+
+ switch (state) {
+ case TYPEC_DP_STATE_C:
+ case TYPEC_DP_STATE_E:
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
+ mode = UDPHY_MODE_DP;
+ udphy->dp_lanes = 4;
+ break;
+
+ case TYPEC_DP_STATE_D:
+ default:
+ if (udphy->flip) {
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
+ } else {
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
+ }
+ mode = UDPHY_MODE_DP_USB;
+ udphy->dp_lanes = 2;
+ break;
+ }
+
+ rk_udphy_mode_set(udphy, mode);
+}
+
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
{
if (udphy->flip) {
@@ -595,10 +631,6 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
udphy->dp_lane_sel[1] = 1;
udphy->dp_lane_sel[2] = 3;
udphy->dp_lane_sel[3] = 2;
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1);
@@ -608,18 +640,14 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
udphy->dp_lane_sel[1] = 3;
udphy->dp_lane_sel[2] = 1;
udphy->dp_lane_sel[3] = 0;
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
}
- rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB);
- udphy->dp_lanes = 2;
+ /* default to USB3 + DP as 4 lane USB is not supported */
+ rk_udphy_set_typec_state(udphy, TYPEC_DP_STATE_D);
}
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
@@ -1305,7 +1333,6 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
- u8 mode;
/*
* Ignore mux events not involving DP AltMode, because
@@ -1317,38 +1344,10 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
mutex_lock(&udphy->mutex);
- switch (state->mode) {
- case TYPEC_DP_STATE_C:
- case TYPEC_DP_STATE_E:
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
- mode = UDPHY_MODE_DP;
- udphy->dp_lanes = 4;
- break;
-
- case TYPEC_DP_STATE_D:
- default:
- if (udphy->flip) {
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
- } else {
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
- }
- mode = UDPHY_MODE_DP_USB;
- udphy->dp_lanes = 2;
- break;
- }
-
- rk_udphy_mode_set(udphy, mode);
+ rk_udphy_set_typec_state(udphy, state->mode);
mutex_unlock(&udphy->mutex);
+
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 22/38] phy: rockchip: usbdp: Properly handle TYPEC_STATE_SAFE and TYPEC_STATE_USB
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (20 preceding siblings ...)
2026-07-01 23:35 ` [PATCH v9 21/38] phy: rockchip: usbdp: Factor out lane_mux_sel setup Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 23/38] phy: rockchip: usbdp: Use guard functions for mutex Sebastian Reichel
` (15 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
Handle TYPEC_STATE_SAFE and TYPEC_STATE_USB Type-C state events,
so that the muxing is properly updated when exiting DP AltMode.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260619155020.CC7361F000E9%40smtp.kernel.org
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 70ef02c4a187..9f0c0127e6d0 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1329,17 +1329,26 @@ static const struct phy_ops rk_udphy_usb3_phy_ops = {
.owner = THIS_MODULE,
};
+static bool rk_udphy_is_supported_mode(struct typec_mux_state *state)
+{
+ /* Handle Safe State and USB State */
+ if (state->mode < TYPEC_STATE_MODAL)
+ return true;
+
+ /* Handle DP AltMode */
+ if (state->alt && state->alt->svid == USB_TYPEC_DP_SID)
+ return true;
+
+ return false;
+}
+
static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
- /*
- * Ignore mux events not involving DP AltMode, because
- * the mode field is being reused, e.g. state->mode == 4
- * could be either TYPEC_MODE_USB4 or TYPEC_DP_STATE_C.
- */
- if (!state->alt || state->alt->svid != USB_TYPEC_DP_SID)
+ /* Ignore mux events not involving USB or DP */
+ if (!rk_udphy_is_supported_mode(state))
return 0;
mutex_lock(&udphy->mutex);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 23/38] phy: rockchip: usbdp: Use guard functions for mutex
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (21 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 22/38] phy: rockchip: usbdp: Properly handle TYPEC_STATE_SAFE and TYPEC_STATE_USB Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 24/38] phy: rockchip: usbdp: Clear USB status on PHY exit Sebastian Reichel
` (14 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Convert the driver to use guard functions for mutex handling as
a small cleanup. There is a small functional change in the DP PHY
power up function, which no longer sleeps if the internal powerup
code returns an error. This is not a problem as the sleep is only
relevant for successful power-up.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 54 +++++++++++++------------------
1 file changed, 23 insertions(+), 31 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 9f0c0127e6d0..bf6e7edbe631 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -10,6 +10,7 @@
#include <dt-bindings/phy/phy.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
@@ -656,14 +657,15 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
if (orien == TYPEC_ORIENTATION_NONE) {
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
/* unattached */
rk_udphy_usb_bvalid_enable(udphy, false);
- goto unlock_ret;
+
+ return 0;
}
if (udphy->flip != flipped)
@@ -673,8 +675,6 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
rk_udphy_set_typec_default_mapping(udphy);
rk_udphy_usb_bvalid_enable(udphy, true);
-unlock_ret:
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1056,26 +1056,25 @@ static int rk_udphy_dp_phy_power_on(struct phy *phy)
struct rk_udphy *udphy = phy_get_drvdata(phy);
int ret;
- mutex_lock(&udphy->mutex);
+ scoped_guard(mutex, &udphy->mutex) {
+ phy_set_bus_width(phy, udphy->dp_lanes);
- phy_set_bus_width(phy, udphy->dp_lanes);
-
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
- if (ret)
- goto unlock;
+ ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
+ if (ret)
+ return ret;
- rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
+ rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
- rk_udphy_dp_lane_select(udphy);
+ rk_udphy_dp_lane_select(udphy);
+ }
-unlock:
- mutex_unlock(&udphy->mutex);
/*
* If data send by aux channel too fast after phy power on,
* the aux may be not ready which will cause aux error. Adding
* delay to avoid this issue.
*/
usleep_range(10000, 11000);
+
return ret;
}
@@ -1083,10 +1082,10 @@ static int rk_udphy_dp_phy_power_off(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
+
rk_udphy_dp_lane_enable(udphy, 0);
rk_udphy_power_off(udphy, UDPHY_MODE_DP);
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1291,35 +1290,30 @@ static const struct phy_ops rk_udphy_dp_phy_ops = {
static int rk_udphy_usb3_phy_init(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- int ret = 0;
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
+
/* DP only or high-speed, disable U3 port */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
rk_udphy_u3_port_disable(udphy, true);
- goto unlock;
+ return 0;
}
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_USB);
-
-unlock:
- mutex_unlock(&udphy->mutex);
- return ret;
+ return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
}
static int rk_udphy_usb3_phy_exit(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
+
/* DP only or high-speed */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
- goto unlock;
+ return 0;
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
-unlock:
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1351,12 +1345,10 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
if (!rk_udphy_is_supported_mode(state))
return 0;
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
rk_udphy_set_typec_state(udphy, state->mode);
- mutex_unlock(&udphy->mutex);
-
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 24/38] phy: rockchip: usbdp: Clear USB status on PHY exit
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (22 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 23/38] phy: rockchip: usbdp: Use guard functions for mutex Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 25/38] phy: rockchip: usbdp: Hold mutex in DP PHY configure Sebastian Reichel
` (13 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
Ensure the USB status flag is cleared when the USB3 PHY is
exited while the system is in DP-only mode. This can happen
if the USB3 controller device is unbound while a DP-only
adapter is plugged into the USB-C port.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260625-rockchip-usbdp-cleanup-v7-24-38eb3cf654fd%40collabora.com
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index bf6e7edbe631..1a983c1cef4b 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1309,8 +1309,10 @@ static int rk_udphy_usb3_phy_exit(struct phy *phy)
guard(mutex)(&udphy->mutex);
/* DP only or high-speed */
- if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
+ if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
+ udphy->status &= ~UDPHY_MODE_USB;
return 0;
+ }
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 25/38] phy: rockchip: usbdp: Hold mutex in DP PHY configure
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (23 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 24/38] phy: rockchip: usbdp: Clear USB status on PHY exit Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 26/38] phy: rockchip: usbdp: Add some extra debug messages Sebastian Reichel
` (12 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
rk_udphy_dp_phy_configure() accesses some variables from the struct
rk_udphy, which are updated independently from the USB-C framework.
The USB-C mux/orientation switch functions already hold a mutex to
ensure mutual exclusive access to the struct rk_udphy states, so
simply hold the same one in the DP PHY configuration function.
Reproducing problems due to this on real hardware would be really hard,
but could be possible when quickly re-connecting the USB-C connector.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/linux-phy/20260612164627.23D391F000E9@smtp.kernel.org/
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 1a983c1cef4b..c874dfeeece9 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1190,6 +1190,8 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
u32 i, val, lane;
int ret;
+ guard(mutex)(&udphy->mutex);
+
if (dp->set_rate) {
ret = rk_udphy_dp_phy_verify_link_rate(udphy, dp);
if (ret)
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 26/38] phy: rockchip: usbdp: Add some extra debug messages
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (24 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 25/38] phy: rockchip: usbdp: Hold mutex in DP PHY configure Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 27/38] phy: rockchip: usbdp: Avoid xHCI SErrors Sebastian Reichel
` (11 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
It's useful to log PHY reinit to ease debugging issues around
USB-C hotplugging.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index c874dfeeece9..6b0add354880 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -24,6 +24,7 @@
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h>
+#include <linux/string_choices.h>
#include <linux/usb/ch9.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
@@ -468,6 +469,8 @@ static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name)
return reset_control_deassert(list[idx].rstc);
}
+ dev_err(udphy->dev, "failed to de-assert missing reset line: %s\n", name);
+
return -EINVAL;
}
@@ -494,6 +497,8 @@ static void rk_udphy_u3_port_disable(struct rk_udphy *udphy, u8 disable)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
const struct rk_udphy_grf_reg *preg;
+ dev_dbg(udphy->dev, "USB3 port %s\n", str_on_off(!disable));
+
preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg;
rk_udphy_grfreg_write(udphy->usbgrf, preg, disable);
}
@@ -668,8 +673,10 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
return 0;
}
- if (udphy->flip != flipped)
+ if (udphy->flip != flipped) {
+ dev_dbg(udphy->dev, "cable orientation changed, PHY re-init required.\n");
udphy->phy_needs_reinit = true;
+ }
udphy->flip = flipped;
rk_udphy_set_typec_default_mapping(udphy);
@@ -787,6 +794,11 @@ static int rk_udphy_init(struct rk_udphy *udphy)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
int ret;
+ dev_dbg(udphy->dev, "reinit PHY with USB=%s and DP=%s (%u lanes) flipped=%s\n",
+ str_enabled_disabled(udphy->mode & UDPHY_MODE_USB),
+ str_enabled_disabled(udphy->mode & UDPHY_MODE_DP),
+ udphy->dp_lanes, str_yes_no(udphy->flip));
+
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
@@ -857,6 +869,8 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
{
int ret;
+ dev_dbg(udphy->dev, "enable PHY\n");
+
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
if (ret) {
dev_err(udphy->dev, "failed to enable clk\n");
@@ -875,6 +889,7 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
static void rk_udphy_disable(struct rk_udphy *udphy)
{
+ dev_dbg(udphy->dev, "disable PHY\n");
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
rk_udphy_reset_assert_all(udphy);
}
@@ -1346,8 +1361,12 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
/* Ignore mux events not involving USB or DP */
- if (!rk_udphy_is_supported_mode(state))
+ if (!rk_udphy_is_supported_mode(state)) {
+ dev_dbg(udphy->dev, "ignore mux event with mode=%lu\n", state->mode);
return 0;
+ }
+
+ dev_dbg(udphy->dev, "new mode: %lu\n", state->mode);
guard(mutex)(&udphy->mutex);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 27/38] phy: rockchip: usbdp: Avoid xHCI SErrors
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (25 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 26/38] phy: rockchip: usbdp: Add some extra debug messages Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 28/38] phy: rockchip: usbdp: Disable USB3 on probe Sebastian Reichel
` (10 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
The USBDP PHY provides the PIPE clock to the USB3 controller, which
means the PHY must be fully running when anything tries to access
the xHCI registers.
When switching between USB3-only, USB3 + DP and DP-only mode, the
PHY must be re-initialized resulting in a short period of the PHY
being disabled. If the DWC3 driver decides to access the xHCI at
this point the system will fail with an SError.
This patch avoids the problems by disabling the USB3 port before
re-initializing it. This does a couple of things:
- forces phystatus to 0 from GRF (not from PHY)
- switches PIPE clock source from PHY to UTMI (safe fallback clock)
- num_u3_port=0
The last part will be ignored, as DWC3 already probed, but the
clock re-routing will avoid the SError. There is a small delay
afterwards to make sure the mux happened. The datasheet gives
no hints how long it takes, so delay time is a guess.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 6b0add354880..146edd35cf83 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1036,8 +1036,8 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
} else if (udphy->phy_needs_reinit) {
- if (udphy->mode == UDPHY_MODE_DP)
- rk_udphy_u3_port_disable(udphy, true);
+ rk_udphy_u3_port_disable(udphy, true);
+ udelay(10);
ret = rk_udphy_init(udphy);
if (ret)
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 28/38] phy: rockchip: usbdp: Disable USB3 on probe
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (26 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 27/38] phy: rockchip: usbdp: Avoid xHCI SErrors Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 29/38] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert errors Sebastian Reichel
` (9 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Disable USB3 support during probe, so that the driver always starts with
a clean state. With this it no longer makes sense to use -EPROBE_DEFER
for cmn ana lcpll lock timeouts afterwards. This error code should only
appear during probe and the PHY initialization function is also being
called at normal runtime.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 146edd35cf83..f788405dfa1f 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -756,17 +756,7 @@ static int rk_udphy_status_check(struct rk_udphy *udphy)
(val & CMN_ANA_LCPLL_LOCK_DONE), 200, 100000);
if (ret) {
dev_err(udphy->dev, "cmn ana lcpll lock timeout\n");
- /*
- * If earlier software (U-Boot) enabled USB once already
- * the PLL may have problems locking on the first try.
- * It will be successful on the second try, so for the
- * time being a -EPROBE_DEFER will solve the issue.
- *
- * This requires further investigation to understand the
- * root cause, especially considering that the driver is
- * asserting all reset lines at probe time.
- */
- return -EPROBE_DEFER;
+ return ret;
}
if (!udphy->flip) {
@@ -965,6 +955,8 @@ static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
msleep(100);
}
+ rk_udphy_u3_port_disable(udphy, true);
+
exit:
rk_udphy_disable(udphy);
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 29/38] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert errors
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (27 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 28/38] phy: rockchip: usbdp: Disable USB3 on probe Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 30/38] phy: rockchip: usbdp: Only enable USB3 when not in high-speed mode Sebastian Reichel
` (8 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
Handle rk_udphy_reset_deassert returning errors to avoid theoretical
(Rockchip reset controller driver does not return errors) SError.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260626211151.2332F1F000E9%40smtp.kernel.org
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index f788405dfa1f..266cbb0e5325 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -799,8 +799,12 @@ static int rk_udphy_init(struct rk_udphy *udphy)
/* Step 1: power on pma and deassert apb rstn */
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.low_pwrn, true);
- rk_udphy_reset_deassert(udphy, "pma_apb");
- rk_udphy_reset_deassert(udphy, "pcs_apb");
+ ret = rk_udphy_reset_deassert(udphy, "pma_apb");
+ if (ret)
+ goto assert_resets;
+ ret = rk_udphy_reset_deassert(udphy, "pcs_apb");
+ if (ret)
+ goto assert_resets;
/* Step 2: set init sequence and phy refclk */
ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_init_sequence,
@@ -826,8 +830,11 @@ static int rk_udphy_init(struct rk_udphy *udphy)
FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
/* Step 4: deassert init rstn and wait for 200ns from datasheet */
- if (udphy->mode & UDPHY_MODE_USB)
- rk_udphy_reset_deassert(udphy, "init");
+ if (udphy->mode & UDPHY_MODE_USB) {
+ ret = rk_udphy_reset_deassert(udphy, "init");
+ if (ret)
+ goto assert_resets;
+ }
if (udphy->mode & UDPHY_MODE_DP) {
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
@@ -839,8 +846,14 @@ static int rk_udphy_init(struct rk_udphy *udphy)
/* Step 5: deassert cmn/lane rstn */
if (udphy->mode & UDPHY_MODE_USB) {
- rk_udphy_reset_deassert(udphy, "cmn");
- rk_udphy_reset_deassert(udphy, "lane");
+ ret = rk_udphy_reset_deassert(udphy, "cmn");
+ if (ret)
+ goto assert_resets;
+
+ ret = rk_udphy_reset_deassert(udphy, "lane");
+ if (ret)
+ goto assert_resets;
+
}
/* Step 6: wait for lock done of pll */
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 30/38] phy: rockchip: usbdp: Only enable USB3 when not in high-speed mode
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (28 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 29/38] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert errors Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 31/38] phy: core: add notifier infrastructure Sebastian Reichel
` (7 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel, Sashiko
Ensure that USB3 mode is not accidently enabled during PHY re-init
for systems that are configured as high-speed only via DT.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260626212424.C215E1F000E9%40smtp.kernel.org
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 266cbb0e5325..c69766929b3a 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1037,7 +1037,7 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
if (ret)
return ret;
- if (udphy->mode & UDPHY_MODE_USB)
+ if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
} else if (udphy->phy_needs_reinit) {
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 31/38] phy: core: add notifier infrastructure
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (29 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 30/38] phy: rockchip: usbdp: Only enable USB3 when not in high-speed mode Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-02 0:06 ` sashiko-bot
2026-07-01 23:36 ` [PATCH v9 32/38] usb: dwc3: core: support PHY reset notifications Sebastian Reichel
` (6 subsequent siblings)
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Some PHY devices with multiple ports (e.g. USB3 and DP) require a reset
if the configuration changes or cable orientation changes. This is a
problem, as the consumer device will run into undefined behavior.
With the new PHY notifier API introduced in this patch, the consumer
driver can hook into reset events coming from a PHY device to handle the
PHY going down gracefully.
Note that this uses -ENOSYS instead of the more sensible -ENOTSUP for
the stub functions when GENERIC_PHY is disabled to stay consistent with
the existing ones.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/phy-core.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/phy/phy.h | 40 ++++++++++++++++++++++++++++++++++
2 files changed, 97 insertions(+)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 21aaf2f76e53..533473d975d3 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -542,6 +542,62 @@ int phy_notify_state(struct phy *phy, union phy_notify state)
}
EXPORT_SYMBOL_GPL(phy_notify_state);
+/**
+ * phy_register_notifier() - register a notifier for PHY events
+ * @phy: the phy returned by phy_get()
+ * @nb: notifier block to register
+ *
+ * Allows PHY consumers to receive notifications about PHY reset events.
+ * PHY providers can signal these events using phy_notify_reset().
+ *
+ * Returns: %0 if successful, a negative error code otherwise
+ */
+int phy_register_notifier(struct phy *phy, struct notifier_block *nb)
+{
+ if (!phy)
+ return 0;
+
+ return blocking_notifier_chain_register(&phy->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(phy_register_notifier);
+
+/**
+ * phy_unregister_notifier() - unregister a notifier for PHY events
+ * @phy: the phy returned by phy_get()
+ * @nb: notifier block to unregister
+ *
+ * Returns: %0 if successful, a negative error code otherwise
+ */
+int phy_unregister_notifier(struct phy *phy, struct notifier_block *nb)
+{
+ if (!phy)
+ return 0;
+
+ return blocking_notifier_chain_unregister(&phy->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(phy_unregister_notifier);
+
+/**
+ * phy_notify_reset() - notify consumers of a PHY reset event
+ * @phy: the phy that is being reset
+ * @event: the notification event (PRE_RESET or POST_RESET)
+ *
+ * Called by PHY providers to notify consumers that the PHY is about to
+ * be reset or has completed a reset. This allows consumers to quiesce
+ * hardware before the PHY becomes unavailable.
+ *
+ * Returns: %0 if successful or no notifiers registered, a negative error
+ * code if a notifier returns an error (for PRE_RESET only)
+ */
+int phy_notify_reset(struct phy *phy, enum phy_notification event)
+{
+ if (!phy)
+ return 0;
+
+ return blocking_notifier_call_chain(&phy->notifier, event, phy);
+}
+EXPORT_SYMBOL_GPL(phy_notify_reset);
+
/**
* phy_configure() - Changes the phy parameters
* @phy: the phy returned by phy_get()
@@ -1018,6 +1074,7 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
device_initialize(&phy->dev);
lockdep_register_key(&phy->lockdep_key);
mutex_init_with_key(&phy->mutex, &phy->lockdep_key);
+ BLOCKING_INIT_NOTIFIER_HEAD(&phy->notifier);
phy->dev.class = &phy_class;
phy->dev.parent = dev;
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index ea47975e288a..3779a4d0a02c 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -11,6 +11,7 @@
#define __DRIVERS_PHY_H
#include <linux/err.h>
+#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
@@ -53,6 +54,16 @@ enum phy_media {
PHY_MEDIA_DAC,
};
+/**
+ * enum phy_notification - PHY notification events
+ * @PHY_NOTIFY_PRE_RESET: PHY is about to be reset, consumers should quiesce
+ * @PHY_NOTIFY_POST_RESET: PHY reset is complete, consumers may resume
+ */
+enum phy_notification {
+ PHY_NOTIFY_PRE_RESET,
+ PHY_NOTIFY_POST_RESET,
+};
+
enum phy_ufs_state {
PHY_UFS_HIBERN8_ENTER,
PHY_UFS_HIBERN8_EXIT,
@@ -170,6 +181,7 @@ struct phy_attrs {
* @power_count: used to protect when the PHY is used by multiple consumers
* @attrs: used to specify PHY specific attributes
* @pwr: power regulator associated with the phy
+ * @notifier: notifier head for PHY reset events
* @debugfs: debugfs directory
*/
struct phy {
@@ -182,6 +194,7 @@ struct phy {
int power_count;
struct phy_attrs attrs;
struct regulator *pwr;
+ struct blocking_notifier_head notifier;
struct dentry *debugfs;
};
@@ -267,6 +280,9 @@ int phy_calibrate(struct phy *phy);
int phy_notify_connect(struct phy *phy, int port);
int phy_notify_disconnect(struct phy *phy, int port);
int phy_notify_state(struct phy *phy, union phy_notify state);
+int phy_register_notifier(struct phy *phy, struct notifier_block *nb);
+int phy_unregister_notifier(struct phy *phy, struct notifier_block *nb);
+int phy_notify_reset(struct phy *phy, enum phy_notification event);
static inline int phy_get_bus_width(struct phy *phy)
{
return phy->attrs.bus_width;
@@ -428,6 +444,30 @@ static inline int phy_notify_state(struct phy *phy, union phy_notify state)
return -ENOSYS;
}
+static inline int phy_register_notifier(struct phy *phy,
+ struct notifier_block *nb)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
+static inline int phy_unregister_notifier(struct phy *phy,
+ struct notifier_block *nb)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
+static inline int phy_notify_reset(struct phy *phy,
+ enum phy_notification event)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
static inline int phy_configure(struct phy *phy,
union phy_configure_opts *opts)
{
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 31/38] phy: core: add notifier infrastructure
2026-07-01 23:36 ` [PATCH v9 31/38] phy: core: add notifier infrastructure Sebastian Reichel
@ 2026-07-02 0:06 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-02 0:06 UTC (permalink / raw)
To: Sebastian Reichel
Cc: linux-phy, conor+dt, neil.armstrong, devicetree, robh, olteanv,
vkoul
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] `phy_notify_reset` returns the raw notifier chain status instead of a standard negative error code, violating its documented return values and breaking error checking for callers.
--
commit 6d8c66a69f0a84cdc34b5b056c42d3cc35cb771e
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: core: add notifier infrastructure
This commit adds a new PHY notifier API to allow consumer drivers to hook
into reset events coming from a PHY device. This helps gracefully handle
situations where PHY devices require a reset due to configuration or cable
orientation changes.
> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> index 21aaf2f76e53e..533473d975d37 100644
> --- a/drivers/phy/phy-core.c
> +++ b/drivers/phy/phy-core.c
> @@ -542,6 +542,62 @@ int phy_notify_state(struct phy *phy, union phy_notify state)
[ ... ]
> +/**
> + * phy_notify_reset() - notify consumers of a PHY reset event
> + * @phy: the phy that is being reset
> + * @event: the notification event (PRE_RESET or POST_RESET)
> + *
> + * Called by PHY providers to notify consumers that the PHY is about to
> + * be reset or has completed a reset. This allows consumers to quiesce
> + * hardware before the PHY becomes unavailable.
> + *
> + * Returns: %0 if successful or no notifiers registered, a negative error
> + * code if a notifier returns an error (for PRE_RESET only)
> + */
> +int phy_notify_reset(struct phy *phy, enum phy_notification event)
> +{
> + if (!phy)
> + return 0;
> +
> + return blocking_notifier_call_chain(&phy->notifier, event, phy);
[Severity: Medium]
Does this return the raw notifier chain status (like NOTIFY_OK or NOTIFY_BAD)
instead of the documented negative error code?
If callers check the return value with `if (ret < 0)`, they will fail to
detect errors since NOTIFY_BAD and notifier_from_errno() results are positive
integers. Also, checking `if (ret)` might falsely detect an error on success
because NOTIFY_OK is 1.
Should this use notifier_to_errno() to convert the return value before passing
it back to the caller?
> +}
> +EXPORT_SYMBOL_GPL(phy_notify_reset);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=31
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 32/38] usb: dwc3: core: support PHY reset notifications
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (30 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 31/38] phy: core: add notifier infrastructure Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-02 0:08 ` sashiko-bot
2026-07-01 23:36 ` [PATCH v9 33/38] phy: rockchip: usbdp: Add phy reset notification support Sebastian Reichel
` (5 subsequent siblings)
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
On recent Rockchip platforms (at least RK3588 & RK3576), DWC3 IP is used
with a USBDP PHY providing USB3 and DP. This PHY needs to be reset when
the mode changes, which may happen when plugging in different USB-C
devices.
If the USBDP PHY resets with the DWC3 IP running, its internal state
corrupts resulting in the USBDP PHY not being able to lock some PLL
clocks, which effectively renders USB3 unusable.
To fix the issue this adds handling for the new PHY framework reset
notifications, which will assert PHYSOFTRST before the actual PHY
is disabled and will deassert it once the PHY returns.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/usb/dwc3/core.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/usb/dwc3/core.h | 16 ++++++++++
2 files changed, 98 insertions(+)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 517aa7f1486d..fdc92c22381a 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -30,6 +30,7 @@
#include <linux/pinctrl/devinfo.h>
#include <linux/reset.h>
#include <linux/bitfield.h>
+#include <linux/phy/phy.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -660,6 +661,9 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc)
return ret;
}
+static void dwc3_phy_register_notifiers(struct dwc3 *dwc);
+static void dwc3_phy_unregister_notifiers(struct dwc3 *dwc);
+
static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
{
u32 reg;
@@ -845,6 +849,8 @@ static int dwc3_phy_init(struct dwc3 *dwc)
goto err_exit_usb3_phy;
}
+ dwc3_phy_register_notifiers(dwc);
+
/*
* Above DWC_usb3.0 1.94a, it is recommended to set
* DWC3_GUSB3PIPECTL_SUSPHY and DWC3_GUSB2PHYCFG_SUSPHY to '0' during
@@ -880,10 +886,86 @@ static int dwc3_phy_init(struct dwc3 *dwc)
return ret;
}
+static int dwc3_usb3_phy_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct dwc3 *dwc = container_of(nb, struct dwc3_phy_nb, nb)->dwc;
+ int i;
+
+ switch (action) {
+ case PHY_NOTIFY_PRE_RESET:
+ /*
+ * If the controller is already suspended (e.g. runtime PM),
+ * there is no internal state to clean up.
+ */
+ if (pm_runtime_suspended(dwc->dev))
+ return NOTIFY_OK;
+
+ dwc->phy_reset_in_progress = true;
+
+ /*
+ * Assert USB3 PHY soft reset within DWC3 before the external
+ * PHY resets. This disconnects the PIPE interface, preventing
+ * the DWC3 from interfering with PHY reinitialization and
+ * avoiding LCPLL lock failures.
+ */
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ u32 reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i));
+
+ reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg);
+ }
+ break;
+
+ case PHY_NOTIFY_POST_RESET:
+ if (!dwc->phy_reset_in_progress)
+ return NOTIFY_OK;
+
+ dwc->phy_reset_in_progress = false;
+
+ /*
+ * Deassert PHY soft reset and reconfigure the PIPE interface
+ * settings after PHY reinitialization.
+ */
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ u32 reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i));
+
+ reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg);
+ }
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static void dwc3_phy_register_notifiers(struct dwc3 *dwc)
+{
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ dwc->usb3_phy_nb[i].nb.notifier_call = dwc3_usb3_phy_notify;
+ dwc->usb3_phy_nb[i].dwc = dwc;
+ phy_register_notifier(dwc->usb3_generic_phy[i],
+ &dwc->usb3_phy_nb[i].nb);
+ }
+}
+
+static void dwc3_phy_unregister_notifiers(struct dwc3 *dwc)
+{
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_unregister_notifier(dwc->usb3_generic_phy[i],
+ &dwc->usb3_phy_nb[i].nb);
+}
+
static void dwc3_phy_exit(struct dwc3 *dwc)
{
int i;
+ dwc3_phy_unregister_notifiers(dwc);
+
for (i = 0; i < dwc->num_usb3_ports; i++)
phy_exit(dwc->usb3_generic_phy[i]);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e0dee9d28740..79d833d00137 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1002,6 +1002,18 @@ struct dwc3_glue_ops {
void (*pre_run_stop)(struct dwc3 *dwc, bool is_on);
};
+struct dwc3;
+
+/**
+ * struct dwc3_phy_nb - wrapper for PHY notifier block
+ * @nb: notifier block
+ * @dwc: back-pointer to the DWC3 controller
+ */
+struct dwc3_phy_nb {
+ struct notifier_block nb;
+ struct dwc3 *dwc;
+};
+
/**
* struct dwc3 - representation of our controller
* @drd_work: workqueue used for role swapping
@@ -1065,6 +1077,7 @@ struct dwc3_glue_ops {
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to array of USB2 PHYs
* @usb3_generic_phy: pointer to array of USB3 PHYs
+ * @usb3_phy_nb: notifier blocks for USB3 PHY reset events
* @num_usb2_ports: number of USB2 ports
* @num_usb3_ports: number of USB3 ports
* @phys_ready: flag to indicate that PHYs are ready
@@ -1171,6 +1184,7 @@ struct dwc3_glue_ops {
* @suspended: set to track suspend event due to U3/L2.
* @susphy_state: state of DWC3_GUSB2PHYCFG_SUSPHY + DWC3_GUSB3PIPECTL_SUSPHY
* before PM suspend.
+ * @phy_reset_in_progress: set if a PHY reset notification is being handled
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
* @max_cfg_eps: current max number of IN eps used across all USB configs.
@@ -1229,6 +1243,7 @@ struct dwc3 {
struct phy *usb2_generic_phy[DWC3_USB2_MAX_PORTS];
struct phy *usb3_generic_phy[DWC3_USB3_MAX_PORTS];
+ struct dwc3_phy_nb usb3_phy_nb[DWC3_USB3_MAX_PORTS];
u8 num_usb2_ports;
u8 num_usb3_ports;
@@ -1415,6 +1430,7 @@ struct dwc3 {
unsigned wakeup_configured:1;
unsigned suspended:1;
unsigned susphy_state:1;
+ unsigned phy_reset_in_progress:1;
u16 imod_interval;
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 32/38] usb: dwc3: core: support PHY reset notifications
2026-07-01 23:36 ` [PATCH v9 32/38] usb: dwc3: core: support PHY reset notifications Sebastian Reichel
@ 2026-07-02 0:08 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-02 0:08 UTC (permalink / raw)
To: Sebastian Reichel
Cc: neil.armstrong, linux-phy, conor+dt, robh, devicetree, olteanv,
vkoul
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] The PHY reset notifier checks `pm_runtime_suspended()` without securing a PM reference, which can cause a kernel crash if the DWC3 controller suspends concurrently.
- [High] Unsynchronized modification of the `phy_reset_in_progress` bitfield causes a data race that can corrupt adjacent bitfields in `struct dwc3`, including PM state flags like `suspended`.
- [High] The PHY reset notifier incorrectly treats a per-port PHY reset as a global event, applying soft reset to all USB3 ports and using a single global state flag, breaking multi-port controllers.
- [High] The notifier performs read-modify-write operations on `DWC3_GUSB3PIPECTL` without acquiring the DWC3 spinlock, leading to register state corruption.
--
commit e7db10d74b0389657368c887d592e9b830ec1fac
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
usb: dwc3: core: support PHY reset notifications
This adds handling for the new PHY framework reset notifications to assert
PHYSOFTRST within the DWC3 controller before the external PHY resets. This
prevents the DWC3 from interfering with PHY reinitialization and avoids
LCPLL lock failures on Rockchip platforms.
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> --- a/drivers/usb/dwc3/core.h
> +++ b/drivers/usb/dwc3/core.h
[ ... ]
> @@ -1430,6 +1430,7 @@ struct dwc3 {
> unsigned wakeup_configured:1;
> unsigned suspended:1;
> unsigned susphy_state:1;
> + unsigned phy_reset_in_progress:1;
[Severity: High]
Can modifying this locklessly corrupt the adjacent bitfield flags?
Since phy_reset_in_progress shares a memory word with suspended and
susphy_state, a concurrent modification to those PM state flags could be
overwritten during the non-atomic read-modify-write of this bitfield.
>
> u16 imod_interval;
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
[ ... ]
> @@ -886,10 +886,86 @@ static int dwc3_phy_init(struct dwc3 *dwc)
> return ret;
> }
>
> +static int dwc3_usb3_phy_notify(struct notifier_block *nb,
> + unsigned long action, void *data)
> +{
> + struct dwc3 *dwc = container_of(nb, struct dwc3_phy_nb, nb)->dwc;
> + int i;
> +
> + switch (action) {
> + case PHY_NOTIFY_PRE_RESET:
> + /*
> + * If the controller is already suspended (e.g. runtime PM),
> + * there is no internal state to clean up.
> + */
> + if (pm_runtime_suspended(dwc->dev))
> + return NOTIFY_OK;
[Severity: High]
Does this check protect against the device suspending immediately after?
If pm_runtime_suspended() returns false, the controller could still
asynchronously suspend before PHY_NOTIFY_POST_RESET occurs.
Should this acquire a reference via pm_runtime_get_if_active() to prevent
the device clocks from being gated while the reset is in progress?
> +
> + dwc->phy_reset_in_progress = true;
> +
> + /*
> + * Assert USB3 PHY soft reset within DWC3 before the external
> + * PHY resets. This disconnects the PIPE interface, preventing
> + * the DWC3 from interfering with PHY reinitialization and
> + * avoiding LCPLL lock failures.
> + */
> + for (i = 0; i < dwc->num_usb3_ports; i++) {
[Severity: High]
Does this unintentionally disrupt all ports on a multi-port controller?
The notifier block identifies the specific dwc3_phy_nb (and implicitly the
port) that triggered the event, but this loops over all num_usb3_ports and
asserts soft reset on all of them.
Overlapping resets on different ports might also clobber the shared
dwc->phy_reset_in_progress flag, dropping connections on ports that did
not trigger the notification.
> + u32 reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i));
> +
> + reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
> + dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg);
[Severity: High]
Can this lockless read-modify-write corrupt DWC3_GUSB3PIPECTL?
This modifies the register without holding dwc->lock.
If this races with PM transitions running dwc3_enable_susphy(), which also
modifies this register, could one of the register updates be lost?
> + }
> + break;
> +
> + case PHY_NOTIFY_POST_RESET:
> + if (!dwc->phy_reset_in_progress)
> + return NOTIFY_OK;
> +
> + dwc->phy_reset_in_progress = false;
> +
> + /*
> + * Deassert PHY soft reset and reconfigure the PIPE interface
> + * settings after PHY reinitialization.
> + */
> + for (i = 0; i < dwc->num_usb3_ports; i++) {
> + u32 reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i));
[Severity: High]
If the device suspended asynchronously between PRE_RESET and POST_RESET,
could accessing this hardware register cause a synchronous external abort?
Since phy_reset_in_progress is true, this path executes unconditionally
even if the controller's clocks are now disabled.
> +
> + reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
> + dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg);
> + }
> + break;
> + }
> +
> + return NOTIFY_OK;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=32
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 33/38] phy: rockchip: usbdp: Add phy reset notification support
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (31 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 32/38] usb: dwc3: core: support PHY reset notifications Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 34/38] phy: rockchip: usbdp: Rename mode to hw_mode Sebastian Reichel
` (4 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
To resolve issues with running into permanent "cmn ana lcpll lock
timeout" errors after a few device replugs, add support for reset
notifications, which will be handled by the DWC3 driver to gracefully
handle the PHY being disabled. This avoids corrupting the controller's
internal state and the PIPE interface between the USB3 controller and
the PHY, thus fixing the issue.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index c69766929b3a..ad594bb3962a 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1033,6 +1033,11 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
}
if (udphy->status == UDPHY_MODE_NONE) {
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
+
+ rk_udphy_u3_port_disable(udphy, true);
+ udelay(10);
+
ret = rk_udphy_setup(udphy);
if (ret)
return ret;
@@ -1040,13 +1045,22 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
+
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
} else if (udphy->phy_needs_reinit) {
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
+
rk_udphy_u3_port_disable(udphy, true);
udelay(10);
ret = rk_udphy_init(udphy);
- if (ret)
+ if (ret) {
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
return ret;
+ }
+
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
+
udphy->phy_needs_reinit = false;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 34/38] phy: rockchip: usbdp: Rename mode to hw_mode
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (32 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 33/38] phy: rockchip: usbdp: Add phy reset notification support Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 35/38] phy: rockchip: usbdp: Simplify power state handling Sebastian Reichel
` (3 subsequent siblings)
37 siblings, 0 replies; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Rename mode field to hw_mode to make clear that this is the modes
currently supported by the hardware, but not necessarily requested
by software. I.e. it is only set by either the USB-C state machine
or device-tree if the PHY is used in a fixed routing setup.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 46 +++++++++++++++----------------
1 file changed, 23 insertions(+), 23 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index ad594bb3962a..a19b048fb39c 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -175,7 +175,7 @@ struct rk_udphy {
/* PHY status management */
bool flip;
bool phy_needs_reinit;
- u8 mode;
+ u8 hw_mode; /* modes currently supported by hardware */
u8 status;
/* utilized for USB */
@@ -585,18 +585,18 @@ static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
}
-static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
+static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 hw_mode)
{
- if (udphy->mode == mode)
+ if (udphy->hw_mode == hw_mode)
return;
udphy->phy_needs_reinit = true;
- udphy->mode = mode;
+ udphy->hw_mode = hw_mode;
}
static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
{
- u8 mode;
+ u8 hw_mode;
switch (state) {
case TYPEC_DP_STATE_C:
@@ -605,7 +605,7 @@ static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
- mode = UDPHY_MODE_DP;
+ hw_mode = UDPHY_MODE_DP;
udphy->dp_lanes = 4;
break;
@@ -622,12 +622,12 @@ static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
}
- mode = UDPHY_MODE_DP_USB;
+ hw_mode = UDPHY_MODE_DP_USB;
udphy->dp_lanes = 2;
break;
}
- rk_udphy_mode_set(udphy, mode);
+ rk_udphy_mode_set(udphy, hw_mode);
}
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
@@ -750,7 +750,7 @@ static int rk_udphy_status_check(struct rk_udphy *udphy)
int ret;
/* LCPLL check */
- if (udphy->mode & UDPHY_MODE_USB) {
+ if (udphy->hw_mode & UDPHY_MODE_USB) {
ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_LCPLL_DONE_OFFSET,
val, (val & CMN_ANA_LCPLL_AFC_DONE) &&
(val & CMN_ANA_LCPLL_LOCK_DONE), 200, 100000);
@@ -785,15 +785,15 @@ static int rk_udphy_init(struct rk_udphy *udphy)
int ret;
dev_dbg(udphy->dev, "reinit PHY with USB=%s and DP=%s (%u lanes) flipped=%s\n",
- str_enabled_disabled(udphy->mode & UDPHY_MODE_USB),
- str_enabled_disabled(udphy->mode & UDPHY_MODE_DP),
+ str_enabled_disabled(udphy->hw_mode & UDPHY_MODE_USB),
+ str_enabled_disabled(udphy->hw_mode & UDPHY_MODE_DP),
udphy->dp_lanes, str_yes_no(udphy->flip));
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
/* enable rx lfps for usb */
- if (udphy->mode & UDPHY_MODE_USB)
+ if (udphy->hw_mode & UDPHY_MODE_USB)
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.rx_lfps, true);
/* Step 1: power on pma and deassert apb rstn */
@@ -830,13 +830,13 @@ static int rk_udphy_init(struct rk_udphy *udphy)
FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
/* Step 4: deassert init rstn and wait for 200ns from datasheet */
- if (udphy->mode & UDPHY_MODE_USB) {
+ if (udphy->hw_mode & UDPHY_MODE_USB) {
ret = rk_udphy_reset_deassert(udphy, "init");
if (ret)
goto assert_resets;
}
- if (udphy->mode & UDPHY_MODE_DP) {
+ if (udphy->hw_mode & UDPHY_MODE_DP) {
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
CMN_DP_INIT_RSTN,
FIELD_PREP(CMN_DP_INIT_RSTN, 0x1));
@@ -845,7 +845,7 @@ static int rk_udphy_init(struct rk_udphy *udphy)
udelay(1);
/* Step 5: deassert cmn/lane rstn */
- if (udphy->mode & UDPHY_MODE_USB) {
+ if (udphy->hw_mode & UDPHY_MODE_USB) {
ret = rk_udphy_reset_deassert(udphy, "cmn");
if (ret)
goto assert_resets;
@@ -904,7 +904,7 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
num_lanes = device_property_count_u32(udphy->dev, "rockchip,dp-lane-mux");
if (num_lanes < 0) {
dev_dbg(udphy->dev, "no dp-lane-mux, following dp alt mode\n");
- udphy->mode = UDPHY_MODE_USB;
+ udphy->hw_mode = UDPHY_MODE_USB;
return 0;
}
@@ -933,10 +933,10 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
}
}
- udphy->mode = UDPHY_MODE_DP;
+ udphy->hw_mode = UDPHY_MODE_DP;
udphy->dp_lanes = num_lanes;
if (num_lanes == 1 || num_lanes == 2) {
- udphy->mode |= UDPHY_MODE_USB;
+ udphy->hw_mode |= UDPHY_MODE_USB;
udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP) ||
(udphy->lane_mux_sel[1] == PHY_LANE_MUX_DP);
}
@@ -1027,7 +1027,7 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
{
int ret;
- if (!(udphy->mode & mode)) {
+ if (!(udphy->hw_mode & mode)) {
dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
return 0;
}
@@ -1042,7 +1042,7 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
if (ret)
return ret;
- if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
+ if (!udphy->hs && udphy->hw_mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
@@ -1071,7 +1071,7 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
{
- if (!(udphy->mode & mode)) {
+ if (!(udphy->hw_mode & mode)) {
dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
return;
}
@@ -1330,7 +1330,7 @@ static int rk_udphy_usb3_phy_init(struct phy *phy)
guard(mutex)(&udphy->mutex);
/* DP only or high-speed, disable U3 port */
- if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
+ if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs) {
rk_udphy_u3_port_disable(udphy, true);
return 0;
}
@@ -1345,7 +1345,7 @@ static int rk_udphy_usb3_phy_exit(struct phy *phy)
guard(mutex)(&udphy->mutex);
/* DP only or high-speed */
- if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
+ if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs) {
udphy->status &= ~UDPHY_MODE_USB;
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* [PATCH v9 35/38] phy: rockchip: usbdp: Simplify power state handling
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (33 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 34/38] phy: rockchip: usbdp: Rename mode to hw_mode Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-02 0:12 ` sashiko-bot
2026-07-01 23:36 ` [PATCH v9 36/38] phy: rockchip: usbdp: Rename phy_needs_reinit to orientation_changed Sebastian Reichel
` (2 subsequent siblings)
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Simplify power state handling by introducing sw_mode in addition
to the hw_mode field, so that the PHY knows about the currently
supported modes from the hardware perspective, the current modes
requested by software and the actual hardware status.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 117 +++++++++++++-----------------
1 file changed, 49 insertions(+), 68 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index a19b048fb39c..51aac07ef9fe 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -176,7 +176,8 @@ struct rk_udphy {
bool flip;
bool phy_needs_reinit;
u8 hw_mode; /* modes currently supported by hardware */
- u8 status;
+ u8 sw_mode; /* modes currently requested */
+ u8 status; /* current PHY power state */
/* utilized for USB */
bool hs; /* flag for high-speed */
@@ -585,15 +586,6 @@ static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
}
-static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 hw_mode)
-{
- if (udphy->hw_mode == hw_mode)
- return;
-
- udphy->phy_needs_reinit = true;
- udphy->hw_mode = hw_mode;
-}
-
static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
{
u8 hw_mode;
@@ -627,7 +619,7 @@ static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state
break;
}
- rk_udphy_mode_set(udphy, hw_mode);
+ udphy->hw_mode = hw_mode;
}
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
@@ -1023,66 +1015,61 @@ static int rk_udphy_parse_dt(struct rk_udphy *udphy)
return rk_udphy_reset_init(udphy, dev);
}
-static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
+static int rk_udphy_update_power_state(struct rk_udphy *udphy)
{
+ u8 target_mode = udphy->hw_mode & udphy->sw_mode;
int ret;
- if (!(udphy->hw_mode & mode)) {
- dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
+ if (!udphy->phy_needs_reinit && udphy->status == target_mode)
return 0;
- }
- if (udphy->status == UDPHY_MODE_NONE) {
- phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
+ /*
+ * Inform DWC3 driver, that we are about to reset the PHY, so that it can
+ * assert its PIPE reset lines and avoid DWC3 getting into a buggy state.
+ */
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
- rk_udphy_u3_port_disable(udphy, true);
- udelay(10);
+ /*
+ * Disable USB3 port, which among other things re-routes a DWC3 clock to
+ * avoid SErrors when the DWC3 registers are accessed while the PHY is
+ * disabled.
+ */
+ rk_udphy_u3_port_disable(udphy, true);
+ udelay(10);
+ if (udphy->status == UDPHY_MODE_NONE) {
+ /* Power up (incl. clocks) */
ret = rk_udphy_setup(udphy);
- if (ret)
+ if (ret) {
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
return ret;
-
- if (!udphy->hs && udphy->hw_mode & UDPHY_MODE_USB)
- rk_udphy_u3_port_disable(udphy, false);
- udphy->phy_needs_reinit = false;
-
- phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
- } else if (udphy->phy_needs_reinit) {
- phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
-
- rk_udphy_u3_port_disable(udphy, true);
- udelay(10);
-
+ }
+ } else if (target_mode == UDPHY_MODE_NONE) {
+ /* Power down (incl. clocks) */
+ rk_udphy_disable(udphy);
+ } else {
+ /* Mode change => re-init */
ret = rk_udphy_init(udphy);
if (ret) {
phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
return ret;
}
-
- phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
-
- udphy->phy_needs_reinit = false;
}
- udphy->status |= mode;
+ /* Ensure USB3 support is enabled when supported and requested */
+ if (!udphy->hs && target_mode & UDPHY_MODE_USB)
+ rk_udphy_u3_port_disable(udphy, false);
- return 0;
-}
-
-static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
-{
- if (!(udphy->hw_mode & mode)) {
- dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
- return;
- }
-
- if (!udphy->status)
- return;
+ /*
+ * Inform DWC3, that we are done with the reset, so that it can deassert
+ * its PIPE reset line.
+ */
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
- udphy->status &= ~mode;
+ udphy->status = target_mode;
+ udphy->phy_needs_reinit = false;
- if (udphy->status == UDPHY_MODE_NONE)
- rk_udphy_disable(udphy);
+ return 0;
}
static int rk_udphy_dp_phy_power_on(struct phy *phy)
@@ -1091,9 +1078,11 @@ static int rk_udphy_dp_phy_power_on(struct phy *phy)
int ret;
scoped_guard(mutex, &udphy->mutex) {
+ udphy->sw_mode |= UDPHY_MODE_DP;
+
phy_set_bus_width(phy, udphy->dp_lanes);
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
+ ret = rk_udphy_update_power_state(udphy);
if (ret)
return ret;
@@ -1118,8 +1107,10 @@ static int rk_udphy_dp_phy_power_off(struct phy *phy)
guard(mutex)(&udphy->mutex);
+ udphy->sw_mode &= ~UDPHY_MODE_DP;
+
rk_udphy_dp_lane_enable(udphy, 0);
- rk_udphy_power_off(udphy, UDPHY_MODE_DP);
+ rk_udphy_update_power_state(udphy);
return 0;
}
@@ -1329,13 +1320,9 @@ static int rk_udphy_usb3_phy_init(struct phy *phy)
guard(mutex)(&udphy->mutex);
- /* DP only or high-speed, disable U3 port */
- if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs) {
- rk_udphy_u3_port_disable(udphy, true);
- return 0;
- }
+ udphy->sw_mode |= UDPHY_MODE_USB;
- return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
+ return rk_udphy_update_power_state(udphy);
}
static int rk_udphy_usb3_phy_exit(struct phy *phy)
@@ -1344,15 +1331,9 @@ static int rk_udphy_usb3_phy_exit(struct phy *phy)
guard(mutex)(&udphy->mutex);
- /* DP only or high-speed */
- if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs) {
- udphy->status &= ~UDPHY_MODE_USB;
- return 0;
- }
+ udphy->sw_mode &= ~UDPHY_MODE_USB;
- rk_udphy_power_off(udphy, UDPHY_MODE_USB);
-
- return 0;
+ return rk_udphy_update_power_state(udphy);
}
static const struct phy_ops rk_udphy_usb3_phy_ops = {
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 35/38] phy: rockchip: usbdp: Simplify power state handling
2026-07-01 23:36 ` [PATCH v9 35/38] phy: rockchip: usbdp: Simplify power state handling Sebastian Reichel
@ 2026-07-02 0:12 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-02 0:12 UTC (permalink / raw)
To: Sebastian Reichel
Cc: robh, linux-phy, conor+dt, devicetree, neil.armstrong, vkoul,
olteanv
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Critical] Dynamic PHY re-initialization destroys active DP lane state, silently breaking the active display link.
- [High] State leak on initialization failure leaves the driver desynchronized from the PHY subsystem.
- [High] High-speed only configurations fully power on unused SuperSpeed PHY hardware, wasting power and risking timeouts.
- [High] The PHY fails to re-initialize when the Type-C mux changes between 2-lane DP and 4-lane DP while only DP is actively powered on.
- [High] Initialization failure during a mode change leaves the software state permanently desynchronized from the hardware.
- [Medium] PHY_NOTIFY_POST_RESET is sent to the DWC3 driver unconditionally, even when powering down the PHY.
--
commit e22432b71115cadf57aac2173cacace2579279d3
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
Subject: phy: rockchip: usbdp: Simplify power state handling
This commit introduces a sw_mode field to track software-requested PHY modes
independently from hardware capabilities. It centralizes power state
management and re-initialization into a single update function.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index a19b048fb39ca..51aac07ef9fe9 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -1023,66 +1015,61 @@ static int rk_udphy_parse_dt(struct rk_udphy *udphy)
> return rk_udphy_reset_init(udphy, dev);
> }
>
> -static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
> +static int rk_udphy_update_power_state(struct rk_udphy *udphy)
> {
> + u8 target_mode = udphy->hw_mode & udphy->sw_mode;
> int ret;
>
> - if (!(udphy->hw_mode & mode)) {
> - dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
> + if (!udphy->phy_needs_reinit && udphy->status == target_mode)
> return 0;
[Severity: High]
Does this early return prevent the PHY from re-initializing when the Type-C
mux changes between 2-lane and 4-lane DP?
If the Type-C driver changes the mux configuration so hw_mode changes from
UDPHY_MODE_DP_USB to UDPHY_MODE_DP, but only DP is currently requested
(sw_mode == UDPHY_MODE_DP), target_mode will evaluate to UDPHY_MODE_DP in
both cases.
Because target_mode acts as a bitmask, the lane count change in hw_mode is
masked out, causing this check to succeed and entirely skip the required
re-initialization.
[ ... ]
> + if (udphy->status == UDPHY_MODE_NONE) {
> + /* Power up (incl. clocks) */
> ret = rk_udphy_setup(udphy);
> - if (ret)
> + if (ret) {
> + phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
> return ret;
> -
> - if (!udphy->hs && udphy->hw_mode & UDPHY_MODE_USB)
> - rk_udphy_u3_port_disable(udphy, false);
> - udphy->phy_needs_reinit = false;
> -
> - phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
> - } else if (udphy->phy_needs_reinit) {
> - phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
> -
> - rk_udphy_u3_port_disable(udphy, true);
> - udelay(10);
> -
> + }
> + } else if (target_mode == UDPHY_MODE_NONE) {
> + /* Power down (incl. clocks) */
> + rk_udphy_disable(udphy);
> + } else {
> + /* Mode change => re-init */
> ret = rk_udphy_init(udphy);
[Severity: Critical]
Does dynamically re-initializing the PHY here destroy the active DP lane
state and silently break the display link?
When a concurrent mode change occurs, such as the DWC3 controller waking
from runtime suspend and requesting UDPHY_MODE_USB, rk_udphy_init is called
to re-initialize the PHY.
During initialization, rk_udphy_init unconditionally asserts resets and
writes 0 to CMN_DP_LANE_EN_ALL. However, rk_udphy_dp_lane_enable is
exclusively called during rk_udphy_dp_phy_power_on.
Since the DP lanes are never restored after this re-initialization, will
any USB runtime PM transition drop the display link without notifying the
DRM stack?
> if (ret) {
> phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
> return ret;
> }
[Severity: High]
If rk_udphy_init fails, does this leave the software state permanently
desynchronized from the hardware?
If a failure occurs during a mode change, such as a PLL lock timeout,
rk_udphy_init jumps to its assert_resets error label, leaving all PHY
hardware resets asserted.
Since this error path returns without clearing udphy->status, the software
state will believe the previous mode is actively running while the hardware
is dead. If the subsystem retries or reverts to the old mode,
udphy->status == target_mode will erroneously succeed, trapping the PHY
in a broken state.
[ ... ]
> + /* Ensure USB3 support is enabled when supported and requested */
> + if (!udphy->hs && target_mode & UDPHY_MODE_USB)
> + rk_udphy_u3_port_disable(udphy, false);
>
> - udphy->status |= mode;
> + /*
> + * Inform DWC3, that we are done with the reset, so that it can deassert
> + * its PIPE reset line.
> + */
> + phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
[Severity: Medium]
Is it safe to unconditionally send PHY_NOTIFY_POST_RESET to the DWC3 driver
here, even when target_mode == UDPHY_MODE_NONE?
Since this notification is now sent after the power-down branch calls
rk_udphy_disable, the DWC3 driver may deassert its internal reset line
and resume PIPE communication. Doing this when the PHY is completely powered
off and its clocks are disabled could lead to internal controller state
corruption or SErrors.
[ ... ]
> @@ -1091,9 +1078,11 @@ static int rk_udphy_dp_phy_power_on(struct phy *phy)
> int ret;
>
> scoped_guard(mutex, &udphy->mutex) {
> + udphy->sw_mode |= UDPHY_MODE_DP;
> +
> phy_set_bus_width(phy, udphy->dp_lanes);
>
> - ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
> + ret = rk_udphy_update_power_state(udphy);
> if (ret)
> return ret;
[Severity: High]
If rk_udphy_update_power_state fails, does this leak the UDPHY_MODE_DP bit
in sw_mode?
The bit is added before the fallible update call and is never reverted on
error. The PHY framework considers the initialization failed and will never
call phy_power_off to clear the state. Future operations will then
incorrectly include this leaked mode in target_mode.
[ ... ]
> @@ -1329,13 +1320,9 @@ static int rk_udphy_usb3_phy_init(struct phy *phy)
>
> guard(mutex)(&udphy->mutex);
>
> - /* DP only or high-speed, disable U3 port */
> - if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs) {
> - rk_udphy_u3_port_disable(udphy, true);
> - return 0;
> - }
> + udphy->sw_mode |= UDPHY_MODE_USB;
>
> - return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
> + return rk_udphy_update_power_state(udphy);
> }
[Severity: High]
By removing the early return for high-speed only configurations (udphy->hs),
does this force full SuperSpeed initialization unnecessarily?
Without the early return, UDPHY_MODE_USB is unconditionally set in sw_mode.
This forces rk_udphy_update_power_state to power up the PMA/PCS and wait
for the SuperSpeed LCPLL to lock, which wastes power on unconnected or
unsupported hardware and may cause LCPLL lock timeouts.
[Severity: High]
Similar to the DP power on routine, if rk_udphy_update_power_state fails
here, does this leak the UDPHY_MODE_USB bit in sw_mode?
The bit is set before the update call and is never reverted on error,
desynchronizing the driver from the PHY subsystem.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=35
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 36/38] phy: rockchip: usbdp: Rename phy_needs_reinit to orientation_changed
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (34 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 35/38] phy: rockchip: usbdp: Simplify power state handling Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-02 0:08 ` sashiko-bot
2026-07-01 23:36 ` [PATCH v9 37/38] phy: rockchip: usbdp: Re-init PHY on mux change Sebastian Reichel
2026-07-01 23:36 ` [PATCH v9 38/38] phy: rockchip: usbdp: Power optimizations Sebastian Reichel
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
phy_needs_reinit used to signal mode or cable orientation changes,
but mode changes are now tracked separately. Thus it makes sense
to rename the variable to show what it actually signals.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 51aac07ef9fe..735ce1bfac7a 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -174,7 +174,7 @@ struct rk_udphy {
/* PHY status management */
bool flip;
- bool phy_needs_reinit;
+ bool orientation_changed;
u8 hw_mode; /* modes currently supported by hardware */
u8 sw_mode; /* modes currently requested */
u8 status; /* current PHY power state */
@@ -667,7 +667,7 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
if (udphy->flip != flipped) {
dev_dbg(udphy->dev, "cable orientation changed, PHY re-init required.\n");
- udphy->phy_needs_reinit = true;
+ udphy->orientation_changed = true;
}
udphy->flip = flipped;
@@ -1020,7 +1020,7 @@ static int rk_udphy_update_power_state(struct rk_udphy *udphy)
u8 target_mode = udphy->hw_mode & udphy->sw_mode;
int ret;
- if (!udphy->phy_needs_reinit && udphy->status == target_mode)
+ if (!udphy->orientation_changed && udphy->status == target_mode)
return 0;
/*
@@ -1067,7 +1067,7 @@ static int rk_udphy_update_power_state(struct rk_udphy *udphy)
phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
udphy->status = target_mode;
- udphy->phy_needs_reinit = false;
+ udphy->orientation_changed = false;
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 36/38] phy: rockchip: usbdp: Rename phy_needs_reinit to orientation_changed
2026-07-01 23:36 ` [PATCH v9 36/38] phy: rockchip: usbdp: Rename phy_needs_reinit to orientation_changed Sebastian Reichel
@ 2026-07-02 0:08 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-02 0:08 UTC (permalink / raw)
To: Sebastian Reichel
Cc: robh, conor+dt, vkoul, linux-phy, devicetree, neil.armstrong,
olteanv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] rk_udphy_update_power_state() will unexpectedly power up the PHY and leak clock references if called when status and target_mode are both UDPHY_MODE_NONE but orientation_changed is true. This problem wasn't introduced by this patch, but was introduced by a preceding patch in this series.
--
commit b963e04ecd7343df15170db1d1feeff729b0f62c
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Rename phy_needs_reinit to orientation_changed
This commit renames phy_needs_reinit to orientation_changed to better
reflect its purpose, as mode changes are now tracked separately.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -1020,7 +1020,7 @@ static int rk_udphy_update_power_state(struct rk_udphy *udphy)
> u8 target_mode = udphy->hw_mode & udphy->sw_mode;
> int ret;
>
> - if (!udphy->phy_needs_reinit && udphy->status == target_mode)
> + if (!udphy->orientation_changed && udphy->status == target_mode)
> return 0;
>
> /*
[ ... ]
> if (udphy->status == UDPHY_MODE_NONE) {
> /* Power up (incl. clocks) */
> ret = rk_udphy_setup(udphy, target_mode);
[Severity: High]
This is a pre-existing issue, but does this logic leak clock references if
called when both udphy->status and target_mode are UDPHY_MODE_NONE (0)?
If a Type-C cable is attached or the orientation is toggled while the DP
PHY client is powered off and the port negotiates a DP-only connection,
udphy->orientation_changed becomes true.
When rk_udphy_update_power_state() is subsequently called, the early return
at the top of the function is bypassed because udphy->orientation_changed
is true. Since udphy->status is UDPHY_MODE_NONE, the code takes this branch
and calls rk_udphy_setup(udphy, 0), which unconditionally prepares and
enables the clocks.
Because target_mode is 0, udphy->status remains UDPHY_MODE_NONE, meaning
subsequent genuine power-offs will fail to decrement the clock counts via
rk_udphy_disable(). Can this eventually overflow the clock reference counters?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=36
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 37/38] phy: rockchip: usbdp: Re-init PHY on mux change
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (35 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 36/38] phy: rockchip: usbdp: Rename phy_needs_reinit to orientation_changed Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-02 0:12 ` sashiko-bot
2026-07-01 23:36 ` [PATCH v9 38/38] phy: rockchip: usbdp: Power optimizations Sebastian Reichel
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Ensure that the right part of the PHY are powered up when the
mode changes. This ensures the PHY is re-initialized in the
following two scenarios, which are currently broken:
- cable orientation changes without DP being involved
- switching from DP-only into a mode with USB support
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 735ce1bfac7a..d056da53cb1c 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1372,6 +1372,8 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
rk_udphy_set_typec_state(udphy, state->mode);
+ rk_udphy_update_power_state(udphy);
+
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 37/38] phy: rockchip: usbdp: Re-init PHY on mux change
2026-07-01 23:36 ` [PATCH v9 37/38] phy: rockchip: usbdp: Re-init PHY on mux change Sebastian Reichel
@ 2026-07-02 0:12 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-02 0:12 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, vkoul, robh, conor+dt, olteanv, linux-phy,
neil.armstrong
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The return value of `rk_udphy_update_power_state()` is ignored in `rk_udphy_typec_mux_set()`, silently masking hardware initialization failures from the Type-C framework.
- [High] A logic flaw in `rk_udphy_update_power_state()` causes a permanent clock reference leak and PHY state desynchronization if a mux change occurs when no PHY consumers are active.
--
commit 53a5cecf0aaba3a0de5eba24ab8177288b8ff3ea
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Re-init PHY on mux change
Ensure that the right part of the PHY are powered up when the
mode changes. This ensures the PHY is re-initialized when cable
orientation changes or when switching from DP-only to a mode with
USB support.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 735ce1bfac7af..d056da53cb1c8 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -1372,6 +1372,8 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
>
> rk_udphy_set_typec_state(udphy, state->mode);
>
> + rk_udphy_update_power_state(udphy);
> +
[Severity: High]
Is it safe to ignore the return value of rk_udphy_update_power_state() here?
If hardware setup steps like PLL lock timeouts or clock enablement fail, the
error is discarded and the Type-C framework is left unaware. Could this lead
to asynchronous SErrors or kernel panics if the DWC3 controller is accessed
while the PHY is broken or disabled?
[Severity: High]
Does calling this function during a mux change when no PHY consumers
are active lead to a permanent clock reference leak?
If a Type-C mux or orientation change occurs while no PHY consumers are active
(for example, the DP driver isn't bound or USB is in runtime suspend),
udphy->sw_mode is 0, making target_mode 0.
Looking at rk_udphy_update_power_state(), it bypasses the early exit because
udphy->orientation_changed is true. Since udphy->status is 0, it calls
rk_udphy_setup() which increments clock refcounts:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_update_power_state() {
...
if (udphy->status == UDPHY_MODE_NONE) {
/* Power up (incl. clocks) */
ret = rk_udphy_setup(udphy, target_mode);
...
}
It then assigns udphy->status = target_mode (which is 0). Because the status
remains 0 despite clocks being enabled, the next time a consumer activates
the PHY, rk_udphy_setup() is called again, double-incrementing the clock
refcounts.
During teardown, rk_udphy_disable() would only be called once, preventing the
SoC from entering deeper low-power states. Can this logic be adjusted to
properly track when clocks are enabled or avoid enabling them if target_mode
is 0?
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=37
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v9 38/38] phy: rockchip: usbdp: Power optimizations
2026-07-01 23:35 [PATCH v9 00/38] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (36 preceding siblings ...)
2026-07-01 23:36 ` [PATCH v9 37/38] phy: rockchip: usbdp: Re-init PHY on mux change Sebastian Reichel
@ 2026-07-01 23:36 ` Sebastian Reichel
2026-07-02 0:06 ` sashiko-bot
37 siblings, 1 reply; 50+ messages in thread
From: Sebastian Reichel @ 2026-07-01 23:36 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, linux-usb, Sebastian Reichel
Right now the initialization routine enables power for all modes
currently supported by the hardware instead of the combination of
hardware supported mode + software requestes modes. This wastes
some power for some use cases like having a USB-C hub plugged in
without the DP (or HDMI) port being used. Fix this by requesting
power for software requested modes, which are currently supported
by the hardware.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index d056da53cb1c..7f90f666076c 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -771,21 +771,21 @@ static int rk_udphy_status_check(struct rk_udphy *udphy)
return 0;
}
-static int rk_udphy_init(struct rk_udphy *udphy)
+static int rk_udphy_init(struct rk_udphy *udphy, u8 mode)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
int ret;
dev_dbg(udphy->dev, "reinit PHY with USB=%s and DP=%s (%u lanes) flipped=%s\n",
- str_enabled_disabled(udphy->hw_mode & UDPHY_MODE_USB),
- str_enabled_disabled(udphy->hw_mode & UDPHY_MODE_DP),
+ str_enabled_disabled(mode & UDPHY_MODE_USB),
+ str_enabled_disabled(mode & UDPHY_MODE_DP),
udphy->dp_lanes, str_yes_no(udphy->flip));
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
/* enable rx lfps for usb */
- if (udphy->hw_mode & UDPHY_MODE_USB)
+ if (mode & UDPHY_MODE_USB)
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.rx_lfps, true);
/* Step 1: power on pma and deassert apb rstn */
@@ -822,13 +822,13 @@ static int rk_udphy_init(struct rk_udphy *udphy)
FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
/* Step 4: deassert init rstn and wait for 200ns from datasheet */
- if (udphy->hw_mode & UDPHY_MODE_USB) {
+ if (mode & UDPHY_MODE_USB) {
ret = rk_udphy_reset_deassert(udphy, "init");
if (ret)
goto assert_resets;
}
- if (udphy->hw_mode & UDPHY_MODE_DP) {
+ if (mode & UDPHY_MODE_DP) {
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
CMN_DP_INIT_RSTN,
FIELD_PREP(CMN_DP_INIT_RSTN, 0x1));
@@ -837,7 +837,7 @@ static int rk_udphy_init(struct rk_udphy *udphy)
udelay(1);
/* Step 5: deassert cmn/lane rstn */
- if (udphy->hw_mode & UDPHY_MODE_USB) {
+ if (mode & UDPHY_MODE_USB) {
ret = rk_udphy_reset_deassert(udphy, "cmn");
if (ret)
goto assert_resets;
@@ -860,7 +860,7 @@ static int rk_udphy_init(struct rk_udphy *udphy)
return ret;
}
-static int rk_udphy_setup(struct rk_udphy *udphy)
+static int rk_udphy_setup(struct rk_udphy *udphy, u8 mode)
{
int ret;
@@ -872,7 +872,7 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
return ret;
}
- ret = rk_udphy_init(udphy);
+ ret = rk_udphy_init(udphy, mode);
if (ret) {
dev_err(udphy->dev, "failed to init combophy\n");
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
@@ -1039,7 +1039,7 @@ static int rk_udphy_update_power_state(struct rk_udphy *udphy)
if (udphy->status == UDPHY_MODE_NONE) {
/* Power up (incl. clocks) */
- ret = rk_udphy_setup(udphy);
+ ret = rk_udphy_setup(udphy, target_mode);
if (ret) {
phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
return ret;
@@ -1049,7 +1049,7 @@ static int rk_udphy_update_power_state(struct rk_udphy *udphy)
rk_udphy_disable(udphy);
} else {
/* Mode change => re-init */
- ret = rk_udphy_init(udphy);
+ ret = rk_udphy_init(udphy, target_mode);
if (ret) {
phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
return ret;
--
2.53.0
^ permalink raw reply related [flat|nested] 50+ messages in thread* Re: [PATCH v9 38/38] phy: rockchip: usbdp: Power optimizations
2026-07-01 23:36 ` [PATCH v9 38/38] phy: rockchip: usbdp: Power optimizations Sebastian Reichel
@ 2026-07-02 0:06 ` sashiko-bot
0 siblings, 0 replies; 50+ messages in thread
From: sashiko-bot @ 2026-07-02 0:06 UTC (permalink / raw)
To: Sebastian Reichel
Cc: robh, conor+dt, vkoul, linux-phy, neil.armstrong, devicetree,
olteanv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] DP-only PHY initialization will always fail with a timeout because `rk_udphy_status_check()` was not updated to use the new `mode` parameter, causing it to poll uninitialized USB PLLs.
--
commit dfd202b9c15655907ae97e937e85e69b72eb2e33
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Power optimizations
This patch changes the initialization routine to request power only
for software requested modes rather than all hardware supported modes.
This reduces wasted power when certain ports are not in use.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index d056da53cb1c8..7f90f666076c3 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -837,7 +837,7 @@ static int rk_udphy_init(struct rk_udphy *udphy)
> udelay(1);
>
> /* Step 5: deassert cmn/lane rstn */
> - if (udphy->hw_mode & UDPHY_MODE_USB) {
> + if (mode & UDPHY_MODE_USB) {
> ret = rk_udphy_reset_deassert(udphy, "cmn");
> if (ret)
> goto assert_resets;
[Severity: High]
Does this cause DP-only initialization to fail with a timeout?
Because rk_udphy_status_check() wasn't updated to take the new mode
parameter, it still checks udphy->hw_mode & UDPHY_MODE_USB:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_status_check() {
...
if (udphy->hw_mode & UDPHY_MODE_USB) {
ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_LCPLL_DONE_OFFSET,
...
}
If software requests DP-only mode, the USB resets are safely left asserted
here in rk_udphy_init().
However, when rk_udphy_init() later calls rk_udphy_status_check(), it will
still try to poll the uninitialized USB LCPLL because hw_mode still supports
USB. This will inevitably time out after 100ms and cause the entire DP
initialization to abort.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-rockchip-usbdp-cleanup-v9-0-e31efbb62d2e@collabora.com?part=38
^ permalink raw reply [flat|nested] 50+ messages in thread