* [PATCH v4 0/5] phy: rockchip: inno-usb2: ensure PHY is running when clock is requested
@ 2026-11-10 0:02 Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 1/5] phy: rockchip: inno-usb2: simplify and unify PHY op debug messages Sebastian Reichel
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Sebastian Reichel @ 2026-11-10 0:02 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Heiko Stuebner,
Philipp Zabel, William Wu
Cc: linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Igor Paunovic, kernel, Sebastian Reichel
This was noticed on RK3588 EVB1 when resuming from system suspend. This
is technically a fix, but its unclear when the bug was introduced and
system suspend is broken on RK3588 for quite a while and not just due
to this problem. So I think this fix can be merged the normal way via
linux-next.
Changes in v4:
- Link to v3: https://patch.msgid.link/20261105-phy-rockchip-inno-usb2-clock-fix-v3-0-b20608c08b94@collabora.com
- Drop patch avoiding rport->suspended in rockchip_usb2phy_sm_work; it
was meant as a simple cleanup and Sashiko suggested it is more tricky
- Add new patch removing error handling for regmap_write and
regmap_write_bits
- Add new patch removing error handling for reset controller
assert/deassert
- Simplify final two patches due to the dropped error handling
- In the final patch move the 480M clock registration after the
port_cfg population to properly fix the race condition instead
of relying on -EAGAIN and the NULL check
- In the final patch create a new helper function deciding between
handling the PHY suspend in the clock (un)prepare functions; we
keep the NULL check for port_cfg as this properly handles the
case of the port sub-node being disabled in DT. From functional
point of view this setup doesn't make sense (no need to enable
the PHY if all ports are disabled), but one extra NULL check is
cheap, so it's better to handle this gracefully.
Changes in v3:
- Link to v2: https://patch.msgid.link/20261001-phy-rockchip-inno-usb2-clock-fix-v2-0-e01bf509359e@collabora.com
- Fix do_suspend negation resulting in SError on RK3576 (Sashiko, CI testing)
- Ensure PHY ports array is initialized in clock enable path (Sashiko)
- Cleanup properly in clock enable error path (Sashiko)
Changes in v2:
- Link to v1: https://patch.msgid.link/20260908-phy-rockchip-inno-usb2-clock-fix-v1-1-f7d59c31b908@collabora.com
- Move suspend exit code from rockchip_usb2phy_power_on into a
separate patch and reuse it in the clock prepare function;
move is done in a separate patch
- Updated commit messages with proper rationale
- Added one more patch dropping useless assignment
of rport->suspended directly after calling
rockchip_usb2phy_power_on/off
- Add one more patch unifying and simplifying the debug
messages for phy init/power_on/power_off/exit
---
To: Vinod Koul <vkoul@kernel.org>
To: Neil Armstrong <neil.armstrong@linaro.org>
To: Manivannan Sadhasivam <mani@kernel.org>
To: Heiko Stuebner <heiko@sntech.de>
To: Philipp Zabel <p.zabel@pengutronix.de>
To: William Wu <william.wu@rock-chips.com>
Cc: linux-phy@lists.infradead.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-rockchip@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: Igor Paunovic <royalnet026@gmail.com>
Cc: kernel@collabora.com
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
Sebastian Reichel (5):
phy: rockchip: inno-usb2: simplify and unify PHY op debug messages
phy: rockchip: inno-usb2: ignore regmap_write return codes
phy: rockchip: inno-usb2: ignore reset_control return codes
phy: rockchip: inno-usb2: move suspend handling into new function
phy: rockchip: inno-usb2: move suspend handling into clock prepare/unprepare for RK3588/RK3576
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 257 ++++++++++----------------
1 file changed, 96 insertions(+), 161 deletions(-)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20260908-phy-rockchip-inno-usb2-clock-fix-edd65b57f884
Best regards,
--
Sebastian Reichel <sebastian.reichel@collabora.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 1/5] phy: rockchip: inno-usb2: simplify and unify PHY op debug messages
2026-11-10 0:02 [PATCH v4 0/5] phy: rockchip: inno-usb2: ensure PHY is running when clock is requested Sebastian Reichel
@ 2026-11-10 0:02 ` Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 2/5] phy: rockchip: inno-usb2: ignore regmap_write return codes Sebastian Reichel
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Sebastian Reichel @ 2026-11-10 0:02 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Heiko Stuebner,
Philipp Zabel, William Wu
Cc: linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Igor Paunovic, kernel, Sebastian Reichel
Add debug message for init/exit in addition to power_on/power_off and
avoid the useless extra dereference in the existing power_on/power_off
to make the prints consistent.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index e47b89015586..0b61f152eb3f 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -508,6 +508,8 @@ static int rockchip_usb2phy_init(struct phy *phy)
struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
int ret = 0;
+ dev_dbg(&phy->dev, "phy port init\n");
+
mutex_lock(&rport->mutex);
if (rport->port_id == USB2PHY_PORT_OTG) {
@@ -592,7 +594,7 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
int ret;
- dev_dbg(&rport->phy->dev, "port power on\n");
+ dev_dbg(&phy->dev, "port power on\n");
if (!rport->suspended)
return 0;
@@ -632,7 +634,7 @@ static int rockchip_usb2phy_power_off(struct phy *phy)
struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
int ret;
- dev_dbg(&rport->phy->dev, "port power off\n");
+ dev_dbg(&phy->dev, "port power off\n");
if (rport->suspended)
return 0;
@@ -651,6 +653,8 @@ static int rockchip_usb2phy_exit(struct phy *phy)
{
struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+ dev_dbg(&phy->dev, "phy port exit\n");
+
if (rport->port_id == USB2PHY_PORT_OTG &&
rport->mode != USB_DR_MODE_HOST &&
rport->mode != USB_DR_MODE_UNKNOWN) {
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 2/5] phy: rockchip: inno-usb2: ignore regmap_write return codes
2026-11-10 0:02 [PATCH v4 0/5] phy: rockchip: inno-usb2: ensure PHY is running when clock is requested Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 1/5] phy: rockchip: inno-usb2: simplify and unify PHY op debug messages Sebastian Reichel
@ 2026-11-10 0:02 ` Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 3/5] phy: rockchip: inno-usb2: ignore reset_control " Sebastian Reichel
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Sebastian Reichel @ 2026-11-10 0:02 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Heiko Stuebner,
Philipp Zabel, William Wu
Cc: linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Igor Paunovic, kernel, Sebastian Reichel
This driver only uses MMIO regmaps, which means regmap_write and
regmap_write_bits() cannot fail (writing to unpowered/unclocked
registers would result in an SError instead of a regmap_write failure).
Thus drop the useless error handling for better code readability.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 161 +++++++-------------------
1 file changed, 43 insertions(+), 118 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 0b61f152eb3f..a705c4b18bd4 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -263,8 +263,8 @@ struct rockchip_usb2phy {
struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS];
};
-static inline int property_enable(struct regmap *base,
- const struct usb2phy_reg *reg, bool en)
+static inline void property_enable(struct regmap *base,
+ const struct usb2phy_reg *reg, bool en)
{
unsigned int val, mask, tmp;
@@ -272,7 +272,7 @@ static inline int property_enable(struct regmap *base,
mask = GENMASK(reg->bitend, reg->bitstart);
val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
- return regmap_write(base, reg->offset, val);
+ regmap_write(base, reg->offset, val);
}
static inline bool property_enabled(struct regmap *base,
@@ -336,15 +336,12 @@ static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
{
const struct usb2phy_reg *clkout_ctl;
struct regmap *base;
- int ret;
rockchip_usb2phy_clk480m_clkout_ctl(hw, &base, &clkout_ctl);
/* turn on 480m clk output if it is off */
if (!property_enabled(base, clkout_ctl)) {
- ret = property_enable(base, clkout_ctl, true);
- if (ret)
- return ret;
+ property_enable(base, clkout_ctl, true);
/* waiting for the clk become stable */
usleep_range(1200, 1300);
@@ -481,25 +478,14 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
return 0;
}
-static int rockchip_usb2phy_enable_host_disc_irq(struct rockchip_usb2phy *rphy,
- struct rockchip_usb2phy_port *rport,
- bool en)
+static void rockchip_usb2phy_enable_host_disc_irq(struct rockchip_usb2phy *rphy,
+ struct rockchip_usb2phy_port *rport,
+ bool en)
{
- int ret;
-
- ret = property_enable(rphy->grf, &rport->port_cfg->disfall_clr, true);
- if (ret)
- return ret;
-
- ret = property_enable(rphy->grf, &rport->port_cfg->disfall_en, en);
- if (ret)
- return ret;
-
- ret = property_enable(rphy->grf, &rport->port_cfg->disrise_clr, true);
- if (ret)
- return ret;
-
- return property_enable(rphy->grf, &rport->port_cfg->disrise_en, en);
+ property_enable(rphy->grf, &rport->port_cfg->disfall_clr, true);
+ property_enable(rphy->grf, &rport->port_cfg->disfall_en, en);
+ property_enable(rphy->grf, &rport->port_cfg->disrise_clr, true);
+ property_enable(rphy->grf, &rport->port_cfg->disrise_en, en);
}
static int rockchip_usb2phy_init(struct phy *phy)
@@ -516,42 +502,14 @@ static int rockchip_usb2phy_init(struct phy *phy)
if (rport->mode != USB_DR_MODE_HOST &&
rport->mode != USB_DR_MODE_UNKNOWN) {
/* clear bvalid status and enable bvalid detect irq */
- ret = property_enable(rphy->grf,
- &rport->port_cfg->bvalid_det_clr,
- true);
- if (ret)
- goto out;
-
- ret = property_enable(rphy->grf,
- &rport->port_cfg->bvalid_det_en,
- true);
- if (ret)
- goto out;
+ property_enable(rphy->grf, &rport->port_cfg->bvalid_det_clr, true);
+ property_enable(rphy->grf, &rport->port_cfg->bvalid_det_en, true);
/* clear id status and enable id detect irqs */
- ret = property_enable(rphy->grf,
- &rport->port_cfg->idfall_det_clr,
- true);
- if (ret)
- goto out;
-
- ret = property_enable(rphy->grf,
- &rport->port_cfg->idrise_det_clr,
- true);
- if (ret)
- goto out;
-
- ret = property_enable(rphy->grf,
- &rport->port_cfg->idfall_det_en,
- true);
- if (ret)
- goto out;
-
- ret = property_enable(rphy->grf,
- &rport->port_cfg->idrise_det_en,
- true);
- if (ret)
- goto out;
+ property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr, true);
+ property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr, true);
+ property_enable(rphy->grf, &rport->port_cfg->idfall_det_en, true);
+ property_enable(rphy->grf, &rport->port_cfg->idrise_det_en, true);
schedule_delayed_work(&rport->otg_sm_work,
OTG_SCHEDULE_DELAY * 3);
@@ -562,28 +520,16 @@ static int rockchip_usb2phy_init(struct phy *phy)
} else if (rport->port_id == USB2PHY_PORT_HOST) {
if (rport->port_cfg->disfall_en.offset) {
rport->host_disconnect = true;
- ret = rockchip_usb2phy_enable_host_disc_irq(rphy, rport, true);
- if (ret) {
- dev_err(rphy->dev, "failed to enable disconnect irq\n");
- goto out;
- }
+ rockchip_usb2phy_enable_host_disc_irq(rphy, rport, true);
}
/* clear linestate and enable linestate detect irq */
- ret = property_enable(rphy->grf,
- &rport->port_cfg->ls_det_clr, true);
- if (ret)
- goto out;
-
- ret = property_enable(rphy->grf,
- &rport->port_cfg->ls_det_en, true);
- if (ret)
- goto out;
+ property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true);
+ property_enable(rphy->grf, &rport->port_cfg->ls_det_en, true);
schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
}
-out:
mutex_unlock(&rport->mutex);
return ret;
}
@@ -603,11 +549,7 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
if (ret)
return ret;
- ret = property_enable(rphy->grf, &rport->port_cfg->phy_sus, false);
- if (ret) {
- clk_disable_unprepare(rphy->clk480m);
- return ret;
- }
+ property_enable(rphy->grf, &rport->port_cfg->phy_sus, false);
/*
* For rk3588, it needs to reset phy when exit from
@@ -632,16 +574,13 @@ static int rockchip_usb2phy_power_off(struct phy *phy)
{
struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
- int ret;
dev_dbg(&phy->dev, "port power off\n");
if (rport->suspended)
return 0;
- ret = property_enable(rphy->grf, &rport->port_cfg->phy_sus, true);
- if (ret)
- return ret;
+ property_enable(rphy->grf, &rport->port_cfg->phy_sus, true);
rport->suspended = true;
clk_disable_unprepare(rphy->clk480m);
@@ -1511,39 +1450,29 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy)
{
/* Turn off differential receiver in suspend mode */
- return regmap_write_bits(rphy->grf, 0x298,
- BIT(2) << BIT_WRITEABLE_SHIFT | BIT(2),
- BIT(2) << BIT_WRITEABLE_SHIFT | 0);
+ regmap_write_bits(rphy->grf, 0x298,
+ BIT(2) << BIT_WRITEABLE_SHIFT | BIT(2),
+ BIT(2) << BIT_WRITEABLE_SHIFT | 0);
+
+ return 0;
}
static int rk3528_usb2phy_tuning(struct rockchip_usb2phy *rphy)
{
- int ret;
-
/* Turn off otg port differential receiver in suspend mode */
- ret = regmap_write(rphy->phy_base, 0x30, BIT(18) | 0x0000);
- if (ret)
- return ret;
+ regmap_write(rphy->phy_base, 0x30, BIT(18) | 0x0000);
/* Turn off host port differential receiver in suspend mode */
- ret = regmap_write(rphy->phy_base, 0x430, BIT(18) | 0x0000);
- if (ret)
- return ret;
+ regmap_write(rphy->phy_base, 0x430, BIT(18) | 0x0000);
/* Set otg port HS eye height to 400mv (default is 450mv) */
- ret = regmap_write(rphy->phy_base, 0x30, GENMASK(22, 20) | 0x0000);
- if (ret)
- return ret;
+ regmap_write(rphy->phy_base, 0x30, GENMASK(22, 20) | 0x0000);
/* Set host port HS eye height to 400mv (default is 450mv) */
- ret = regmap_write(rphy->phy_base, 0x430, GENMASK(22, 20) | 0x0000);
- if (ret)
- return ret;
+ regmap_write(rphy->phy_base, 0x430, GENMASK(22, 20) | 0x0000);
/* Choose the Tx fs/ls data as linestate from TX driver for otg port */
- ret = regmap_write(rphy->phy_base, 0x94, GENMASK(22, 19) | 0x0018);
- if (ret)
- return ret;
+ regmap_write(rphy->phy_base, 0x94, GENMASK(22, 19) | 0x0018);
return 0;
}
@@ -1554,9 +1483,7 @@ static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy)
u32 reg = rphy->phy_cfg->reg;
/* Deassert SIDDQ to power on analog block */
- ret = regmap_write(rphy->grf, reg + 0x0010, GENMASK(29, 29) | 0x0000);
- if (ret)
- return ret;
+ regmap_write(rphy->grf, reg + 0x0010, GENMASK(29, 29) | 0x0000);
/* Do reset after exit IDDQ mode */
ret = rockchip_usb2phy_reset(rphy);
@@ -1564,12 +1491,12 @@ static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy)
return ret;
/* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */
- ret |= regmap_write(rphy->grf, reg + 0x000c, GENMASK(27, 24) | 0x0900);
+ regmap_write(rphy->grf, reg + 0x000c, GENMASK(27, 24) | 0x0900);
/* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */
- ret |= regmap_write(rphy->grf, reg + 0x0010, GENMASK(20, 19) | 0x0010);
+ regmap_write(rphy->grf, reg + 0x0010, GENMASK(20, 19) | 0x0010);
- return ret;
+ return 0;
}
static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy)
@@ -1594,9 +1521,7 @@ static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy)
}
/* Deassert SIDDQ to power on analog block */
- ret = regmap_write(rphy->grf, 0x0008, GENMASK(29, 29) | 0x0000);
- if (ret)
- return ret;
+ regmap_write(rphy->grf, 0x0008, GENMASK(29, 29) | 0x0000);
/* Do reset after exit IDDQ mode */
ret = rockchip_usb2phy_reset(rphy);
@@ -1604,21 +1529,21 @@ static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy)
return ret;
/* suspend configuration */
- ret |= regmap_write(rphy->grf, 0x000c, GENMASK(20, 16) | suspend_cfg);
+ regmap_write(rphy->grf, 0x000c, GENMASK(20, 16) | suspend_cfg);
/* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */
- ret |= regmap_write(rphy->grf, 0x0004, GENMASK(27, 24) | 0x0900);
+ regmap_write(rphy->grf, 0x0004, GENMASK(27, 24) | 0x0900);
/* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */
- ret |= regmap_write(rphy->grf, 0x0008, GENMASK(20, 19) | 0x0010);
+ regmap_write(rphy->grf, 0x0008, GENMASK(20, 19) | 0x0010);
if (!usb3otg)
- return ret;
+ return 0;
/* Pullup iddig pin for USB3_0 OTG mode */
- ret |= regmap_write(rphy->grf, 0x0010, GENMASK(17, 16) | 0x0003);
+ regmap_write(rphy->grf, 0x0010, GENMASK(17, 16) | 0x0003);
- return ret;
+ return 0;
}
static const struct rockchip_usb2phy_cfg rk3036_phy_cfgs[] = {
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 3/5] phy: rockchip: inno-usb2: ignore reset_control return codes
2026-11-10 0:02 [PATCH v4 0/5] phy: rockchip: inno-usb2: ensure PHY is running when clock is requested Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 1/5] phy: rockchip: inno-usb2: simplify and unify PHY op debug messages Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 2/5] phy: rockchip: inno-usb2: ignore regmap_write return codes Sebastian Reichel
@ 2026-11-10 0:02 ` Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 4/5] phy: rockchip: inno-usb2: move suspend handling into new function Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 5/5] phy: rockchip: inno-usb2: move suspend handling into clock prepare/unprepare for RK3588/RK3576 Sebastian Reichel
4 siblings, 0 replies; 7+ messages in thread
From: Sebastian Reichel @ 2026-11-10 0:02 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Heiko Stuebner,
Philipp Zabel, William Wu
Cc: linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Igor Paunovic, kernel, Sebastian Reichel
Asserting or deasserting reset lines for the Rockchip MMIO based reset
controller never fails. Drop error handling for it from the driver to
simplify the logic.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 31 ++++++---------------------
1 file changed, 6 insertions(+), 25 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index a705c4b18bd4..6b3104901695 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -290,23 +290,12 @@ static inline bool property_enabled(struct regmap *base,
return tmp != reg->disable;
}
-static int rockchip_usb2phy_reset(struct rockchip_usb2phy *rphy)
+static void rockchip_usb2phy_reset(struct rockchip_usb2phy *rphy)
{
- int ret;
-
- ret = reset_control_assert(rphy->phy_reset);
- if (ret)
- return ret;
-
+ reset_control_assert(rphy->phy_reset);
udelay(10);
-
- ret = reset_control_deassert(rphy->phy_reset);
- if (ret)
- return ret;
-
+ reset_control_deassert(rphy->phy_reset);
usleep_range(100, 200);
-
- return 0;
}
static void rockchip_usb2phy_clk_bulk_disable(void *data)
@@ -559,9 +548,7 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
* please keep the common_on_n 1'b0 to set these blocks
* remain powered.
*/
- ret = rockchip_usb2phy_reset(rphy);
- if (ret)
- return ret;
+ rockchip_usb2phy_reset(rphy);
/* waiting for the utmi_clk to become stable */
usleep_range(1500, 2000);
@@ -1479,16 +1466,13 @@ static int rk3528_usb2phy_tuning(struct rockchip_usb2phy *rphy)
static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy)
{
- int ret;
u32 reg = rphy->phy_cfg->reg;
/* Deassert SIDDQ to power on analog block */
regmap_write(rphy->grf, reg + 0x0010, GENMASK(29, 29) | 0x0000);
/* Do reset after exit IDDQ mode */
- ret = rockchip_usb2phy_reset(rphy);
- if (ret)
- return ret;
+ rockchip_usb2phy_reset(rphy);
/* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */
regmap_write(rphy->grf, reg + 0x000c, GENMASK(27, 24) | 0x0900);
@@ -1501,7 +1485,6 @@ static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy)
static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy)
{
- int ret;
bool usb3otg = false;
/*
* utmi_termselect = 1'b1 (en FS terminations)
@@ -1524,9 +1507,7 @@ static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy)
regmap_write(rphy->grf, 0x0008, GENMASK(29, 29) | 0x0000);
/* Do reset after exit IDDQ mode */
- ret = rockchip_usb2phy_reset(rphy);
- if (ret)
- return ret;
+ rockchip_usb2phy_reset(rphy);
/* suspend configuration */
regmap_write(rphy->grf, 0x000c, GENMASK(20, 16) | suspend_cfg);
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 4/5] phy: rockchip: inno-usb2: move suspend handling into new function
2026-11-10 0:02 [PATCH v4 0/5] phy: rockchip: inno-usb2: ensure PHY is running when clock is requested Sebastian Reichel
` (2 preceding siblings ...)
2026-11-10 0:02 ` [PATCH v4 3/5] phy: rockchip: inno-usb2: ignore reset_control " Sebastian Reichel
@ 2026-11-10 0:02 ` Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 5/5] phy: rockchip: inno-usb2: move suspend handling into clock prepare/unprepare for RK3588/RK3576 Sebastian Reichel
4 siblings, 0 replies; 7+ messages in thread
From: Sebastian Reichel @ 2026-11-10 0:02 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Heiko Stuebner,
Philipp Zabel, William Wu
Cc: linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Igor Paunovic, kernel, Sebastian Reichel
Move handling of the PHY suspend handling into its own dedicated
function. No functional changes intended.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 38 ++++++++++++++++-----------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 6b3104901695..642c996d23de 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -321,6 +321,27 @@ rockchip_usb2phy_clk480m_clkout_ctl(struct clk_hw *hw, struct regmap **base,
}
}
+static void rockchip_usb2phy_set_suspend(struct rockchip_usb2phy *rphy,
+ struct rockchip_usb2phy_port *rport,
+ bool do_suspend)
+{
+ property_enable(rphy->grf, &rport->port_cfg->phy_sus, do_suspend);
+
+ if (!do_suspend) {
+ /*
+ * For rk3588, it needs to reset phy when exit from suspend
+ * mode with common_on_n 1'b1(aka REFCLK_LOGIC, Bias, and PLL
+ * blocks are powered down) for lower power consumption. If you
+ * don't want to reset phy, please keep the common_on_n 1'b0 to
+ * set these blocks remain powered.
+ */
+ rockchip_usb2phy_reset(rphy);
+
+ /* waiting for the utmi_clk to become stable */
+ usleep_range(1500, 2000);
+ }
+}
+
static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
{
const struct usb2phy_reg *clkout_ctl;
@@ -538,20 +559,7 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
if (ret)
return ret;
- property_enable(rphy->grf, &rport->port_cfg->phy_sus, false);
-
- /*
- * For rk3588, it needs to reset phy when exit from
- * suspend mode with common_on_n 1'b1(aka REFCLK_LOGIC,
- * Bias, and PLL blocks are powered down) for lower
- * power consumption. If you don't want to reset phy,
- * please keep the common_on_n 1'b0 to set these blocks
- * remain powered.
- */
- rockchip_usb2phy_reset(rphy);
-
- /* waiting for the utmi_clk to become stable */
- usleep_range(1500, 2000);
+ rockchip_usb2phy_set_suspend(rphy, rport, false);
rport->suspended = false;
return 0;
@@ -567,7 +575,7 @@ static int rockchip_usb2phy_power_off(struct phy *phy)
if (rport->suspended)
return 0;
- property_enable(rphy->grf, &rport->port_cfg->phy_sus, true);
+ rockchip_usb2phy_set_suspend(rphy, rport, true);
rport->suspended = true;
clk_disable_unprepare(rphy->clk480m);
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 5/5] phy: rockchip: inno-usb2: move suspend handling into clock prepare/unprepare for RK3588/RK3576
2026-11-10 0:02 [PATCH v4 0/5] phy: rockchip: inno-usb2: ensure PHY is running when clock is requested Sebastian Reichel
` (3 preceding siblings ...)
2026-11-10 0:02 ` [PATCH v4 4/5] phy: rockchip: inno-usb2: move suspend handling into new function Sebastian Reichel
@ 2026-11-10 0:02 ` Sebastian Reichel
2026-11-10 0:32 ` sashiko-bot
4 siblings, 1 reply; 7+ messages in thread
From: Sebastian Reichel @ 2026-11-10 0:02 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Heiko Stuebner,
Philipp Zabel, William Wu
Cc: linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Igor Paunovic, kernel, Sebastian Reichel
On RK3588 the 480MHz PHY clock must be running to access registers on
the OHCI and EHCI controllers. This requires that the clock output bit
is configured correctly (already happening) and that the PHY PLL itself
is running. The PHY PLL is only running when the PHY is not suspended.
This is currently handled independently of the clock and thus the clock
might be enabled with the PHY being suspended resulting in a
non-functional clock despite the clock being marked as prepared and
enabled according to the common clock framework.
This is especially a problem with OHCI system resume on RK3588, which
does:
ohci_platform_resume
-> ohci_platform_resume_common
-> deassert resets
-> ohci_platform_power_on -> enable clocks
-> ohci_resume
-> ohci_readl(ohci, &ohci->regs->control); // SError !
-> ...
-> ...
-> root hub resume (this resumes the PHY)
Fix this by fully powering the PHY from the clock prepare function.
This does not work on older platforms, which have multiple ports and
only one 480MHz clock as we do not know which port should be resumed.
But as far as I can tell these platforms do not have the clock
dependency from their USB controllers and thus are not affected.
Note that the 480MHz PHY clock registration is moved after the port
initialization loop as the clock operations may access the port after
this change. Additionally the old behaviour is used for single port
devices, that have their port disabled and thus port_cfg == NULL.
Backporting this is not trivial as the change relies on prior cleanups.
Considering system suspend/resume on RK3588 never worked on upstream and
there are more issues from preventing it (especially on older kernels),
this is not marked for stable backporting.
Fixes: 3a7db8e9edef ("phy: phy-rockchip-inno-usb2: add rk3588 support")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 29 +++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 642c996d23de..9fbd9e25f2c1 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -342,8 +342,15 @@ static void rockchip_usb2phy_set_suspend(struct rockchip_usb2phy *rphy,
}
}
+static bool rockchip_usb2phy_clk_manages_suspend(struct rockchip_usb2phy *rphy)
+{
+ return rphy->phy_cfg->num_ports == 1 && rphy->ports[0].port_cfg;
+}
+
static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
{
+ struct rockchip_usb2phy *rphy =
+ container_of(hw, struct rockchip_usb2phy, clk480m_hw);
const struct usb2phy_reg *clkout_ctl;
struct regmap *base;
@@ -357,16 +364,24 @@ static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
usleep_range(1200, 1300);
}
+ if (rockchip_usb2phy_clk_manages_suspend(rphy))
+ rockchip_usb2phy_set_suspend(rphy, &rphy->ports[0], false);
+
return 0;
}
static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw)
{
+ struct rockchip_usb2phy *rphy =
+ container_of(hw, struct rockchip_usb2phy, clk480m_hw);
const struct usb2phy_reg *clkout_ctl;
struct regmap *base;
rockchip_usb2phy_clk480m_clkout_ctl(hw, &base, &clkout_ctl);
+ if (rockchip_usb2phy_clk_manages_suspend(rphy))
+ rockchip_usb2phy_set_suspend(rphy, &rphy->ports[0], true);
+
/* turn off 480m clk output */
property_enable(base, clkout_ctl, false);
}
@@ -559,7 +574,8 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
if (ret)
return ret;
- rockchip_usb2phy_set_suspend(rphy, rport, false);
+ if (!rockchip_usb2phy_clk_manages_suspend(rphy))
+ rockchip_usb2phy_set_suspend(rphy, rport, false);
rport->suspended = false;
return 0;
@@ -575,7 +591,8 @@ static int rockchip_usb2phy_power_off(struct phy *phy)
if (rport->suspended)
return 0;
- rockchip_usb2phy_set_suspend(rphy, rport, true);
+ if (!rockchip_usb2phy_clk_manages_suspend(rphy))
+ rockchip_usb2phy_set_suspend(rphy, rport, true);
rport->suspended = true;
clk_disable_unprepare(rphy->clk480m);
@@ -1365,10 +1382,6 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
/* Clocks are optional */
rphy->num_clks = ret < 0 ? 0 : ret;
- ret = rockchip_usb2phy_clk480m_register(rphy);
- if (ret)
- return dev_err_probe(dev, ret, "failed to register 480m output clock\n");
-
ret = clk_bulk_prepare_enable(rphy->num_clks, rphy->clks);
if (ret)
return dev_err_probe(dev, ret, "failed to enable phy clock\n");
@@ -1421,6 +1434,10 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
}
}
+ ret = rockchip_usb2phy_clk480m_register(rphy);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register 480m output clock\n");
+
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (rphy->irq > 0) {
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4 5/5] phy: rockchip: inno-usb2: move suspend handling into clock prepare/unprepare for RK3588/RK3576
2026-11-10 0:02 ` [PATCH v4 5/5] phy: rockchip: inno-usb2: move suspend handling into clock prepare/unprepare for RK3588/RK3576 Sebastian Reichel
@ 2026-11-10 0:32 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-11-10 0:32 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: linux-phy, vkoul, neil.armstrong, olteanv
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] phy: rockchip: inno-usb2: late clk480m setup in
rockchip_usb2phy_probe()
--
commit 6ec3b4b8f3c0ab3cafbd20b325e5d225b7df828c
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: inno-usb2: move suspend handling into clock prepare/unprepare for RK3588/RK3576
This commit moves PHY suspend and resume handling on single-port devices
into the 480MHz clock prepare and unprepare callbacks, and moves 480MHz
clock registration in rockchip_usb2phy_probe() after port initialization.
> diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
> index 642c996d23dec..9fbd9e25f2c19 100644
> --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
> +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
[ ... ]
> @@ -1365,10 +1382,6 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
> /* Clocks are optional */
> rphy->num_clks = ret < 0 ? 0 : ret;
>
> - ret = rockchip_usb2phy_clk480m_register(rphy);
> - if (ret)
> - return dev_err_probe(dev, ret, "failed to register 480m output clock\n");
> -
> ret = clk_bulk_prepare_enable(rphy->num_clks, rphy->clks);
> if (ret)
> return dev_err_probe(dev, ret, "failed to enable phy clock\n");
> @@ -1421,6 +1434,10 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
> }
> }
>
> + ret = rockchip_usb2phy_clk480m_register(rphy);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register 480m output clock\n");
> +
> provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
[Severity: Medium]
Does moving rockchip_usb2phy_clk480m_register() after the port
initialization loop allow rport->otg_sm_work and per-port IRQs to use
rphy->clk480m before it is registered and after it is unregistered?
Inside the loop, rockchip_usb2phy_otg_port_init() and
rockchip_usb2phy_host_port_init() request per-port threaded IRQs, and
rockchip_usb2phy_otg_port_init() also registers the extcon notifier and
synchronously triggers rockchip_otg_event() to schedule rport->otg_sm_work:
drivers/phy/rockchip/phy-rockchip-inno-usb2.c:rockchip_usb2phy_otg_port_init() {
...
ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np);
...
if (!IS_ERR(rphy->edev)) {
rport->event_nb.notifier_call = rockchip_otg_event;
ret = devm_extcon_register_notifier(rphy->dev, rphy->edev,
EXTCON_USB_HOST, &rport->event_nb);
...
if (!of_property_present(rphy->dev->of_node, "extcon")) {
/* do initial sync of usb state */
id = property_enabled(rphy->grf, &rport->port_cfg->utmi_id);
extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, !id);
}
}
}
Because rockchip_usb2phy_clk480m_register() now queues its devm action
after the devm-managed port IRQs and extcon notifier, devres teardown runs
rockchip_usb2phy_clk480m_unregister() first and frees rphy->clk480m while
port IRQ handlers and work items can still run:
CPU0 CPU1 (kworker / IRQ thread)
device_unbind_cleanup()
devres_release_all()
rockchip_usb2phy_clk480m_unregister()
clk_unregister(rphy->clk480m)
rockchip_usb2phy_otg_sm_work()
rockchip_usb2phy_power_on()
clk_prepare_enable(rphy->clk480m)
<-- use-after-free
devm_irq_release()
If rockchip_usb2phy_clk480m_register() fails after
rockchip_usb2phy_otg_port_init() has queued rport->otg_sm_work,
rphy->clk480m is left holding an ERR_PTR that crashes clk_prepare(), and
rport->otg_sm_work remains queued after devres frees rphy.
Also, if rport->otg_sm_work or a port IRQ reaches
rockchip_usb2phy_power_on() before rockchip_usb2phy_clk480m_register()
assigns rphy->clk480m, clk_prepare_enable(NULL) succeeds as a no-op while
rockchip_usb2phy_set_suspend() is skipped, leaving the PHY suspended with
rport->suspended set to false and causing a later
rockchip_usb2phy_power_off() to unprepare an unprepared clock.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261110-phy-rockchip-inno-usb2-clock-fix-v4-0-51acc071634f@collabora.com?part=5
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-10 0:32 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-10 0:02 [PATCH v4 0/5] phy: rockchip: inno-usb2: ensure PHY is running when clock is requested Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 1/5] phy: rockchip: inno-usb2: simplify and unify PHY op debug messages Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 2/5] phy: rockchip: inno-usb2: ignore regmap_write return codes Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 3/5] phy: rockchip: inno-usb2: ignore reset_control " Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 4/5] phy: rockchip: inno-usb2: move suspend handling into new function Sebastian Reichel
2026-11-10 0:02 ` [PATCH v4 5/5] phy: rockchip: inno-usb2: move suspend handling into clock prepare/unprepare for RK3588/RK3576 Sebastian Reichel
2026-11-10 0:32 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox