* [PATCH v10 01/36] dt-bindings: phy: rockchip-usbdp: add improved ports scheme
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 23:42 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 02/36] phy: rockchip: usbdp: Update mode_change after error handling Sebastian Reichel
` (34 subsequent siblings)
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
index 8b7059d5b182..35d6717b9cc9 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
@@ -110,10 +110,34 @@ properties:
port:
$ref: /schemas/graph.yaml#/properties/port
+ depreated: true
description:
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] 47+ messages in thread* Re: [PATCH v10 01/36] dt-bindings: phy: rockchip-usbdp: add improved ports scheme
2026-07-03 18:13 ` [PATCH v10 01/36] dt-bindings: phy: rockchip-usbdp: add improved ports scheme Sebastian Reichel
@ 2026-07-03 23:42 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-03 23:42 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, robh, neil.armstrong, olteanv, vkoul, linux-phy,
conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Typo in the deprecation keyword ('depreated' instead of 'deprecated').
--
commit 80cbcd5498584cd573a979a5b4e236da93d09776
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
dt-bindings: phy: rockchip-usbdp: add improved ports scheme
This commit introduces a proper port scheme to the Rockchip USBDP PHY binding
to support USB-C DP AltMode, replacing an undocumented scheme. It deprecates
the single port node in favor of a ports node containing multiple specific
endpoints. The new design roughly follows the Qualcomm QMP USB4-USB3-DP PHY
controller binding while adding an additional port for the USB-C SBU mux.
> diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
> index 8b7059d5b1826..35d6717b9cc9e 100644
> --- a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
> +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
> @@ -110,10 +110,34 @@ properties:
>
> port:
> $ref: /schemas/graph.yaml#/properties/port
> + depreated: true
^^^^^^^^^
[Severity: Low]
This isn't a bug, but there is a typo here ("depreated" instead of
"deprecated").
> description:
> A port node to link the PHY to a TypeC controller for the purpose of
> handling orientation switching.
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=1
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 02/36] phy: rockchip: usbdp: Update mode_change after error handling
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 01/36] dt-bindings: phy: rockchip-usbdp: add improved ports scheme Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 03/36] phy: rockchip: usbdp: Do not lose USB3 PHY status Sebastian Reichel
` (33 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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] 47+ messages in thread* [PATCH v10 03/36] phy: rockchip: usbdp: Do not lose USB3 PHY status
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 01/36] dt-bindings: phy: rockchip-usbdp: add improved ports scheme Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 02/36] phy: rockchip: usbdp: Update mode_change after error handling Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 04/36] phy: rockchip: usbdp: Fix devm_clk_bulk_get_all check Sebastian Reichel
` (32 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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] 47+ messages in thread* [PATCH v10 04/36] phy: rockchip: usbdp: Fix devm_clk_bulk_get_all check
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (2 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 03/36] phy: rockchip: usbdp: Do not lose USB3 PHY status Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 05/36] phy: rockchip: usbdp: Handle missing clock-names DT property gracefully Sebastian Reichel
` (31 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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] 47+ messages in thread* [PATCH v10 05/36] phy: rockchip: usbdp: Handle missing clock-names DT property gracefully
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (3 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 04/36] phy: rockchip: usbdp: Fix devm_clk_bulk_get_all check Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 06/36] phy: rockchip: usbdp: Drop seamless DP takeover Sebastian Reichel
` (30 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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] 47+ messages in thread* [PATCH v10 06/36] phy: rockchip: usbdp: Drop seamless DP takeover
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (4 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 05/36] phy: rockchip: usbdp: Handle missing clock-names DT property gracefully Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 07/36] phy: rockchip: usbdp: Keep clocks running on PHY re-init Sebastian Reichel
` (29 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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.
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 | 31 -------------------------------
1 file changed, 31 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 3fc8222fcaec..6cb9f6b4dbf6 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -452,11 +452,6 @@ static int rk_udphy_reset_assert_all(struct rk_udphy *udphy)
return reset_control_bulk_assert(udphy->num_rsts, udphy->rsts);
}
-static int rk_udphy_reset_deassert_all(struct rk_udphy *udphy)
-{
- return reset_control_bulk_deassert(udphy->num_rsts, udphy->rsts);
-}
-
static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name)
{
struct reset_control_bulk_data *list = udphy->rsts;
@@ -924,28 +919,6 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
return 0;
}
-static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
-{
- int ret;
- u32 value;
-
- ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
- if (ret) {
- dev_err(udphy->dev, "failed to enable clk\n");
- return ret;
- }
-
- 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);
-
- return 0;
-}
-
static int rk_udphy_parse_dt(struct rk_udphy *udphy)
{
struct device *dev = udphy->dev;
@@ -1495,10 +1468,6 @@ static int rk_udphy_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = rk_udphy_get_initial_status(udphy);
- if (ret)
- return ret;
-
mutex_init(&udphy->mutex);
platform_set_drvdata(pdev, udphy);
--
2.53.0
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH v10 07/36] phy: rockchip: usbdp: Keep clocks running on PHY re-init
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (5 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 06/36] phy: rockchip: usbdp: Drop seamless DP takeover Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 08/36] phy: rockchip: usbdp: Amend SSC modulation deviation Sebastian Reichel
` (28 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 6cb9f6b4dbf6..e3f5a26c876a 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -987,8 +987,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] 47+ messages in thread* [PATCH v10 08/36] phy: rockchip: usbdp: Amend SSC modulation deviation
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (6 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 07/36] phy: rockchip: usbdp: Keep clocks running on PHY re-init Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 23:44 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 09/36] phy: rockchip: usbdp: Fix LFPS detect threshold control Sebastian Reichel
` (27 subsequent siblings)
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 e3f5a26c876a..cb6acadfbccf 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] 47+ messages in thread* Re: [PATCH v10 08/36] phy: rockchip: usbdp: Amend SSC modulation deviation
2026-07-03 18:13 ` [PATCH v10 08/36] phy: rockchip: usbdp: Amend SSC modulation deviation Sebastian Reichel
@ 2026-07-03 23:44 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-03 23:44 UTC (permalink / raw)
To: Sebastian Reichel
Cc: neil.armstrong, conor+dt, devicetree, olteanv, linux-phy, vkoul,
robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Medium] Partial state update during the configuration verification phase leaves inconsistent driver state on error.
--
commit 90f0c5bc1613dc3c61ed76de74f75f85b147f759
Author: Frank Wang <frank.wang@rock-chips.com>
phy: rockchip: usbdp: Amend SSC modulation deviation
Move SSC modulation deviation into private config of clock.
- 24M: 0x00d4[5:0] = 0x30
- 26M: 0x00d4[5:0] = 0x33
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index e3f5a26c876a5..cb6acadfbccfb 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
While reviewing this, I noticed a pre-existing issue in this file:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_dp_phy_configure() {
...
if (dp->set_rate) {
ret = rk_udphy_dp_phy_verify_link_rate(udphy, dp);
if (ret)
return ret;
}
if (dp->set_lanes) {
ret = rk_udphy_dp_phy_verify_lanes(udphy, dp);
if (ret)
return ret;
}
if (dp->set_voltages) {
ret = rk_udphy_dp_phy_verify_voltages(udphy, dp);
if (ret)
return ret;
}
...
}
[Severity: Medium]
This isn't a bug introduced by this patch, but does partial state update
during the configuration verification phase leave inconsistent driver state
on error?
The helpers rk_udphy_dp_phy_verify_link_rate() and
rk_udphy_dp_phy_verify_lanes() prematurely mutate the driver's internal
state before guaranteeing overall configuration success.
If a caller invokes phy_configure() with set_rate=1 and set_voltages=1,
the verify_link_rate check might pass and immediately overwrite
udphy->link_rate.
If the verify_voltages check then fails, the function aborts without
programming the hardware or restoring the old link_rate.
Could a subsequent call setting only voltages then use the corrupted
link_rate to configure the PHY clock inversion registers?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=8
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 09/36] phy: rockchip: usbdp: Fix LFPS detect threshold control
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (7 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 08/36] phy: rockchip: usbdp: Amend SSC modulation deviation Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 10/36] phy: rockchip: usbdp: Add missing mode_change update Sebastian Reichel
` (26 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 cb6acadfbccf..982315535caf 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] 47+ messages in thread* [PATCH v10 10/36] phy: rockchip: usbdp: Add missing mode_change update
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (8 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 09/36] phy: rockchip: usbdp: Fix LFPS detect threshold control Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 11/36] phy: rockchip: usbdp: Support single-lane DP Sebastian Reichel
` (25 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 982315535caf..7540c897dde1 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -617,6 +617,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) {
@@ -647,7 +656,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,
@@ -1361,10 +1370,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] 47+ messages in thread* [PATCH v10 11/36] phy: rockchip: usbdp: Support single-lane DP
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (9 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 10/36] phy: rockchip: usbdp: Add missing mode_change update Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 12/36] phy: rockchip: usbdp: Limit DP lane count to muxed lanes Sebastian Reichel
` (24 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 7540c897dde1..c4d62b234ea8 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;
@@ -535,6 +536,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)
@@ -542,18 +550,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:
@@ -566,28 +574,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;
@@ -657,6 +643,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,
@@ -895,7 +882,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");
@@ -921,9 +908,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;
@@ -1050,18 +1039,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);
@@ -1341,6 +1329,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:
@@ -1357,6 +1346,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;
}
@@ -1501,7 +1491,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] 47+ messages in thread* [PATCH v10 12/36] phy: rockchip: usbdp: Limit DP lane count to muxed lanes
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (10 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 11/36] phy: rockchip: usbdp: Support single-lane DP Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 13/36] phy: rockchip: usbdp: Rename DP lane functions Sebastian Reichel
` (23 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 c4d62b234ea8..b172ce6a722e 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1099,6 +1099,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] 47+ messages in thread* [PATCH v10 13/36] phy: rockchip: usbdp: Rename DP lane functions
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (11 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 12/36] phy: rockchip: usbdp: Limit DP lane count to muxed lanes Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 23:46 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 14/36] phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST Sebastian Reichel
` (22 subsequent siblings)
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 b172ce6a722e..7fb864a1486c 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -545,7 +545,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;
@@ -574,7 +574,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;
@@ -1049,9 +1049,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);
@@ -1069,7 +1069,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] 47+ messages in thread* Re: [PATCH v10 13/36] phy: rockchip: usbdp: Rename DP lane functions
2026-07-03 18:13 ` [PATCH v10 13/36] phy: rockchip: usbdp: Rename DP lane functions Sebastian Reichel
@ 2026-07-03 23:46 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-03 23:46 UTC (permalink / raw)
To: Sebastian Reichel
Cc: olteanv, vkoul, robh, neil.armstrong, conor+dt, devicetree,
linux-phy
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Failure during PHY initialization destroys active PHY hardware state while leaving software state out of sync.
--
commit 27edcd7e836a7f62ad5d0b336e4f8cfd4fedc67c
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Rename DP lane functions
The patch updates the two DisplayPort lane functions to follow the common
rk_udphy_dp_ prefix scheme for consistency.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index b172ce6a722ee..7fb864a1486c0 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -1069,7 +1069,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);
[Severity: High]
This isn't a bug introduced by this patch, but looking at the driver, does
failure during PHY initialization destroy active PHY hardware state while
leaving the software state out of sync?
If rk_udphy_init() fails during a mode change (e.g., PLL lock timeout when a
DP cable is plugged in), it aborts, asserts all PHY resets, and returns an
error:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_init() {
...
assert_resets:
rk_udphy_reset_assert_all(udphy);
return ret;
}
When this happens, rk_udphy_update_power_state() receives the error, notifies
DWC3 that the reset is complete, and returns early without clearing
udphy->status:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_update_power_state() {
...
ret = rk_udphy_init(udphy);
if (ret) {
phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
return ret;
}
...
}
Will this cause the DWC3 USB controller to access a physically dead (reset)
PHY and crash the system with a synchronous external abort (SError), since
the controller is explicitly told the PHY is ready and the software driver is
left believing it is fully functional?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=13
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 14/36] phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (12 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 13/36] phy: rockchip: usbdp: Rename DP lane functions Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 15/36] phy: rockchip: usbdp: Cleanup DP lane selection function Sebastian Reichel
` (21 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 7fb864a1486c..05593e98c16d 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] 47+ messages in thread* [PATCH v10 15/36] phy: rockchip: usbdp: Cleanup DP lane selection function
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (13 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 14/36] phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 16/36] phy: rockchip: usbdp: Register DP aux bridge Sebastian Reichel
` (20 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 05593e98c16d..eda3f7a1e267 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -548,30 +548,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] 47+ messages in thread* [PATCH v10 16/36] phy: rockchip: usbdp: Register DP aux bridge
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (14 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 15/36] phy: rockchip: usbdp: Cleanup DP lane selection function Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 17/36] phy: rockchip: usbdp: Drop DP HPD handling Sebastian Reichel
` (19 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 eda3f7a1e267..8ac6a83b0b2a 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>
@@ -1414,6 +1415,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;
@@ -1468,6 +1470,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] 47+ messages in thread* [PATCH v10 17/36] phy: rockchip: usbdp: Drop DP HPD handling
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (15 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 16/36] phy: rockchip: usbdp: Register DP aux bridge Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 18/36] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit Sebastian Reichel
` (18 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 | 85 ++++---------------------------
1 file changed, 9 insertions(+), 76 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 8ac6a83b0b2a..24108816e3b9 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -128,7 +128,6 @@ struct rk_udphy_grf_cfg {
struct rk_udphy_vogrf_cfg {
/* vo-grf */
- struct rk_udphy_grf_reg hpd_trigger;
u32 dp_lane_reg;
};
@@ -186,14 +185,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 */
@@ -577,19 +573,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)
@@ -1000,29 +983,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);
@@ -1254,8 +1214,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,
@@ -1309,6 +1267,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) {
@@ -1340,22 +1306,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;
@@ -1510,20 +1461,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"
};
@@ -1547,7 +1484,6 @@ static const struct rk_udphy_cfg rk3576_udphy_cfgs = {
},
.vogrfcfg = {
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
},
@@ -1588,11 +1524,9 @@ static const struct rk_udphy_cfg rk3588_udphy_cfgs = {
},
.vogrfcfg = {
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0008, 11, 10, 1, 3),
.dp_lane_reg = 0x0008,
},
},
@@ -1628,7 +1562,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] 47+ messages in thread* [PATCH v10 18/36] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (16 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 17/36] phy: rockchip: usbdp: Drop DP HPD handling Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 19/36] phy: rockchip: usbdp: Re-init the PHY on orientation change Sebastian Reichel
` (17 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 24108816e3b9..e44d19c9d119 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -171,7 +171,7 @@ struct rk_udphy {
/* PHY status management */
bool flip;
- bool mode_change;
+ bool phy_needs_reinit;
u8 mode;
u8 status;
@@ -578,7 +578,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;
}
@@ -951,15 +951,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] 47+ messages in thread* [PATCH v10 19/36] phy: rockchip: usbdp: Re-init the PHY on orientation change
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (17 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 18/36] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 23:51 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 20/36] phy: rockchip: usbdp: Factor out lane_mux_sel setup Sebastian Reichel
` (16 subsequent siblings)
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 e44d19c9d119..4b454798c4ed 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -620,6 +620,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);
@@ -631,7 +632,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] 47+ messages in thread* Re: [PATCH v10 19/36] phy: rockchip: usbdp: Re-init the PHY on orientation change
2026-07-03 18:13 ` [PATCH v10 19/36] phy: rockchip: usbdp: Re-init the PHY on orientation change Sebastian Reichel
@ 2026-07-03 23:51 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-03 23:51 UTC (permalink / raw)
To: Sebastian Reichel
Cc: linux-phy, robh, devicetree, vkoul, conor+dt, olteanv,
neil.armstrong
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The patch defers PHY hardware re-initialization during Type-C orientation changes by setting a `phy_needs_reinit` flag, assuming a subsequent call to `rk_udphy_update_power_state()` will apply the change. However, for USB3-only connections, this subsequent call is often never made, resulting in broken USB3 functionality.
--
commit af712ecc489de5c647a8e305c7beca4b1c6dd5cb
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Re-init the PHY on orientation change
This patch updates the rockchip usbdp PHY driver to track orientation
changes by checking if the cable orientation is reversed. If the orientation
changes, it flags the PHY as needing re-initialization so that the lane
muxing can be reconfigured.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index e44d19c9d1193..4b454798c4ed4 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -620,6 +620,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);
>
> @@ -631,7 +632,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;
> +
[Severity: High]
Does this deferral of PHY initialization break USB3-only connections?
By only setting the phy_needs_reinit flag here, the actual hardware update
appears to be deferred until rk_udphy_update_power_state() is called to
consume it.
For USB3-only connections (especially on fixed-role ports or boards without a
mode-switch property), is rk_udphy_update_power_state() guaranteed to be
called after this orientation change?
If it isn't called, it seems a reverse-plugged USB3 device might fail to
connect because the reverse lane routing is never written to the hardware.
Additionally, if a DisplayPort device (which does trigger a power state
update) is plugged in reverse and then replaced with a normal USB3 device,
could the software state reset to normal while the hardware remains stuck
in reverse?
> + udphy->flip = flipped;
> rk_udphy_set_typec_default_mapping(udphy);
> rk_udphy_usb_bvalid_enable(udphy, true);
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=19
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 20/36] phy: rockchip: usbdp: Factor out lane_mux_sel setup
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (18 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 19/36] phy: rockchip: usbdp: Re-init the PHY on orientation change Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 23:51 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 21/36] phy: rockchip: usbdp: Properly handle TYPEC_STATE_SAFE and TYPEC_STATE_USB Sebastian Reichel
` (15 subsequent siblings)
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 4b454798c4ed..4e54020d1755 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -582,6 +582,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) {
@@ -589,10 +625,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);
@@ -602,18 +634,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,
@@ -1269,7 +1297,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
@@ -1281,38 +1308,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] 47+ messages in thread* Re: [PATCH v10 20/36] phy: rockchip: usbdp: Factor out lane_mux_sel setup
2026-07-03 18:13 ` [PATCH v10 20/36] phy: rockchip: usbdp: Factor out lane_mux_sel setup Sebastian Reichel
@ 2026-07-03 23:51 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-03 23:51 UTC (permalink / raw)
To: Sebastian Reichel
Cc: conor+dt, vkoul, robh, olteanv, neil.armstrong, linux-phy,
devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Overwriting udphy->dp_lanes in rk_udphy_set_typec_state destroys the hardware lane limit, leading to uninitialized array access.
--
commit b5a0ae84556b392b692f17a4e792243cb346f579
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Factor out lane_mux_sel setup
This commit factors out the Type-C state logic into a new helper function
rk_udphy_set_typec_state() to reduce code duplication when configuring the
USB+DP lane_mux_sel.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 4b454798c4ed4..4e54020d17551 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -582,6 +582,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;
^^^^^^^^^^^^^^^^^^^
[Severity: High]
This is a pre-existing issue, but does overwriting udphy->dp_lanes here
destroy the hardware lane limit?
The variable udphy->dp_lanes seems to be overloaded. It is initially
populated from the device tree to represent the physical hardware lane
limit (e.g., 2 lanes). However, it is unconditionally overwritten here
to represent the requested Type-C state.
If a 2-lane PHY receives a 4-lane Type-C request, udphy->dp_lanes becomes 4.
Does this defeat the hardware limit check if (dp->lanes > udphy->dp_lanes)
in rk_udphy_dp_phy_verify_lanes() (added in a subsequent patch in this
series)?
If the hardware limit check is defeated, the DP controller might configure
4 lanes, causing rk_udphy_dp_phy_configure() to iterate 4 times and access
uninitialized elements in the dp_lane_sel array:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_dp_phy_configure()
...
if (dp->set_voltages) {
for (i = 0; i < udphy->lanes; i++) {
lane = udphy->dp_lane_sel[i];
...
Since dp_lane_sel[2] and dp_lane_sel[3] would be 0 (due to devm_kzalloc),
could this result in lane 0 being misconfigured multiple times, overriding
its intended settings and corrupting the USB3 state?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=20
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 21/36] phy: rockchip: usbdp: Properly handle TYPEC_STATE_SAFE and TYPEC_STATE_USB
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (19 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 20/36] phy: rockchip: usbdp: Factor out lane_mux_sel setup Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 22/36] phy: rockchip: usbdp: Use guard functions for mutex Sebastian Reichel
` (14 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 4e54020d1755..0399cbf96e19 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1293,17 +1293,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] 47+ messages in thread* [PATCH v10 22/36] phy: rockchip: usbdp: Use guard functions for mutex
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (20 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 21/36] phy: rockchip: usbdp: Properly handle TYPEC_STATE_SAFE and TYPEC_STATE_USB Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 23/36] phy: rockchip: usbdp: Clear USB status on PHY exit Sebastian Reichel
` (13 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 0399cbf96e19..8c165bcab796 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>
@@ -650,14 +651,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)
@@ -667,8 +669,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;
}
@@ -1020,26 +1020,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;
}
@@ -1047,10 +1046,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;
}
@@ -1255,35 +1254,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;
}
@@ -1315,12 +1309,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] 47+ messages in thread* [PATCH v10 23/36] phy: rockchip: usbdp: Clear USB status on PHY exit
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (21 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 22/36] phy: rockchip: usbdp: Use guard functions for mutex Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 23:49 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 24/36] phy: rockchip: usbdp: Hold mutex in DP PHY configure Sebastian Reichel
` (12 subsequent siblings)
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 8c165bcab796..a742bde7155b 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1273,8 +1273,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] 47+ messages in thread* Re: [PATCH v10 23/36] phy: rockchip: usbdp: Clear USB status on PHY exit
2026-07-03 18:13 ` [PATCH v10 23/36] phy: rockchip: usbdp: Clear USB status on PHY exit Sebastian Reichel
@ 2026-07-03 23:49 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-03 23:49 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, vkoul, robh, neil.armstrong, linux-phy, olteanv,
conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Clearing `udphy->status` directly without checking if the PHY hardware should be disabled causes an unbounded clock reference count leak.
--
commit e836c3d9b92996113cb8593f93bf48152ed5de6c
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Clear USB status on PHY exit
This commit ensures the USB status flag is cleared when the USB3 PHY is
exited while the system is in DP-only mode. This happens if the USB3
controller device is unbound while a DP-only adapter is plugged in.
Closes: https://sashiko.dev/#/message/20260625-rockchip-usbdp-cleanup-v7-24-38eb3cf654fd%40collabora.com
> 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
> @@ -1273,8 +1273,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);
[Severity: High]
Does clearing udphy->status without actually disabling the hardware cause an
unbounded clock reference count leak?
When the system switches to DP-only mode, udphy->mode loses the
UDPHY_MODE_USB bit. If the USB controller is then unbound, we hit this path
and clear UDPHY_MODE_USB from udphy->status, but return early. The hardware
clocks remain enabled since we bypass the hardware disable call.
If the system then switches back to USB mode, rk_udphy_usb3_phy_init() will
see udphy->status as UDPHY_MODE_NONE and call rk_udphy_setup(). This calls
clk_bulk_prepare_enable() again, incrementing the clock refcount indefinitely
on every cycle.
I see this issue is addressed later in the series by commit
e0219fd5f4d784e78ff969af266e10196367be69 ("phy: rockchip: usbdp: Simplify
power state handling"), but doesn't this patch introduce a regression here
that breaks bisectability?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=23
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 24/36] phy: rockchip: usbdp: Hold mutex in DP PHY configure
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (22 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 23/36] phy: rockchip: usbdp: Clear USB status on PHY exit Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 25/36] phy: rockchip: usbdp: Add some extra debug messages Sebastian Reichel
` (11 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 a742bde7155b..cd79c5da566a 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1154,6 +1154,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] 47+ messages in thread* [PATCH v10 25/36] phy: rockchip: usbdp: Add some extra debug messages
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (23 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 24/36] phy: rockchip: usbdp: Hold mutex in DP PHY configure Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 26/36] phy: rockchip: usbdp: Avoid xHCI SErrors Sebastian Reichel
` (10 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 cd79c5da566a..edee27933d89 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>
@@ -462,6 +463,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;
}
@@ -488,6 +491,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);
}
@@ -662,8 +667,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);
@@ -781,6 +788,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 USB3=%s and DP=%s (%u lanes) flipped=%s\n",
+ str_on_off(udphy->mode & UDPHY_MODE_USB),
+ str_on_off(udphy->mode & UDPHY_MODE_DP),
+ udphy->dp_lanes, str_yes_no(udphy->flip));
+
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
@@ -851,6 +863,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");
@@ -869,6 +883,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);
}
@@ -1310,8 +1325,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] 47+ messages in thread* [PATCH v10 26/36] phy: rockchip: usbdp: Avoid xHCI SErrors
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (24 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 25/36] phy: rockchip: usbdp: Add some extra debug messages Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 27/36] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert errors Sebastian Reichel
` (9 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 edee27933d89..9e77513643a3 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1000,8 +1000,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] 47+ messages in thread* [PATCH v10 27/36] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert errors
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (25 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 26/36] phy: rockchip: usbdp: Avoid xHCI SErrors Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 28/36] phy: rockchip: usbdp: Only enable USB3 when not in high-speed mode Sebastian Reichel
` (8 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 9e77513643a3..c645fea67c4d 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -803,8 +803,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,
@@ -830,8 +834,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,
@@ -843,8 +850,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] 47+ messages in thread* [PATCH v10 28/36] phy: rockchip: usbdp: Only enable USB3 when not in high-speed mode
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (26 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 27/36] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert errors Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 29/36] phy: core: add notifier infrastructure Sebastian Reichel
` (7 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 c645fea67c4d..694688ed387f 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1009,7 +1009,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] 47+ messages in thread* [PATCH v10 29/36] phy: core: add notifier infrastructure
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (27 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 28/36] phy: rockchip: usbdp: Only enable USB3 when not in high-speed mode Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 30/36] usb: dwc3: core: support PHY reset notifications Sebastian Reichel
` (6 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 | 60 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/phy/phy.h | 40 +++++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 21aaf2f76e53..92c93d684ab0 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -542,6 +542,65 @@ 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)
+{
+ int ret;
+
+ if (!phy)
+ return 0;
+
+ ret = blocking_notifier_call_chain(&phy->notifier, event, phy);
+ return notifier_to_errno(ret);
+}
+EXPORT_SYMBOL_GPL(phy_notify_reset);
+
/**
* phy_configure() - Changes the phy parameters
* @phy: the phy returned by phy_get()
@@ -1018,6 +1077,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] 47+ messages in thread* [PATCH v10 30/36] usb: dwc3: core: support PHY reset notifications
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (28 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 29/36] phy: core: add notifier infrastructure Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 23:59 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 31/36] phy: rockchip: usbdp: Add phy reset notification support Sebastian Reichel
` (5 subsequent siblings)
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/usb/dwc3/core.h | 16 +++++++++++
2 files changed, 90 insertions(+)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 517aa7f1486d..899decbd0da0 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,78 @@ 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_phy_nb *pnb = container_of(nb, struct dwc3_phy_nb, nb);
+ struct dwc3 *dwc = pnb->dwc;
+ int port = pnb->port_index;
+ unsigned long flags;
+ u32 reg;
+
+ switch (action) {
+ case PHY_NOTIFY_PRE_RESET:
+ pm_runtime_get_sync(dwc->dev);
+
+ /*
+ * 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.
+ */
+ spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(port));
+ reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+
+ case PHY_NOTIFY_POST_RESET:
+ /*
+ * Deassert PHY soft reset to reconnect the PIPE interface
+ * after PHY reinitialization.
+ */
+ spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(port));
+ reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ pm_runtime_put_autosuspend(dwc->dev);
+ 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;
+ dwc->usb3_phy_nb[i].port_index = i;
+ 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..8250d96262ed 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1002,6 +1002,20 @@ 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
+ * @port_index: USB3 port index this notifier is registered for
+ */
+struct dwc3_phy_nb {
+ struct notifier_block nb;
+ struct dwc3 *dwc;
+ u8 port_index;
+};
+
/**
* struct dwc3 - representation of our controller
* @drd_work: workqueue used for role swapping
@@ -1065,6 +1079,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
@@ -1229,6 +1244,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;
--
2.53.0
^ permalink raw reply related [flat|nested] 47+ messages in thread* Re: [PATCH v10 30/36] usb: dwc3: core: support PHY reset notifications
2026-07-03 18:13 ` [PATCH v10 30/36] usb: dwc3: core: support PHY reset notifications Sebastian Reichel
@ 2026-07-03 23:59 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-03 23:59 UTC (permalink / raw)
To: Sebastian Reichel
Cc: olteanv, conor+dt, robh, vkoul, neil.armstrong, linux-phy,
devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Missing error handling for pm_runtime_get_sync() in dwc3_usb3_phy_notify() can lead to hardware access on a suspended device and PM usage count leak.
- [High] AB-BA deadlock between PHY notifier rwsem and Runtime PM state lock during DWC3 runtime suspend.
- [High] Concurrent read-modify-write on DWC3_GUSB3PIPECTL can silently clear the newly added PHYSOFTRST bit.
- [Medium] The PM runtime usage counter is permanently leaked if the DWC3 driver unbinds mid-reset.
--
commit 7addd6da511b4b59c9d034fe7f0878d5304dda87
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
usb: dwc3: core: support PHY reset notifications
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.
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index 517aa7f1486da..899decbd0da04 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
[ ... ]
> @@ -880,10 +886,78 @@ 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_phy_nb *pnb = container_of(nb, struct dwc3_phy_nb, nb);
> + struct dwc3 *dwc = pnb->dwc;
> + int port = pnb->port_index;
> + unsigned long flags;
> + u32 reg;
> +
> + switch (action) {
> + case PHY_NOTIFY_PRE_RESET:
> + pm_runtime_get_sync(dwc->dev);
[Severity: High]
Does this code handle the case where pm_runtime_get_sync() fails?
If pm_runtime_get_sync() returns a negative error code (failing to resume the
device), it still increments the PM usage counter. Since the return value is
ignored, the code will proceed to unconditionally access DWC3 hardware
registers via dwc3_readl() and dwc3_writel(). Accessing registers on a
suspended, clock-gated device can trigger a synchronous external abort
(SError) or bus fault, causing a kernel panic.
Should this error path balance the usage counter via pm_runtime_put_noidle()
and return an error to prevent the leak and crash?
[Severity: High]
Can this pm_runtime_get_sync() call cause a deadlock during runtime suspend?
When a PHY reset notification arrives, the blocking_notifier_call_chain()
holds phy->notifier.rwsem (read lock), and dwc3_usb3_phy_notify() calls
pm_runtime_get_sync().
If DWC3 is concurrently runtime-suspending (in DEVICE mode, the state is
RPM_SUSPENDING), pm_runtime_get_sync() blocks waiting for the suspend to
finish.
The suspend thread calls dwc3_suspend_common() -> dwc3_core_exit() ->
dwc3_phy_unregister_notifiers(), which attempts to acquire
phy->notifier.rwsem (write lock) via blocking_notifier_chain_unregister():
dwc3_phy_unregister_notifiers()
phy_unregister_notifier()
blocking_notifier_chain_unregister() -> blocks on write lock
The two threads would mutually block, causing a strict AB-BA system deadlock.
> +
> + /*
> + * 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.
> + */
> + spin_lock_irqsave(&dwc->lock, flags);
> + reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(port));
> + reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
> + dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
> + spin_unlock_irqrestore(&dwc->lock, flags);
[Severity: High]
Can the PHYSOFTRST bit be silently cleared by concurrent register accesses?
While dwc3_usb3_phy_notify() modifies DWC3_GUSB3PIPECTL holding dwc->lock,
the existing dwc3_enable_susphy() function also modifies this register using a
read-modify-write sequence, but without holding dwc->lock:
dwc3_enable_susphy() {
...
reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i));
if (enable && !dwc->dis_u3_susphy_quirk)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
else
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg);
...
}
If dwc3_enable_susphy() executes concurrently (e.g., triggered via
__dwc3_set_mode() during a role switch when the PHY reset is also triggered),
it can read the register before PHYSOFTRST is set and write it back after,
inadvertently clearing the bit.
> + break;
> +
> + case PHY_NOTIFY_POST_RESET:
[ ... ]
> + break;
> + }
> +
> + return NOTIFY_OK;
> +}
[ ... ]
> +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);
> +}
[Severity: Medium]
Can this cause a PM runtime usage counter leak if the driver unbinds mid-reset?
PHY_NOTIFY_PRE_RESET increments the PM usage counter via pm_runtime_get_sync().
If the driver is unbound (dwc3_remove() -> dwc3_core_remove() ->
dwc3_core_exit()) before PHY_NOTIFY_POST_RESET is sent,
dwc3_phy_unregister_notifiers() cleanly unregisters the notifier.
Because the notifier is unregistered, the DWC3 driver never receives the
POST_RESET event, skipping the corresponding pm_runtime_put_autosuspend(). The
PM usage counter would remain unbalanced, preventing the device instance from
ever suspending if rebound.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=30
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 31/36] phy: rockchip: usbdp: Add phy reset notification support
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (29 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 30/36] usb: dwc3: core: support PHY reset notifications Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 32/36] phy: rockchip: usbdp: Drop -EPROBE_DEFER hack Sebastian Reichel
` (4 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 694688ed387f..5d151fff9eeb 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1005,6 +1005,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;
@@ -1012,13 +1017,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] 47+ messages in thread* [PATCH v10 32/36] phy: rockchip: usbdp: Drop -EPROBE_DEFER hack
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (30 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 31/36] phy: rockchip: usbdp: Add phy reset notification support Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-04 0:12 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 33/36] phy: rockchip: usbdp: Rename mode to hw_mode Sebastian Reichel
` (3 subsequent siblings)
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 hack to return -EPROBE_DEFER when the lcpll lock timeouts is no
longer needed. The driver now does a reset during its probe routine
marking everything as off and later on does a re-init, which avoids
the problem.
Apart from that rk_udphy_status_check() is called after the probe,
so it should not return -EPROBE_DEFER.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 5d151fff9eeb..3fb6eea2979b 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -750,17 +750,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) {
--
2.53.0
^ permalink raw reply related [flat|nested] 47+ messages in thread* Re: [PATCH v10 32/36] phy: rockchip: usbdp: Drop -EPROBE_DEFER hack
2026-07-03 18:13 ` [PATCH v10 32/36] phy: rockchip: usbdp: Drop -EPROBE_DEFER hack Sebastian Reichel
@ 2026-07-04 0:12 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-04 0:12 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, robh, olteanv, vkoul, neil.armstrong, linux-phy,
conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The commit message incorrectly claims that the driver performs a reset during its probe routine, which can lead to a permanent DWC3 probe failure when initialized by U-Boot.
--
commit 6fe2e9f6ad72e10da5478abbe129b4525d89b28f
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Drop -EPROBE_DEFER hack
This commit removes the -EPROBE_DEFER fallback from rk_udphy_status_check().
It relies on a hardware reset in the probe routine to avoid the PLL locking
timeout.
> The hack to return -EPROBE_DEFER when the lcpll lock timeouts is no
> longer needed. The driver now does a reset during its probe routine
> marking everything as off and later on does a re-init, which avoids
> the problem.
[Severity: High]
Is this description accurate? Looking at rk_udphy_probe(), the hardware reset
appears to be missing.
If the bootloader (U-Boot) initializes and leaves the USB PHY active, and
rk_udphy_probe() fails to assert the hardware resets, the PHY remains active.
When DWC3 later probes and calls phy_init(), it attempts to re-initialize the
PHY.
Because DWC3 registers its PHY notifier after calling phy_init(), it misses
the PHY_NOTIFY_PRE_RESET event and fails to soft-reset the USB MAC. The
active DWC3 MAC can interfere with the PHY's PLL lock, causing the
rk_udphy_status_check() to timeout.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 5d151fff9eeba..3fb6eea2979b9 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -750,17 +750,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;
> }
[Severity: High]
Does propagating this timeout instead of returning -EPROBE_DEFER cause the
DWC3 probe to fail permanently rather than retrying?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=32
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 33/36] phy: rockchip: usbdp: Rename mode to hw_mode
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (31 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 32/36] phy: rockchip: usbdp: Drop -EPROBE_DEFER hack Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 34/36] phy: rockchip: usbdp: Simplify power state handling Sebastian Reichel
` (2 subsequent siblings)
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 3fb6eea2979b..e2cd72643a7d 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;
- u8 mode;
+ u8 hw_mode; /* modes currently supported by hardware */
u8 status;
/* utilized for USB */
@@ -579,18 +579,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:
@@ -599,7 +599,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;
@@ -616,12 +616,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)
@@ -744,7 +744,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);
@@ -779,15 +779,15 @@ static int rk_udphy_init(struct rk_udphy *udphy)
int ret;
dev_dbg(udphy->dev, "reinit PHY with USB3=%s and DP=%s (%u lanes) flipped=%s\n",
- str_on_off(udphy->mode & UDPHY_MODE_USB),
- str_on_off(udphy->mode & UDPHY_MODE_DP),
+ str_on_off(udphy->hw_mode & UDPHY_MODE_USB),
+ str_on_off(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 */
@@ -824,13 +824,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));
@@ -839,7 +839,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;
@@ -898,7 +898,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;
}
@@ -927,10 +927,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);
}
@@ -989,7 +989,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;
}
@@ -1004,7 +1004,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;
@@ -1033,7 +1033,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;
}
@@ -1292,7 +1292,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;
}
@@ -1307,7 +1307,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] 47+ messages in thread* [PATCH v10 34/36] phy: rockchip: usbdp: Simplify power state handling
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (32 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 33/36] phy: rockchip: usbdp: Rename mode to hw_mode Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 23:58 ` sashiko-bot
2026-07-03 18:13 ` [PATCH v10 35/36] phy: rockchip: usbdp: Re-init PHY on mux change Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 36/36] phy: rockchip: usbdp: Add USB-C state without DP enabled Sebastian Reichel
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 | 142 ++++++++++++++++++------------
1 file changed, 84 insertions(+), 58 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index e2cd72643a7d..ca9418fab8f3 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -173,9 +173,10 @@ struct rk_udphy {
/* PHY status management */
bool flip;
- bool phy_needs_reinit;
+ bool phy_needs_reinit; /* lane mux changed */
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 */
@@ -985,66 +986,84 @@ 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;
int ret;
- if (!(udphy->hw_mode & mode)) {
- dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
+ /*
+ * Initialize PHY mode according to the hardware setup (either described
+ * in DT or negotiated via the Type-C controller) instead of requesting
+ * only the needed PHY side, because that would break the USB/DP data
+ * streams when the other PHY is being requested. This is not an issue
+ * during the Type-C negotiation as that happens during the hotplug phase
+ * and not during normal operation. Also disable everything if the
+ * software has not requested anything, as there shouldn't be any active
+ * data streams in that case.
+ */
+ target_mode = udphy->hw_mode;
+ if (udphy->sw_mode == UDPHY_MODE_NONE)
+ target_mode = UDPHY_MODE_NONE;
+
+ 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);
+ /* Avoid to re-init disabled PHY */
+ if (udphy->status == target_mode && target_mode == UDPHY_MODE_NONE)
+ return 0;
- rk_udphy_u3_port_disable(udphy, true);
- udelay(10);
+ /*
+ * 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.
+ * This is intentionally done for a PHY disable, since that also changes
+ * the clocks routed to the PHY.
+ */
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
+
+ /*
+ * 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 */
+ 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. This is sent in pair with a PRE_RESET allowing
+ * consumer driver to do paired resource requests (e.g. clocks) in their
+ * notification handlers. As we reroute the clocks, its also fine to
+ * send this after completely disabling the PHY.
+ */
+ 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)
@@ -1053,11 +1072,15 @@ 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);
- if (ret)
+ ret = rk_udphy_update_power_state(udphy);
+ if (ret) {
+ udphy->sw_mode &= ~UDPHY_MODE_DP;
return ret;
+ }
rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
@@ -1080,10 +1103,10 @@ static int rk_udphy_dp_phy_power_off(struct phy *phy)
guard(mutex)(&udphy->mutex);
- rk_udphy_dp_lane_enable(udphy, 0);
- rk_udphy_power_off(udphy, UDPHY_MODE_DP);
+ udphy->sw_mode &= ~UDPHY_MODE_DP;
- return 0;
+ rk_udphy_dp_lane_enable(udphy, 0);
+ return rk_udphy_update_power_state(udphy);
}
/*
@@ -1288,16 +1311,22 @@ 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;
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);
+ if (udphy->hs)
return 0;
+
+ udphy->sw_mode |= UDPHY_MODE_USB;
+
+ ret = rk_udphy_update_power_state(udphy);
+ if (ret) {
+ udphy->sw_mode &= ~UDPHY_MODE_USB;
+ return ret;
}
- return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
+ return 0;
}
static int rk_udphy_usb3_phy_exit(struct phy *phy)
@@ -1306,15 +1335,12 @@ 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;
+ if (udphy->hs)
return 0;
- }
- rk_udphy_power_off(udphy, UDPHY_MODE_USB);
+ udphy->sw_mode &= ~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] 47+ messages in thread* Re: [PATCH v10 34/36] phy: rockchip: usbdp: Simplify power state handling
2026-07-03 18:13 ` [PATCH v10 34/36] phy: rockchip: usbdp: Simplify power state handling Sebastian Reichel
@ 2026-07-03 23:58 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-03 23:58 UTC (permalink / raw)
To: Sebastian Reichel
Cc: olteanv, vkoul, neil.armstrong, linux-phy, robh, conor+dt,
devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing USB3 port disable in high-speed mode causes DWC3 SErrors on probe.
--
commit e0219fd5f4d784e78ff969af266e10196367be69
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Simplify power state handling
This commit simplifies power state handling by introducing a sw_mode field in
addition to the hw_mode field. This allows the PHY to track hardware-supported
modes, software-requested modes, and the actual hardware status.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index e2cd72643a7d7..ca9418fab8f3a 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -1288,16 +1311,22 @@ 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;
>
> 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);
> + if (udphy->hs)
> return 0;
[Severity: High]
Does returning early here without disabling the U3 port cause DWC3 SErrors
during probe in high-speed mode?
Previously, this path explicitly called rk_udphy_u3_port_disable(udphy, true)
before returning. By returning early now, we completely bypass
rk_udphy_update_power_state(), which leaves the U3 port enabled. When the
DWC3 controller is probed, this can leave it clocked by the unpowered PHY
instead of the safe UTMI fallback clock, resulting in an SError.
> - }
>
> - return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
> + udphy->sw_mode |= UDPHY_MODE_USB;
> +
> + ret = rk_udphy_update_power_state(udphy);
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=34
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v10 35/36] phy: rockchip: usbdp: Re-init PHY on mux change
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (33 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 34/36] phy: rockchip: usbdp: Simplify power state handling Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-03 18:13 ` [PATCH v10 36/36] phy: rockchip: usbdp: Add USB-C state without DP enabled Sebastian Reichel
35 siblings, 0 replies; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index ca9418fab8f3..6eacbea6839a 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1379,7 +1379,7 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
rk_udphy_set_typec_state(udphy, state->mode);
- return 0;
+ return rk_udphy_update_power_state(udphy);
}
static void rk_udphy_typec_mux_unregister(void *data)
--
2.53.0
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH v10 36/36] phy: rockchip: usbdp: Add USB-C state without DP enabled
2026-07-03 18:13 [PATCH v10 00/36] phy: rockchip: usbdp: Clean up the mess Sebastian Reichel
` (34 preceding siblings ...)
2026-07-03 18:13 ` [PATCH v10 35/36] phy: rockchip: usbdp: Re-init PHY on mux change Sebastian Reichel
@ 2026-07-03 18:13 ` Sebastian Reichel
2026-07-04 0:01 ` sashiko-bot
35 siblings, 1 reply; 47+ messages in thread
From: Sebastian Reichel @ 2026-07-03 18:13 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thinh Nguyen,
Greg Kroah-Hartman, Philipp Zabel
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 driver currently only differs between 4 lanes DP mode or combined DP
+ USB3 mode. This makes sense from a lane routing point of view, as the
hardware only has 2 lanes of USB3.
But adding a separate state for USB-only helps with power management,
since we always power up all PHY parts according to the current hardware
setup to avoid data stream interruptions. Even if some lanes are muxed
to the DP controller there is no need to keep the DP side enabled if
something without DP AltMode is plugged into USB-C.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 58 ++++++++++++++++++-------------
1 file changed, 34 insertions(+), 24 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 6eacbea6839a..fcf9b3de7ba8 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -580,32 +580,14 @@ 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)
+static void rk_udphy_set_lane_mux(struct rk_udphy *udphy)
{
- 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;
-
- switch (state) {
- case TYPEC_DP_STATE_C:
- case TYPEC_DP_STATE_E:
+ if (udphy->dp_lanes == 4) {
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;
- hw_mode = UDPHY_MODE_DP;
- udphy->dp_lanes = 4;
- break;
-
- case TYPEC_DP_STATE_D:
- default:
+ } else {
if (udphy->flip) {
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
@@ -617,12 +599,40 @@ 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;
}
- hw_mode = UDPHY_MODE_DP_USB;
- udphy->dp_lanes = 2;
+ }
+}
+
+static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 hw_mode, u8 dp_lanes)
+{
+ if (udphy->hw_mode == hw_mode && udphy->dp_lanes == dp_lanes)
+ return;
+
+ udphy->phy_needs_reinit = true;
+ udphy->hw_mode = hw_mode;
+ udphy->dp_lanes = dp_lanes;
+
+ rk_udphy_set_lane_mux(udphy);
+}
+
+static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
+{
+ switch (state) {
+ case TYPEC_DP_STATE_C:
+ case TYPEC_DP_STATE_E:
+ rk_udphy_mode_set(udphy, UDPHY_MODE_DP, 4);
+ break;
+
+ case TYPEC_DP_STATE_D:
+ rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB, 2);
+ break;
+
+ case TYPEC_STATE_SAFE:
+ case TYPEC_STATE_USB:
+ default:
+ rk_udphy_mode_set(udphy, UDPHY_MODE_USB, 0);
break;
}
- rk_udphy_mode_set(udphy, hw_mode);
}
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
--
2.53.0
^ permalink raw reply related [flat|nested] 47+ messages in thread* Re: [PATCH v10 36/36] phy: rockchip: usbdp: Add USB-C state without DP enabled
2026-07-03 18:13 ` [PATCH v10 36/36] phy: rockchip: usbdp: Add USB-C state without DP enabled Sebastian Reichel
@ 2026-07-04 0:01 ` sashiko-bot
0 siblings, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-07-04 0:01 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, robh, vkoul, neil.armstrong, olteanv, linux-phy,
conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Early return in `rk_udphy_mode_set` prevents `lane_mux_sel` from being updated when only the cable orientation changes.
- [Medium] Mapping `TYPEC_STATE_USB` to `UDPHY_MODE_USB` causes PHY resets and USB disconnections during DP Alt Mode entry.
Pre-existing issues:
- [High] Devres registration ordering causes a Use-After-Free of `phy_u3` during unbind.
--
commit 054870f51259caaa299758adc71548fa9bf71159
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Add USB-C state without DP enabled
This commit adds a separate state for USB-only mode without DisplayPort enabled
to improve power management. It splits the previous DP + USB3 combined mode
when DP AltMode is not present.
> 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
> @@ -617,12 +599,40 @@ 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;
> }
> - hw_mode = UDPHY_MODE_DP_USB;
> - udphy->dp_lanes = 2;
> + }
> +}
> +
> +static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 hw_mode, u8 dp_lanes)
> +{
> + if (udphy->hw_mode == hw_mode && udphy->dp_lanes == dp_lanes)
> + return;
[Severity: High]
Does this early return prevent updating the lane mux if only the cable
orientation changes?
If a cable is physically flipped, the code updates udphy->flip and calls
rk_udphy_set_typec_default_mapping(), which then sets the state to
TYPEC_DP_STATE_D.
If the PHY was already in UDPHY_MODE_DP_USB with 2 DP lanes, this check
causes an early return, skipping the newly moved rk_udphy_set_lane_mux()
call below. This would leave stale inverted lane assignments when
rk_udphy_init() subsequently configures the hardware.
> +
> + udphy->phy_needs_reinit = true;
> + udphy->hw_mode = hw_mode;
> + udphy->dp_lanes = dp_lanes;
> +
> + rk_udphy_set_lane_mux(udphy);
> +}
> +
> +static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
> +{
> + switch (state) {
> [ ... ]
> + case TYPEC_STATE_SAFE:
> + case TYPEC_STATE_USB:
> + default:
> + rk_udphy_mode_set(udphy, UDPHY_MODE_USB, 0);
> + break;
[Severity: Medium]
Will mapping this intermediate state to USB-only force a disruptive hardware
mode change when transitioning to DP Alt Mode?
Type-C port managers typically request TYPEC_STATE_USB upon connection.
Setting this to UDPHY_MODE_USB configures 0 DP lanes.
When DP Alt Mode successfully negotiates shortly after and transitions to
TYPEC_DP_STATE_D, hw_mode changes to UDPHY_MODE_DP_USB. This triggers a
full PHY reset in rk_udphy_update_power_state() and interrupts active
USB transfers.
The commit message mentions keeping all PHY parts powered up to avoid data
stream interruptions, but could this intermediate state mapping negate that
benefit during initial connection?
> }
>
> }
[Severity: High]
This is a pre-existing issue, but does the devres registration ordering in
rk_udphy_probe() cause a use-after-free of phy_u3 during unbind?
The Type-C orientation switch and mux are registered with devres before
the PHYs are created:
rk_udphy_probe() {
...
if (device_property_present(dev, "mode-switch")) {
ret = rk_udphy_setup_typec_mux(udphy);
...
udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops);
...
}
During unbind, devres executes cleanup in reverse order. The PHY structures
are freed while the Type-C mux remains registered.
If a concurrent Type-C event arrives and calls rk_udphy_typec_mux_set(),
it invokes rk_udphy_update_power_state() which uses the freed pointer:
rk_udphy_update_power_state() {
...
phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
...
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-rockchip-usbdp-cleanup-v10-0-a392711ca8a9@collabora.com?part=36
^ permalink raw reply [flat|nested] 47+ messages in thread