Linux-PHY Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 01/16] dt-bindings: phy: rockchip-usbdp: add improved ports scheme
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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.

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
 .../bindings/phy/phy-rockchip-usbdp.yaml           | 23 ++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
index 8b7059d5b182..f728acf057e4 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
@@ -114,6 +114,29 @@ properties:
       A port node to link the PHY to a TypeC controller for the purpose of
       handling orientation switching.
 
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of the PHY for USB (or DP when configured into 4 lane
+          mode), which should point to the superspeed port of a USB connector.
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Incoming endpoint from the USB controller
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Incoming endpoint from the DisplayPort controller
+
+      port@3:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of the PHY for DP, 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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 00/16] phy: rockchip: usbdp: Fixes, DP 1-lane support and cleanups
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel, William Wu

This series overhauls the Rockchip USBDP driver; apart from a
a bunch of cleanups and small improvements the main goal is to
get the driver ready for proper USB-C DP AltMode support.

Once this series has landed, it unblocks enabling proper USB-C
DP AltMode on the RK3588 and RK3576 platforms incl. runtime PM
for the Synopsys DesignWare DisplayPort controller.

Apart from this series, further changes are required on the
DRM side. There are no compile-time dependencies between the
DRM side and the PHY side, but the PHY side must be applied
to avoid SErrors once runtime PM is added to the DisplayPort
controller driver. Thus it would be really good to land this
series in the next merge window.

Changes in v4:
- Link to v3: https://lore.kernel.org/r/20260313-rockchip-usbdp-cleanup-v3-0-3e8fe89a35b5@collabora.com
- rebased to v7.1-rc1 (no changes)
- Update DRM bridge registration patch to avoid registration when DP aux
  port is not connected to anything, since this results in errors and some
  boards use USBDP instances for USB3 only.
- Add patch renaming mode_change into phy_needs_reinit
- Add patch to re-init PHY on orientation change
- Add patch to factor out lane_mux_sel setup
- Add patch to handle mutex via guard functions

Changes in v3:
- Link to v2: https://lore.kernel.org/r/20260213-rockchip-usbdp-cleanup-v2-0-b67ec225f96e@collabora.com
- Add patch to register the USBDP PHY as DRM bridge
- Add patch to describe ports in DT binding (used by the DRM bridge)
- Add patch to drop HPD handling from the PHY

Changes in v2:
- Link to v1: https://lore.kernel.org/r/20260203-rockchip-usbdp-cleanup-v1-0-16a6f92ed176@collabora.com
- Added new patches to fix USB3 SError

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
Frank Wang (1):
      phy: rockchip: usbdp: Amend SSC modulation deviation

Sebastian Reichel (13):
      dt-bindings: phy: rockchip-usbdp: add improved ports scheme
      phy: rockchip: usbdp: Do not loose USB3 PHY status
      phy: rockchip: usbdp: Keep clocks running on PHY re-init
      phy: rockchip: usbdp: Add missing mode_change update
      phy: rockchip: usbdp: Rename DP lane functions
      phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST
      phy: rockchip: usbdp: Cleanup DP lane selection function
      phy: rockchip: usbdp: Register DP aux bridge
      phy: rockchip: usbdp: Drop DP HPD handling
      phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit
      phy: rockchip: usbdp: Re-init the PHY on orientation change
      phy: rockchip: usbdp: Factor out lane_mux_sel setup
      phy: rockchip: usbdp: Use guard functions for mutex

William Wu (1):
      phy: rockchip: usbdp: Fix LFPS detect threshold control

Zhang Yubing (1):
      phy: rockchip: usbdp: Support single-lane DP

 .../bindings/phy/phy-rockchip-usbdp.yaml           |  23 ++
 drivers/phy/rockchip/Kconfig                       |   2 +
 drivers/phy/rockchip/phy-rockchip-usbdp.c          | 302 +++++++++------------
 3 files changed, 149 insertions(+), 178 deletions(-)
---
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
change-id: 20260203-rockchip-usbdp-cleanup-5b59dfb561a3

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

* [PATCH v4 10/16] phy: rockchip: usbdp: Cleanup DP lane selection function
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

Use FIELD_PREP_WM16() helpers to simplify the DP lane selection
logic.

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 1bfc365e2b2c..beab20e4c512 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -550,30 +550,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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 05/16] phy: rockchip: usbdp: Fix LFPS detect threshold control
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel, William Wu
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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]
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 1f686844c337..97e53b933225 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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 06/16] phy: rockchip: usbdp: Add missing mode_change update
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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.

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 97e53b933225..febc148a754e 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -619,6 +619,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) {
@@ -649,7 +658,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,
@@ -1385,10 +1394,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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 03/16] phy: rockchip: usbdp: Keep clocks running on PHY re-init
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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().

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 744cc7c642f4..98562a888b42 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1012,8 +1012,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;
 	}

-- 
2.53.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 04/16] phy: rockchip: usbdp: Amend SSC modulation deviation
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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]
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 98562a888b42..1f686844c337 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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 08/16] phy: rockchip: usbdp: Rename DP lane functions
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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.

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 bf8394174294..6d7ca11b308e 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -547,7 +547,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;
@@ -576,7 +576,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;
@@ -1072,9 +1072,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);
@@ -1092,7 +1092,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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 13/16] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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 3f1233f50686..694832cc161e 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -172,7 +172,7 @@ struct rk_udphy {
 
 	/* PHY status management */
 	bool flip;
-	bool mode_change;
+	bool phy_needs_reinit;
 	u8 mode;
 	u8 status;
 
@@ -582,7 +582,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;
 }
 
@@ -970,15 +970,15 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
 	}
 
 	if (udphy->status == UDPHY_MODE_NONE) {
-		udphy->mode_change = false;
+		udphy->phy_needs_reinit = 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->phy_needs_reinit) {
+		udphy->phy_needs_reinit = false;
 		if (udphy->mode == UDPHY_MODE_DP)
 			rk_udphy_u3_port_disable(udphy, true);
 

-- 
2.53.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 16/16] phy: rockchip: usbdp: Use guard functions for mutex
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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 | 60 ++++++++++++++-----------------
 1 file changed, 27 insertions(+), 33 deletions(-)

diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 17637d92cf9b..f318b04c097d 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>
@@ -654,14 +655,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)
@@ -671,8 +673,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;
 }
 
@@ -1044,12 +1044,10 @@ static int rk_udphy_dp_phy_init(struct phy *phy)
 {
 	struct rk_udphy *udphy = phy_get_drvdata(phy);
 
-	mutex_lock(&udphy->mutex);
+	guard(mutex)(&udphy->mutex);
 
 	udphy->dp_in_use = true;
 
-	mutex_unlock(&udphy->mutex);
-
 	return 0;
 }
 
@@ -1057,9 +1055,10 @@ static int rk_udphy_dp_phy_exit(struct phy *phy)
 {
 	struct rk_udphy *udphy = phy_get_drvdata(phy);
 
-	mutex_lock(&udphy->mutex);
+	guard(mutex)(&udphy->mutex);
+
 	udphy->dp_in_use = false;
-	mutex_unlock(&udphy->mutex);
+
 	return 0;
 }
 
@@ -1068,26 +1067,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;
 }
 
@@ -1095,10 +1093,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;
 }
@@ -1302,19 +1300,18 @@ 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;
+	int ret;
+
+	guard(mutex)(&udphy->mutex);
 
-	mutex_lock(&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;
 }
 
@@ -1322,15 +1319,14 @@ 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;
 }
 
@@ -1345,12 +1341,10 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
 {
 	struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
 
-	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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 09/16] phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

Cleanup code by replacing open-coded version of FIELD_PREP_WM16_CONST
with the existing helper macro.

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 6d7ca11b308e..1bfc365e2b2c 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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 07/16] phy: rockchip: usbdp: Support single-lane DP
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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 | 61 +++++++++++++------------------
 1 file changed, 25 insertions(+), 36 deletions(-)

diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index febc148a754e..bf8394174294 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;
@@ -537,6 +538,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)
@@ -544,18 +552,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:
@@ -568,28 +576,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;
@@ -659,6 +645,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,
@@ -897,7 +884,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");
 
@@ -923,7 +910,8 @@ 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);
 	}
@@ -1074,18 +1062,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);
 
@@ -1365,6 +1352,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:
@@ -1381,6 +1369,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;
 	}
 
@@ -1529,7 +1518,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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 12/16] phy: rockchip: usbdp: Drop DP HPD handling
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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 | 47 +------------------------------
 1 file changed, 1 insertion(+), 46 deletions(-)

diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 77ad2a89d4f2..3f1233f50686 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -186,8 +186,6 @@ 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;
@@ -579,19 +577,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)
@@ -1360,22 +1345,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;
@@ -1531,20 +1501,6 @@ static int rk_udphy_probe(struct platform_device *pdev)
 	return 0;
 }
 
-static int __maybe_unused rk_udphy_resume(struct device *dev)
-{
-	struct rk_udphy *udphy = dev_get_drvdata(dev);
-
-	if (udphy->dp_sink_hpd_sel)
-		rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
-
-	return 0;
-}
-
-static const struct dev_pm_ops rk_udphy_pm_ops = {
-	SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, rk_udphy_resume)
-};
-
 static const char * const rk_udphy_rst_list[] = {
 	"init", "cmn", "lane", "pcs_apb", "pma_apb"
 };
@@ -1649,7 +1605,6 @@ static struct platform_driver rk_udphy_driver = {
 	.driver		= {
 		.name	= "rockchip-usbdp-phy",
 		.of_match_table = rk_udphy_dt_match,
-		.pm = &rk_udphy_pm_ops,
 	},
 };
 module_platform_driver(rk_udphy_driver);

-- 
2.53.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 14/16] phy: rockchip: usbdp: Re-init the PHY on orientation change
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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 694832cc161e..80fe5993c6c7 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -624,6 +624,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);
 
@@ -635,7 +636,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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 15/16] phy: rockchip: usbdp: Factor out lane_mux_sel setup
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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 80fe5993c6c7..17637d92cf9b 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -586,6 +586,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) {
@@ -593,10 +629,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);
@@ -606,18 +638,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,
@@ -1316,42 +1344,13 @@ 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;
 
 	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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH v4 11/16] phy: rockchip: usbdp: Register DP aux bridge
From: Sebastian Reichel @ 2026-04-28 16:13 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
	linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
	devicetree, Sebastian Reichel
In-Reply-To: <20260428-rockchip-usbdp-cleanup-v4-0-7775671ece22@collabora.com>

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

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
 drivers/phy/rockchip/Kconfig              |  2 ++
 drivers/phy/rockchip/phy-rockchip-usbdp.c | 14 ++++++++++++++
 2 files changed, 16 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 beab20e4c512..77ad2a89d4f2 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>
@@ -1434,6 +1435,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;
@@ -1492,6 +1494,18 @@ 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.
+	 */
+	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


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH] phy: airoha: use C-style SPDX comment for header file
From: Aditya Dabhade @ 2026-04-28 19:34 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: Vinod Koul, Neil Armstrong, linux-arm-kernel, linux-phy,
	linux-kernel, Aditya Dabhade

checkpatch reports an improper SPDX comment style for this header:

    WARNING: Improper SPDX comment style for
    'drivers/phy/phy-airoha-pcie-regs.h', please use '/*' instead

Per Documentation/process/license-rules.rst, C header files must use the
'/* SPDX-License-Identifier: ...  */' form instead of the '// ....' form
used in C source files. Fix it.

Signed-off-by: Aditya Dabhade <aditya.dabhade066@gmail.com>
---
 drivers/phy/phy-airoha-pcie-regs.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/phy/phy-airoha-pcie-regs.h b/drivers/phy/phy-airoha-pcie-regs.h
index b938a7b468f..58572c79372 100644
--- a/drivers/phy/phy-airoha-pcie-regs.h
+++ b/drivers/phy/phy-airoha-pcie-regs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-only
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2024 AIROHA Inc
  * Author: Lorenzo Bianconi <lorenzo@kernel.org>
-- 
2.34.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* Re: [PATCH 4/4] phy: qualcomm: qmp-combo: Add support for Hawi SoC
From: Ronak Raheja @ 2026-04-29  7:28 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: vkoul, krzk+dt, conor+dt, robh, neil.armstrong, gregkh,
	konrad.dybcio, abel.vesa, wesley.cheng, krzysztof.kozlowski,
	linux-arm-msm, linux-phy, devicetree, linux-usb, linux-kernel
In-Reply-To: <s6ortwyjjoq6lsxjjykbbshg2rczchtol4ntmxipcrsqufvsby@whwxtlnn74id>


On 4/28/2026 4:40 AM, Dmitry Baryshkov wrote:
> On Mon, Apr 27, 2026 at 02:42:17PM -0700, Ronak Raheja wrote:
>> Add support for the USB3-DP combo PHY found on Hawi platform.
>>
>> The QMP PHY for Hawi uses QSERDES V10 register layouts. Add the required
>> PHY sequences from the hardware programming guide and new V10 register
>> header files. Also add a new v10 offset structure to incorporate the new
>> COM AON register module.
> 
> Was this tested with the DP too or did you test only the USB part?
> 

Only USB was tested. DP support will be added in a follow-up patch
once the MDSS nodes are ready for Hawi.

>>
>> Signed-off-by: Ronak Raheja <ronak.raheja@oss.qualcomm.com>
>> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>> ---
>>  .../phy/qualcomm/phy-qcom-qmp-com-aon-v10.h   |  15 ++
>>  drivers/phy/qualcomm/phy-qcom-qmp-combo.c     | 231 +++++++++++++++++-
>>  .../phy/qualcomm/phy-qcom-qmp-dp-phy-v10.h    |  15 ++
>>  .../phy/qualcomm/phy-qcom-qmp-pcs-aon-v10.h   |  13 +
>>  .../phy/qualcomm/phy-qcom-qmp-pcs-usb-v10.h   |  19 ++
>>  drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h   |  34 +++
>>  .../qualcomm/phy-qcom-qmp-qserdes-com-v10.h   |  89 +++++++
>>  .../qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h  |  89 +++++++
>>  drivers/phy/qualcomm/phy-qcom-qmp.h           |   5 +
>>  9 files changed, 506 insertions(+), 4 deletions(-)
>>  create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-com-aon-v10.h
>>  create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v10.h
>>  create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v10.h
>>  create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v10.h
>>  create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h
>>  create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h
>>  create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h
>>
>> @@ -2198,6 +2368,7 @@ struct qmp_combo_offsets {
>>  	u16 dp_txa;
>>  	u16 dp_txb;
>>  	u16 dp_dp_phy;
>> +	u16 aon_toggle;
> 
> Are there (or will there) be any other regs? Is it just one-register
> space?
> 

The COM AON block has 3 registers: USB3_AON_TOGGLE_ENABLE (0x00),
DP_AON_TOGGLE_ENABLE (0x04), and DUMMY_STATUS (0x08). Only the first
two are currently used.

>>  };
>>  
>>  struct qmp_phy_cfg {
>> @@ -2705,6 +2895,27 @@ static const struct qmp_phy_cfg x1e80100_usb3dpphy_cfg = {
>>  	.regs			= qmp_v6_n4_usb3phy_regs_layout,
>>  };
>>  
>> +static const struct qmp_phy_cfg hawi_usb3dpphy_cfg = {
>> +	.offsets		= &qmp_combo_offsets_v10,
>> +
>> +	.serdes_tbl		= hawi_usb3_serdes_tbl,
>> +	.serdes_tbl_num		= ARRAY_SIZE(hawi_usb3_serdes_tbl),
>> +	.tx_tbl			= hawi_usb3_tx_tbl,
>> +	.tx_tbl_num		= ARRAY_SIZE(hawi_usb3_tx_tbl),
>> +	.rx_tbl			= hawi_usb3_rx_tbl,
>> +	.rx_tbl_num		= ARRAY_SIZE(hawi_usb3_rx_tbl),
>> +	.pcs_tbl		= hawi_usb3_pcs_tbl,
>> +	.pcs_tbl_num		= ARRAY_SIZE(hawi_usb3_pcs_tbl),
>> +	.pcs_usb_tbl		= hawi_usb3_pcs_usb_tbl,
>> +	.pcs_usb_tbl_num	= ARRAY_SIZE(hawi_usb3_pcs_usb_tbl),
>> +
>> +	.regs			= qmp_v10_usb3phy_regs_layout,
>> +	.reset_list		= msm8996_usb3phy_reset_l,
>> +	.num_resets		= ARRAY_SIZE(msm8996_usb3phy_reset_l),
>> +	.vreg_list		= qmp_phy_vreg_l,
>> +	.num_vregs		= ARRAY_SIZE(qmp_phy_vreg_l),
> 
> No DP counterpart and no mention of it in the commit message.
> 

The DP configuration will be added in a follow-up patch once the MDSS
nodes are ready for Hawi.

Thanks,
Ronak

>> +};
>> +
>>  static const struct qmp_phy_cfg sm6350_usb3dpphy_cfg = {
>>  	.offsets		= &qmp_combo_offsets_v3,
>>  
> 


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH] phy: tegra: xusb: Fix per-pad high-speed termination calibration
From: Jon Hunter @ 2026-04-29  9:10 UTC (permalink / raw)
  To: Wei-Cheng Chen, jckuo, vkoul, neil.armstrong, thierry.reding
  Cc: linux-phy, linux-tegra, linux-kernel, waynec, wtsai
In-Reply-To: <fde997f6-c0a8-430b-8e45-38a490e683ad@nvidia.com>

Hi Vinod,

On 17/04/2026 09:23, Jon Hunter wrote:
> Hi Vinod,
> 
> On 24/03/2026 09:11, Jon Hunter wrote:
>>
>> On 04/03/2026 10:26, Wei-Cheng Chen wrote:
>>> From: Wayne Chang <waynec@nvidia.com>
>>>
>>> The existing code reads a single hs_term_range_adj value from bit field
>>> [10:7] of FUSE_SKU_CALIB_0 and applies it to all USB2 pads uniformly.
>>> However, on SoCs that support per-pad termination, each pad has its own
>>> hs_term_range_adj field: pad 0 in FUSE_SKU_CALIB_0[10:7], and pads 1-3
>>> in FUSE_USB_CALIB_EXT_0 at bit offsets [8:5], [12:9], and [16:13]
>>> respectively.
>>>
>>> Fix the calibration by reading per-pad values from the appropriate fuse
>>> registers. For SoCs that do not support per-pad termination, replicate
>>> pad 0's value to all pads to maintain existing behavior.
>>>
>>> Add a has_per_pad_term flag to the SoC data to indicate whether per-pad
>>> termination values are available in FUSE_USB_CALIB_EXT_0.
>>>
>>> Fixes: 1ef535c6ba8e ("phy: tegra: xusb: Add Tegra194 support")
>>> Cc: stable@vger.kernel.org
>>> Signed-off-by: Wayne Chang <waynec@nvidia.com>
>>> Signed-off-by: Wei-Cheng Chen <weichengc@nvidia.com>
>>> ---
>>>   drivers/phy/tegra/xusb-tegra186.c | 33 ++++++++++++++++++++++++-------
>>>   drivers/phy/tegra/xusb.h          |  1 +
>>>   2 files changed, 27 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/ 
>>> xusb-tegra186.c
>>> index bec9616c4a2..4452e73fb82 100644
>>> --- a/drivers/phy/tegra/xusb-tegra186.c
>>> +++ b/drivers/phy/tegra/xusb-tegra186.c
>>> @@ -20,8 +20,8 @@
>>>   /* FUSE USB_CALIB registers */
>>>   #define HS_CURR_LEVEL_PADX_SHIFT(x)    ((x) ? (11 + (x - 1) * 6) : 0)
>>>   #define HS_CURR_LEVEL_PAD_MASK        0x3f
>>> -#define HS_TERM_RANGE_ADJ_SHIFT        7
>>> -#define HS_TERM_RANGE_ADJ_MASK        0xf
>>> +#define HS_TERM_RANGE_ADJ_PADX_SHIFT(x)    ((x) ? (5 + (x - 1) * 
>>> 4) : 7)
>>> +#define HS_TERM_RANGE_ADJ_PAD_MASK    0xf
>>>   #define HS_SQUELCH_SHIFT        29
>>>   #define HS_SQUELCH_MASK            0x7
>>> @@ -253,7 +253,7 @@
>>>   struct tegra_xusb_fuse_calibration {
>>>       u32 *hs_curr_level;
>>>       u32 hs_squelch;
>>> -    u32 hs_term_range_adj;
>>> +    u32 *hs_term_range_adj;
>>>       u32 rpd_ctrl;
>>>   };
>>> @@ -930,7 +930,7 @@ static int tegra186_utmi_phy_power_on(struct phy 
>>> *phy)
>>>       value = padctl_readl(padctl, 
>>> XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
>>>       value &= ~TERM_RANGE_ADJ(~0);
>>> -    value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj);
>>> +    value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj[index]);
>>>       value &= ~RPD_CTRL(~0);
>>>       value |= RPD_CTRL(priv->calib.rpd_ctrl);
>>>       padctl_writel(padctl, value, 
>>> XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
>>> @@ -1464,17 +1464,23 @@ static const char * const 
>>> tegra186_usb3_functions[] = {
>>>   static int
>>>   tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl 
>>> *padctl)
>>>   {
>>> +    const struct tegra_xusb_padctl_soc *soc = padctl->base.soc;
>>>       struct device *dev = padctl->base.dev;
>>>       unsigned int i, count;
>>>       u32 value, *level;
>>> +    u32 *hs_term_range_adj;
>>>       int err;
>>> -    count = padctl->base.soc->ports.usb2.count;
>>> +    count = soc->ports.usb2.count;
>>>       level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
>>>       if (!level)
>>>           return -ENOMEM;
>>> +    hs_term_range_adj = devm_kcalloc(dev, count, sizeof(u32), 
>>> GFP_KERNEL);
>>> +    if (!hs_term_range_adj)
>>> +        return -ENOMEM;
>>> +
>>>       err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
>>>       if (err)
>>>           return dev_err_probe(dev, err,
>>> @@ -1490,8 +1496,8 @@ tegra186_xusb_read_fuse_calibration(struct 
>>> tegra186_xusb_padctl *padctl)
>>>       padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) &
>>>                       HS_SQUELCH_MASK;
>>> -    padctl->calib.hs_term_range_adj = (value >> 
>>> HS_TERM_RANGE_ADJ_SHIFT) &
>>> -                        HS_TERM_RANGE_ADJ_MASK;
>>> +    hs_term_range_adj[0] = (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(0)) &
>>> +                HS_TERM_RANGE_ADJ_PAD_MASK;
>>>       err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
>>>       if (err) {
>>> @@ -1503,6 +1509,17 @@ tegra186_xusb_read_fuse_calibration(struct 
>>> tegra186_xusb_padctl *padctl)
>>>       padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & 
>>> RPD_CTRL_MASK;
>>> +    for (i = 1; i < count; i++) {
>>> +        if (soc->has_per_pad_term)
>>> +            hs_term_range_adj[i] =
>>> +                (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(i)) &
>>> +                HS_TERM_RANGE_ADJ_PAD_MASK;
>>> +        else
>>> +            hs_term_range_adj[i] = hs_term_range_adj[0];
>>> +    }
>>> +
>>> +    padctl->calib.hs_term_range_adj = hs_term_range_adj;
>>> +
>>>       return 0;
>>>   }
>>> @@ -1708,6 +1725,7 @@ const struct tegra_xusb_padctl_soc 
>>> tegra194_xusb_padctl_soc = {
>>>       .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
>>>       .supports_gen2 = true,
>>>       .poll_trk_completed = true,
>>> +    .has_per_pad_term = true,
>>>   };
>>>   EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
>>> @@ -1732,6 +1750,7 @@ const struct tegra_xusb_padctl_soc 
>>> tegra234_xusb_padctl_soc = {
>>>       .trk_hw_mode = false,
>>>       .trk_update_on_idle = true,
>>>       .supports_lp_cfg_en = true,
>>> +    .has_per_pad_term = true,
>>>   };
>>>   EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
>>>   #endif
>>> diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
>>> index d2b5f956513..810b410672f 100644
>>> --- a/drivers/phy/tegra/xusb.h
>>> +++ b/drivers/phy/tegra/xusb.h
>>> @@ -436,6 +436,7 @@ struct tegra_xusb_padctl_soc {
>>>       bool trk_hw_mode;
>>>       bool trk_update_on_idle;
>>>       bool supports_lp_cfg_en;
>>> +    bool has_per_pad_term;
>>>   };
>>>   struct tegra_xusb_padctl {
>>
>> Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
>> Tested-by: Jon Hunter <jonathanh@nvidia.com>
> 
> I guess this was missed for Linux v7.1. If you don't have any concerns 
> can you pick this up for v7.2 once v7.1-rc1 is out?

OK to pick this up now? It would be great to get this into -next.

Thanks!
Jon

-- 
nvpublic


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v4 0/2] phy: spacemit: Add USB2 PHY support for K3 SoC
From: Yixun Lan @ 2026-04-30  2:04 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Ze Huang
  Cc: Junzhong Pan, linux-phy, devicetree, linux-riscv, spacemit,
	linux-kernel, Krzysztof Kozlowski, Yao Zi
In-Reply-To: <20260401130713-GKD976850@kernel.org>

Hi Vinod Koul,

On 21:07 Wed 01 Apr     , Yixun Lan wrote:
> Hi Vinod Koul,
> 
> On 08:14 Thu 26 Mar     , Yixun Lan wrote:
> > Hi Vinod Koul,
> > 
> > On 01:00 Thu 05 Mar     , Yixun Lan wrote:
> > > The series trys to add USB2 PHY support for SpacemiT K3 SoC, while 
> > > patch [1/2] implement a disconnect function which is needed during
> > > next connection.
> > > 
> > > No DTS part has been inclueded in this series, instead I plan to
> > > submit them later while adding USB host support.
> > > 
> > > I've collected all patches and pushed a complete review branch here[1],
> > > for people who interested to test easily, which include DTS and necessary
> > > changes, other patches may still need to improve, but sufficient for
> > > verifying the functionality.
> > > 
> > > Link: https://github.com/spacemit-com/linux/tree/WIP/k3/usb2 [1]
> > > 
> > 
> > Just want to ping this, Can you queue it for v7.1? Thanks
> > 
> Ping again, I'd like to see USB2.0 support in v7.1 for SpacemiT K3
> SoC.. and I think this phy patch is the only dependency that waiting
> for, the USB DWC[1], and hub[2] patch already merged
> 
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=c05cf9d274daf72dc7e433480cf2e0e888f6bd89 [1] 
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=00b4fe5be06aecd6426930de86b7cffc2330f4b8 [2]
> 
I'd like to ping inclusion for v7.2 cycle, checked locally, it's still able
to apply to v7.1-rc1 cleanly, so I see no need to send new version.

Many thanks

-- 
Yixun Lan (dlan)

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [PATCH 0/2] riscv: spacemit: Add K3 PCIe/USB comb phy support
From: Inochi Amaoto @ 2026-04-30  2:28 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yixun Lan, Kees Cook, Gustavo A. R. Silva,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Inochi Amaoto, Ze Huang, Alex Elder
  Cc: linux-phy, devicetree, linux-riscv, spacemit, linux-kernel,
	linux-hardening, Yixun Lan, Longbin Li

The PCIe/USB comb phy on K3 is a big phy that contains multiple
standalone phys for each PCIe and USB controllers. This phy is
required to configure a syscon device for mux configuration and
calibration.

Inochi Amaoto (2):
  dt-bindings: phy: Add Spacemit K3 USB3/PCIe comb phy support
  phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3

 .../bindings/phy/spacemit,k3-comb-phy.yaml    |  63 +++
 drivers/phy/spacemit/Kconfig                  |  16 +
 drivers/phy/spacemit/Makefile                 |   2 +
 drivers/phy/spacemit/phy-k3-combphy.c         | 250 +++++++++++
 drivers/phy/spacemit/phy-k3-common.c          | 398 ++++++++++++++++++
 drivers/phy/spacemit/phy-k3-common.h          |  27 ++
 6 files changed, 756 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/spacemit,k3-comb-phy.yaml
 create mode 100644 drivers/phy/spacemit/phy-k3-combphy.c
 create mode 100644 drivers/phy/spacemit/phy-k3-common.c
 create mode 100644 drivers/phy/spacemit/phy-k3-common.h

--
2.54.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [PATCH 1/2] dt-bindings: phy: Add Spacemit K3 USB3/PCIe comb phy support
From: Inochi Amaoto @ 2026-04-30  2:28 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yixun Lan, Kees Cook, Gustavo A. R. Silva,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Inochi Amaoto, Ze Huang, Alex Elder
  Cc: linux-phy, devicetree, linux-riscv, spacemit, linux-kernel,
	linux-hardening, Yixun Lan, Longbin Li
In-Reply-To: <20260430022843.1090138-1-inochiama@gmail.com>

The USB3/PCIe comb PHY on the K3 is a complex PHY group that
can provide multiple phy for both PCIe and USB controller.
Its mux configuration is controlled by the APMU syscon device.

Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
---
 .../bindings/phy/spacemit,k3-comb-phy.yaml    | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/spacemit,k3-comb-phy.yaml

diff --git a/Documentation/devicetree/bindings/phy/spacemit,k3-comb-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,k3-comb-phy.yaml
new file mode 100644
index 000000000000..7aa2cf9301b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/spacemit,k3-comb-phy.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/spacemit,k3-comb-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Spacemit K3 PCIE/USB3 Comb PHY
+
+maintainers:
+  - Inochi Amaoto <inochiama@gmail.com>
+
+properties:
+  compatible:
+    const: spacemit,k3-comb-phy
+
+  reg:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 2
+    description:
+      The first one is phy id, the second one is phy type.
+
+  spacemit,apb-spare:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to APB SPARE system controller interface, used for
+      PHY calibration.
+
+  spacemit,apmu:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: phandle of APMU syscon
+          - description: configuration of the PHY lanes
+    description: |
+      Phandle to control PHY mux configuration. The configuration
+      is described as follows:
+      bit 4: 0 - PCIe A x8 mode, 1 - PCIe lane share mode
+      bit 3: 0 - PCIe A x4 mode, 1 - PCIe A x2 and PCIe B x2 mode
+      bit 2: 0 - PCIe C lane 0 is PCIe mode , 1 - USB mode
+      bit 1: 0 - PCIe C lane 1 is PCIe mode , 1 - USB mode
+      bit 0: 0 - PCIe D lane is PCIe mode , 1 - USB mode
+
+      The bit[3:0] is only valid when bit 4 is 1.
+
+required:
+  - compatible
+  - "#phy-cells"
+  - spacemit,apb-spare
+  - spacemit,apmu
+
+additionalProperties: false
+
+examples:
+  - |
+    phy@81d00000 {
+      compatible = "spacemit,k3-comb-phy";
+      reg = <0x81d00000 0x600000>;
+      #phy-cells = <2>;
+      spacemit,apb-spare = <&apb_spare>;
+      spacemit,apmu = <&apmu 0x00>;
+    };
-- 
2.54.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* [PATCH 2/2] phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3
From: Inochi Amaoto @ 2026-04-30  2:28 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yixun Lan, Kees Cook, Gustavo A. R. Silva,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Inochi Amaoto, Ze Huang, Alex Elder
  Cc: linux-phy, devicetree, linux-riscv, spacemit, linux-kernel,
	linux-hardening, Yixun Lan, Longbin Li
In-Reply-To: <20260430022843.1090138-1-inochiama@gmail.com>

The comb PHY on K3 requires to configure a syscon device for the
right mux configuration. And it requires calibration before any
usage.

Add USB3/PCIe comb PHY driver for Spacemit K3.

Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
---
 drivers/phy/spacemit/Kconfig          |  16 ++
 drivers/phy/spacemit/Makefile         |   2 +
 drivers/phy/spacemit/phy-k3-combphy.c | 250 ++++++++++++++++
 drivers/phy/spacemit/phy-k3-common.c  | 398 ++++++++++++++++++++++++++
 drivers/phy/spacemit/phy-k3-common.h  |  27 ++
 5 files changed, 693 insertions(+)
 create mode 100644 drivers/phy/spacemit/phy-k3-combphy.c
 create mode 100644 drivers/phy/spacemit/phy-k3-common.c
 create mode 100644 drivers/phy/spacemit/phy-k3-common.h

diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
index 50b0005acf66..5fdf18fce499 100644
--- a/drivers/phy/spacemit/Kconfig
+++ b/drivers/phy/spacemit/Kconfig
@@ -23,3 +23,19 @@ config PHY_SPACEMIT_K1_USB2
 	help
 	  Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
 	  enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.
+
+config PHY_SPACEMIT_K3_COMMON_OPS
+	tristate
+	select MFD_SYSCON
+	select GENERIC_PHY
+
+config PHY_SPACEMIT_K3_COMBO_PHY
+	tristate "SpacemiT K3 USB3/PCIe PHY support"
+	depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
+	depends on COMMON_CLK
+	select PHY_SPACEMIT_K3_COMMON_OPS
+	help
+	  Enable this to support K3 USB3/PCIe combo PHY driver. This
+	  driver takes care of enabling and clock setup and will be used
+	  by K3 dwc3 driver.
+	  If unsure, say N.
diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
index a821a21d6142..41be7b0388da 100644
--- a/drivers/phy/spacemit/Makefile
+++ b/drivers/phy/spacemit/Makefile
@@ -1,3 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE)		+= phy-k1-pcie.o
 obj-$(CONFIG_PHY_SPACEMIT_K1_USB2)		+= phy-k1-usb2.o
+obj-$(CONFIG_PHY_SPACEMIT_K3_COMBO_PHY)		+= phy-k3-combphy.o
+obj-$(CONFIG_PHY_SPACEMIT_K3_COMMON_OPS)	+= phy-k3-common.o
diff --git a/drivers/phy/spacemit/phy-k3-combphy.c b/drivers/phy/spacemit/phy-k3-combphy.c
new file mode 100644
index 000000000000..66fa6330ad6e
--- /dev/null
+++ b/drivers/phy/spacemit/phy-k3-combphy.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * phy-k3-usb3.c - SpacemiT K3 Type-C Orientation Switch Driver
+ *
+ * Copyright (c) 2025 SpacemiT Technology Co. Ltd
+ */
+
+#include <linux/bitfield.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/phy/phy.h>
+
+#include <dt-bindings/phy/phy.h>
+
+#include "phy-k3-common.h"
+
+/*
+ * The PCIE/USB Subsystem on SpacemiT K3 have 3 single lane PIPE3 PHYs
+ * (PHY2/3/4) shared by PCIE PortC/D and USB3 PortB/C/D.
+ *
+ * PMUA_PCIE_SUBSYS_MGMT[4:0]
+ *
+ *   bit4 = 0 : PCIe A X8 mode, all 8 lanes dedicated to PCIe Port A
+ *          1 : PHY lanes shared between PCIe or USB according to [3:0]
+ *
+ * All PHY matrix combinations according to [4:0]:
+ *
+ *   0x0X : PCIe-A X8
+ *   0x10 : PCIe-C x2 (PHY2+PHY3) + PCIe-D x1 (PHY4)
+ *   0x11 : PCIe-C x2 (PHY2+PHY3) + USB-D (PHY4)
+ *   0x12 : PCIe-C x1 (PHY2)      + USB-C (PHY3)
+ *   0x13 : PCIe-C x1 (PHY2)      + USB-C (PHY3) + USB-D (PHY4)
+ *   0x14 : PCIe-C x1 (PHY3)      + USB-B (PHY2)
+ *   0x15 : PCIe-C x1 (PHY3)      + USB-B (PHY2) + USB-D (PHY4)
+ *   0x16 : USB-B (PHY2) + USB-C (PHY3) + PCIe D x1 (PHY4)
+ *   0x17 : USB-B (PHY2) + USB-C (PHY3) + USB-D (PHY4)
+ *
+ * So any USB Port B/C/D operation requires PCIe A X8 mode to be disabled.
+ */
+#define PMUA_PCIE_SUBSYS_MGMT		0x1d8
+#define PU_MATRIX_CONF_MASK		GENMASK(4, 0)
+
+#define COMBPHY_MAX_SUBPHYS		6
+
+struct k3_comb_phy {
+	struct device *dev;
+	struct k3_lane_group groups[COMBPHY_MAX_SUBPHYS];
+	void __iomem *base;
+	struct regmap *apb_spare;
+};
+
+static const struct k3_phy_lane_group_data k3_combphy_lane_group0 = {
+	.lanes		= 2,
+	.config		= 0x00,
+	.mask		= 0xff,
+	.offsets	= {
+		0x0, 0x400
+	},
+};
+
+static const struct k3_phy_lane_group_data k3_combphy_lane_group1 = {
+	.lanes		= 2,
+	.config		= 0x00,
+	.mask		= 0xff,
+	.offsets	= {
+		0x100000, 0x100400
+	},
+};
+
+static const struct k3_phy_lane_group_data k3_combphy_lane_group2 = {
+	.lanes		= 1,
+	.config		= 0x14,
+	.mask		= 0x14,
+	.offsets	= {
+		0x200000
+	},
+};
+
+static const struct k3_phy_lane_group_data k3_combphy_lane_group3 = {
+	.lanes		= 1,
+	.config		= 0x12,
+	.mask		= 0x12,
+	.offsets	= {
+		0x300000
+	},
+};
+
+static const struct k3_phy_lane_group_data k3_combphy_lane_group4 = {
+	.lanes		= 1,
+	.config		= 0x11,
+	.mask		= 0x11,
+	.offsets	= {
+		0x400000
+	},
+};
+
+static const struct k3_phy_lane_group_data k3_combphy_lane_group5 = {
+	.lanes		= 1,
+	.config		= 0x00,
+	.mask		= 0xff,
+	.offsets	= {
+		0x500000
+	},
+};
+
+static const struct k3_phy_lane_group_data *k3_combphy_lane_datas[] = {
+	&k3_combphy_lane_group0,
+	&k3_combphy_lane_group1,
+	&k3_combphy_lane_group2,
+	&k3_combphy_lane_group3,
+	&k3_combphy_lane_group4,
+	&k3_combphy_lane_group5,
+};
+
+static int k3_comb_phy_init_lanes(struct k3_comb_phy *phy, unsigned int config)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(k3_combphy_lane_datas); i++) {
+		const struct k3_phy_lane_group_data *data = k3_combphy_lane_datas[i];
+		struct k3_lane_group *lg = &phy->groups[i];
+		const struct phy_ops *ops;
+		bool is_usb;
+
+		is_usb = (data->mask & config) == data->config;
+		if (is_usb)
+			ops = &k3_usb3_phy_ops;
+		else
+			ops = &k3_pcie_phy_ops;
+
+		lg->phy = devm_phy_create(phy->dev, NULL, ops);
+		if (IS_ERR(lg->phy))
+			return PTR_ERR(lg->phy);
+
+		lg->is_pcie = !is_usb;
+		lg->data = data;
+		lg->base = phy->base;
+		phy_set_drvdata(lg->phy, lg);
+	}
+
+	return 0;
+}
+
+static int k3_comb_phy_update_config(struct regmap *apmu, unsigned int config)
+{
+	if (config & ~PU_MATRIX_CONF_MASK)
+		return -EINVAL;
+
+	return regmap_update_bits(apmu, PMUA_PCIE_SUBSYS_MGMT, PU_MATRIX_CONF_MASK, config);
+}
+
+static struct phy *k3_comb_phy_xlate(struct device *dev, const struct of_phandle_args *args)
+{
+	struct k3_comb_phy *phy = dev_get_drvdata(dev);
+	struct k3_lane_group *lg;
+
+	if (args->args_count != 2) {
+		dev_err(dev, "Invalid number of arguments\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (args->args[0] >= ARRAY_SIZE(k3_combphy_lane_datas)) {
+		dev_err(dev, "Invalid PHY id\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	lg = &phy->groups[args->args[0]];
+
+	if ((lg->is_pcie && args->args[1] != PHY_TYPE_PCIE) ||
+	    (!lg->is_pcie && args->args[1] != PHY_TYPE_USB3)) {
+		dev_err(dev, "Invalid PHY mode\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return lg->phy;
+}
+
+static int k3_comb_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct phy_provider *provider;
+	struct k3_comb_phy *phy;
+	struct regmap *apmu;
+	u32 config = 0;
+	int ret;
+
+	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	phy->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(phy->base))
+		return PTR_ERR(phy->base);
+
+	phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare");
+	if (IS_ERR(phy->apb_spare))
+		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
+				     "Failed to fine APB SPARE syscon");
+
+	apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config);
+	if (IS_ERR(apmu))
+		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
+				     "Failed to fine APMU syscon");
+
+	ret = k3_comb_phy_update_config(apmu, config);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to set lane configuration");
+
+	phy->dev = dev;
+	platform_set_drvdata(pdev, phy);
+
+	ret = k3_phy_calibrate(phy->apb_spare);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to calibrate phy");
+
+	ret = k3_comb_phy_init_lanes(phy, config);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to init lanes");
+
+	provider = devm_of_phy_provider_register(dev, k3_comb_phy_xlate);
+	if (IS_ERR(provider))
+		return dev_err_probe(dev, PTR_ERR(provider),
+				     "Failed to register provider\n");
+
+	return 0;
+}
+
+static const struct of_device_id k3_comb_phy_of_match[] = {
+	{ .compatible = "spacemit,k3-comb-phy" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, k3_comb_phy_of_match);
+
+static struct platform_driver k3_comb_phy_driver = {
+	.probe = k3_comb_phy_probe,
+	.driver = {
+		.name = "spacemit,k3-comb-phy",
+		.of_match_table = k3_comb_phy_of_match,
+	},
+};
+module_platform_driver(k3_comb_phy_driver);
+
+MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe comb PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/spacemit/phy-k3-common.c b/drivers/phy/spacemit/phy-k3-common.c
new file mode 100644
index 000000000000..77c4b4073b96
--- /dev/null
+++ b/drivers/phy/spacemit/phy-k3-common.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/usb.h>
+
+#include <dt-bindings/phy/phy.h>
+
+#include "phy-k3-common.h"
+
+/* PHY Registers */
+#define PHY_VERSION			0x0
+
+#define PHY_RESET_CFG			0x04
+
+#define PHY_RESET_RXBUF_RST		BIT(0)
+#define PHY_RESET_SOFT_RST_PCS		BIT(1)
+#define PHY_RESET_SOFT_RST_AHB		BIT(2)
+#define PHY_RESET_EN_SD_AFTER_LOCK	BIT(6)
+
+#define PHY_CLK_CFG			0x08
+
+#define PHY_CLK_PLL_READY		BIT(0)
+#define PHY_CLK_TXCLK_INV		BIT(2)
+#define PHY_CLK_RXCLK_EN		BIT(3)
+#define PHY_CLK_TXCLK_EN		BIT(4)
+#define PHY_CLK_PCLK_EN			BIT(5)
+#define PHY_CLK_PIPE_PCLK_EN		BIT(6)
+#define PHY_CLK_REFCLK_FREQ		GENMASK(10, 7)
+#define PHY_CLK_REFCLK_24M		2
+#define PHY_CLK_SW_INIT_DONE		BIT(11)
+#define PHY_CLK_PU_SSC_OUT		BIT(23)
+
+#define PHY_MODE_CFG			0x0C
+
+#define PHY_MODE_PCIE_INT_EN		BIT(0)
+#define PHY_MODE_LFPS_TPERIOD		GENMASK(9, 8)
+#define PHY_MODE_LFPS_TPERIOD_USB	3
+
+#define PHY_PU_SEL			0x40
+
+#define PHY_PU_CFG_STATUS		BIT(9)
+#define PHY_PU_OVRD_STATUS		BIT(10)
+
+#define PHY_PU_CK_REG			0x54
+
+#define PHY_PU_REFCLK_100		BIT(25)
+
+#define PHY_PLL_REG1			0x58
+
+#define PHY_PLL_FREF_SEL		GENMASK(15, 13)
+#define PHY_PLL_FREF_24M		0x1
+#define PHY_PLL_SSC_DEP_SEL		GENMASK(27, 24)
+#define PHY_PLL_SSC_5000PPM		0xa
+#define PHY_PLL_SSC_MODE		GENMASK(29, 28)
+#define PHY_PLL_SSC_MODE_CENTER_SPREAD	0
+#define PHY_PLL_SSC_MODE_UP_SPREAD	1
+#define PHY_PLL_SSC_MODE_DOWN_SPREAD	2
+#define PHY_PLL_SSC_MODE_DOWN_SPREAD1	3
+
+#define PHY_PLL_REG2			0x5c
+
+#define PHY_PLL_SEL_REF100		BIT(21)
+
+/* PHY RX Register Definitions */
+#define PHY_RX_REG_A			0x60
+
+#define PHY_RX_REG0_RLOAD		BIT(4)
+#define PHY_RX_REG1_RTERM		GENMASK(11, 8)
+#define PHY_RX_REG1_RC_CALI		GENMASK(15, 12)
+#define PHY_RX_REG2_CSEL		GENMASK(19, 16)
+#define PHY_RX_REG2_FORCE_CSEL		BIT(20)
+#define PHY_RX_REG2_PSEL		GENMASK(23, 21)
+#define PHY_RX_REG3_I_LOAD		GENMASK(26, 24)
+#define PHY_RX_REG3_SEL_CBOOST_CODE	BIT(27)
+#define PHY_RX_REG3_ADJ_BIAS		GENMASK(29, 28)
+#define PHY_RX_REG3_RDEG1		GENMASK(31, 30)
+
+#define PHY_RX_REG_B			0x64
+
+#define PHY_RX_REGB_MASK		GENMASK(23, 0)
+
+#define PHY_RX_REG4_RDEG2		GENMASK(2, 1)
+#define PHY_RX_REG4_ENVOS		BIT(4)
+#define PHY_RX_REG4_RTERM_SEL		BIT(5)
+#define PHY_RX_REG4_MANUAL_CFG		BIT(7)
+#define PHY_RX_REG5_RCELL_VCM		GENMASK(11, 8)
+#define PHY_RX_REG5_RCELL_BIAS		GENMASK(15, 12)
+#define PHY_RX_REG6_H1_REG		GENMASK(19, 16)
+#define PHY_RX_REG6_ADAPT_GAIN		GENMASK(21, 20)
+#define PHY_RX_REG6_BYPASS_ADPT		BIT(22)
+
+#define PHY_ADPT_CFG0			0x140
+#define PHY_ADPT_AFE_RST_OVRD_EN	BIT(1)
+#define PHY_ADPT_AFE_RST_OVRD_VAL	BIT(4)
+
+#define PHY_RXEQ_TIME			0xb4
+#define PHY_RXEQ_TIME_OVRD_POST_C_SOC	BIT(21)
+#define PHY_RXEQ_TIME_CFG_AMP_SOC	GENMASK(23, 22)
+#define PHY_RXEQ_TIME_AMP_SOC_650M	0
+#define PHY_RXEQ_TIME_AMP_SOC_800M	1
+#define PHY_RXEQ_TIME_AMP_SOC_870M	2
+#define PHY_RXEQ_TIME_AMP_SOC_900M	3
+#define PHY_RXEQ_TIME_OVRD_AMP_SOC	BIT(24)
+
+#define PCIE_PU_ADDR_CLK_CFG		0x0008
+#define PHY_CLK_PLL_READY		BIT(0)
+#define PCIE_INITAL_TIMER		GENMASK(6, 3)
+#define CFG_INTERNAL_TIMER_ADJ		GENMASK(10, 7)
+#define CFG_SW_PHY_INIT_DONE		BIT(11)
+
+/* Lane RX/TX configuration (per‑lane, at lane_base) */
+#define PCIE_RX_REG1			0x050
+#define PCIE_TX_REG1			0x064
+
+#define PCIE_PLL_TIMEOUT		500000
+#define PCIE_POLL_DELAY			500
+
+static int k3_usb3phy_init_single(struct k3_lane_group *lg, void __iomem *base)
+{
+	struct phy *phy = lg->phy;
+	u32 val, tmp;
+	int ret;
+
+	val = readl(base + PHY_PU_SEL);
+	val = u32_replace_bits(val, 0, PHY_PU_CFG_STATUS);
+	val |= PHY_PU_OVRD_STATUS;
+	writel(val, base + PHY_PU_SEL);
+
+	udelay(200);
+
+	/* Do not wait CDR lock before sampling data */
+	val = readl(base + PHY_RESET_CFG);
+	val = u32_replace_bits(val, 0, PHY_RESET_EN_SD_AFTER_LOCK);
+	writel(val, base + PHY_RESET_CFG);
+
+	/* Power down 100MHz refclk buffer */
+	val = readl(base + PHY_PU_CK_REG);
+	val = u32_replace_bits(val, 0, PHY_PU_REFCLK_100);
+	writel(val, base + PHY_PU_CK_REG);
+
+	/* Program PLL REG1 configure the SSC */
+	val = FIELD_PREP(PHY_PLL_SSC_MODE, PHY_PLL_SSC_MODE_DOWN_SPREAD1) |
+	      FIELD_PREP(PHY_PLL_SSC_DEP_SEL, PHY_PLL_SSC_5000PPM) |
+	      FIELD_PREP(PHY_PLL_FREF_SEL, PHY_PLL_FREF_24M);
+	writel(val, base + PHY_PLL_REG1);
+
+	/* Un-select 100MHz PLL reference */
+	val = readl(base + PHY_PLL_REG2);
+	val = u32_replace_bits(val, 0, PHY_PLL_SEL_REF100);
+	writel(val, base + PHY_PLL_REG2);
+
+	/* USB LFPS period configuration */
+	val = readl(base + PHY_MODE_CFG);
+	val = u32_replace_bits(val, PHY_MODE_LFPS_TPERIOD_USB, PHY_MODE_LFPS_TPERIOD);
+	writel(val, base + PHY_MODE_CFG);
+
+	/* Force AFE adaptation reset */
+	val = readl(base + PHY_ADPT_CFG0);
+	val |= PHY_ADPT_AFE_RST_OVRD_EN | PHY_ADPT_AFE_RST_OVRD_VAL;
+	writel(val, base + PHY_ADPT_CFG0);
+
+	/* Override driver amplitude value to 900m */
+	val = readl(base + PHY_RXEQ_TIME);
+	val |= PHY_RXEQ_TIME_OVRD_AMP_SOC;
+	val = u32_replace_bits(val, PHY_RXEQ_TIME_AMP_SOC_900M, PHY_RXEQ_TIME_CFG_AMP_SOC);
+	writel(val, base + PHY_RXEQ_TIME);
+
+	/* Configure RX parameters */
+	val = PHY_RX_REG0_RLOAD |
+		FIELD_PREP(PHY_RX_REG1_RTERM, 0x8) |
+		FIELD_PREP(PHY_RX_REG1_RC_CALI, 0x7) |
+		FIELD_PREP(PHY_RX_REG2_CSEL, 0x8) |
+		PHY_RX_REG2_FORCE_CSEL |
+		FIELD_PREP(PHY_RX_REG2_PSEL, 0x4) |
+		FIELD_PREP(PHY_RX_REG3_I_LOAD, 0x7) |
+		PHY_RX_REG3_SEL_CBOOST_CODE |
+		FIELD_PREP(PHY_RX_REG3_ADJ_BIAS, 0x1) |
+		FIELD_PREP(PHY_RX_REG3_RDEG1, 0x3);
+	writel(val, base + PHY_RX_REG_A);
+
+	val = readl(base + PHY_RX_REG_B);
+	tmp = FIELD_PREP(PHY_RX_REG4_RDEG2, 0x2) |
+		PHY_RX_REG4_ENVOS | PHY_RX_REG4_RTERM_SEL | PHY_RX_REG4_MANUAL_CFG |
+		FIELD_PREP(PHY_RX_REG5_RCELL_VCM, 0x8) |
+		FIELD_PREP(PHY_RX_REG5_RCELL_BIAS, 0x8) |
+		FIELD_PREP(PHY_RX_REG6_H1_REG, 0x8) |
+		FIELD_PREP(PHY_RX_REG6_ADAPT_GAIN, 0x2);
+	val = u32_replace_bits(val, tmp, PHY_RX_REGB_MASK);
+	writel(val, base + PHY_RX_REG_B);
+
+	/*
+	 * Inform PHY that all PLL-related configuration is done.
+	 * PLL will not start locking until PHY_CLK_SW_INIT_DONE is set.
+	 */
+	val = PHY_CLK_SW_INIT_DONE | PHY_CLK_PU_SSC_OUT |
+	      FIELD_PREP(PHY_CLK_REFCLK_FREQ, PHY_CLK_REFCLK_24M) |
+	      PHY_CLK_RXCLK_EN | PHY_CLK_TXCLK_EN |
+	      PHY_CLK_PCLK_EN | PHY_CLK_PIPE_PCLK_EN;
+	writel(val, base + PHY_CLK_CFG);
+
+	ret = readl_poll_timeout(base + PHY_CLK_CFG, val,
+				 (val & PHY_CLK_PLL_READY),
+				 PCIE_POLL_DELAY, PCIE_PLL_TIMEOUT);
+	if (ret) {
+		dev_err(&phy->dev, "PHY PLL polling timeout\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int k3_usb3phy_init(struct phy *phy)
+{
+	struct k3_lane_group *lg = phy_get_drvdata(phy);
+	int ret, i;
+
+	for (i = 0; i < lg->data->lanes; i++) {
+		ret = k3_usb3phy_init_single(lg, lg->base + lg->data->offsets[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int k3_usb3phy_set_speed(struct phy *phy, int speed)
+{
+	struct k3_lane_group *lg = phy_get_drvdata(phy);
+	void __iomem *base = lg->base + lg->data->offsets[0];
+	u32 val;
+
+	if (speed == USB_SPEED_HIGH) {
+		val = readl(base + PHY_PU_SEL);
+		val = u32_replace_bits(val, 0, PHY_PU_CFG_STATUS);
+		val |= PHY_PU_OVRD_STATUS;
+		writel(val, base + PHY_PU_SEL);
+
+		udelay(200);
+	}
+
+	return 0;
+}
+
+const struct phy_ops k3_usb3_phy_ops = {
+	.init = k3_usb3phy_init,
+	.set_speed = k3_usb3phy_set_speed,
+	.owner = THIS_MODULE,
+};
+EXPORT_SYMBOL_GPL(k3_usb3_phy_ops);
+
+static int k3_pcie_phy_init(struct phy *phy)
+{
+	struct k3_lane_group *lg = phy_get_drvdata(phy);
+	void __iomem *phy_base = lg->base + lg->data->offsets[0];
+	u32 val;
+	int ret;
+	int i;
+
+	val = readl(phy_base + PHY_PLL_REG1);
+	val = u32_replace_bits(val, 0x2, GENMASK(15, 12));
+	writel(val, phy_base + PHY_PLL_REG1);
+
+	val = readl(phy_base + PHY_PLL_REG2);
+	val = u32_replace_bits(val, 0, BIT(21));
+	writel(val, phy_base + PHY_PLL_REG2);
+
+	for (i = 0; i < lg->data->lanes; i++) {
+		void __iomem *lane_base = lg->base + lg->data->offsets[i];
+
+		val = readl(lane_base + PCIE_RX_REG1);
+		val = u32_replace_bits(val, 0, 0x3);
+		writel(val, phy_base + PCIE_RX_REG1);
+	}
+
+	val = readl(phy_base + PHY_PLL_REG2);
+	val |= BIT(20);
+	writel(val, phy_base + PHY_PLL_REG2);
+
+	writel(0x00006505, phy_base + PCIE_RX_REG1);
+
+	/* pll_reg1 of lane0, disable SSC: pll_reg4[3:0] = 0 */
+	val = readl(phy_base + PHY_PLL_REG1);
+	val = u32_replace_bits(val, 0, GENMASK(27, 24));
+	writel(val, phy_base + PHY_PLL_REG1);
+
+	for (i = 0; i < lg->data->lanes; i++) {
+		void __iomem *lane_base = lg->base + lg->data->offsets[i];
+
+		/* set cfg_tx_send_dummy_data to be 1'b1 for disable dash data */
+		val = readl(lane_base + PHY_PU_SEL);
+		val = u32_replace_bits(val, 1, BIT(13));
+		writel(val, lane_base + PHY_PU_SEL);
+
+		/* disable en_sample_data_after_cdr_locked */
+		val = readl(lane_base + PHY_RESET_CFG);
+		val = u32_replace_bits(val, 0, BIT(6));
+		writel(val, lane_base + PHY_RESET_CFG);
+
+		/* Dynamic Lock */
+		val = readl(lane_base + PHY_MODE_CFG);
+		val = u32_replace_bits(val, 1, BIT(2));
+		writel(val, lane_base + PHY_MODE_CFG);
+
+		val = FIELD_PREP(GENMASK(7, 0), 0x10) |
+			FIELD_PREP(GENMASK(15, 8), 0x78) |
+			FIELD_PREP(GENMASK(23, 16), 0x98) |
+			FIELD_PREP(GENMASK(31, 24), 0xdf);
+		writel(val, lane_base + PHY_RX_REG_A);
+
+		val = readl(lane_base + PHY_RX_REG_B);
+		val &= ~PHY_RX_REGB_MASK;
+		val |= FIELD_PREP(GENMASK(7, 0), 0xb4) |
+			FIELD_PREP(GENMASK(15, 8), 0x88) |
+			FIELD_PREP(GENMASK(23, 16), 0x28);
+		writel(val, lane_base + PHY_RX_REG_B);
+
+		/* Set init done */
+		val = readl(lane_base + PCIE_PU_ADDR_CLK_CFG);
+		val = u32_replace_bits(val, 1, CFG_SW_PHY_INIT_DONE);
+		writel(val, lane_base + PCIE_PU_ADDR_CLK_CFG);
+	}
+
+	ret = readl_poll_timeout(phy_base + PCIE_PU_ADDR_CLK_CFG, val,
+				 (val & PHY_CLK_PLL_READY), PCIE_POLL_DELAY,
+				 PCIE_PLL_TIMEOUT);
+	if (ret) {
+		dev_err(&lg->phy->dev, "PHY PLL lock timeout\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+const struct phy_ops k3_pcie_phy_ops = {
+	.init		= k3_pcie_phy_init,
+	.owner		= THIS_MODULE,
+};
+EXPORT_SYMBOL_GPL(k3_pcie_phy_ops);
+
+/* PHY rcal init requires APB_SPARE regmap access */
+
+#define APB_SPARE_PU_CAL		0x178
+#define PU_CAL				BIT(17)
+
+#define APB_SPARE_RCAL_HSIO		0x17c
+#define APB_SPARE_PU_CAL_DONE		BIT(8)
+#define RCAL_OVRD_PTRIM			GENMASK(23, 20)
+#define RCAL_OVRD_NTRIM			GENMASK(27, 24)
+#define RCAL_OVRD_PTRIM_EN		BIT(28)
+#define RCAL_OVRD_NTRIM_EN		BIT(29)
+#define RCAL_OVRD_STABLE_VAL		BIT(30)
+#define RCAL_OVRD_STABLE_EN		BIT(31)
+
+#define RCAL_OVRD_TRIM_EN		(RCAL_OVRD_NTRIM_EN | RCAL_OVRD_PTRIM_EN)
+#define RCAL_OVRD_TRIM_MASK		(RCAL_OVRD_NTRIM | RCAL_OVRD_PTRIM)
+
+#define PU_CAL_TIMEOUT 2000000
+
+static DEFINE_MUTEX(calibrate_lock);
+
+int k3_phy_calibrate(struct regmap *apb_spare)
+{
+	unsigned int val;
+	int ret;
+
+	guard(mutex)(&calibrate_lock);
+
+	regmap_read(apb_spare, APB_SPARE_RCAL_HSIO, &val);
+	if (val & APB_SPARE_PU_CAL_DONE)
+		return 0;
+
+	regmap_update_bits(apb_spare, APB_SPARE_PU_CAL, PU_CAL,
+			   PU_CAL);
+
+	ret = regmap_read_poll_timeout(apb_spare, APB_SPARE_RCAL_HSIO,
+				       val, (val & APB_SPARE_PU_CAL_DONE), PCIE_POLL_DELAY,
+				       PU_CAL_TIMEOUT);
+
+	if (ret)
+		regmap_update_bits(apb_spare, APB_SPARE_RCAL_HSIO,
+				   RCAL_OVRD_TRIM_EN | RCAL_OVRD_STABLE_VAL |
+				   RCAL_OVRD_TRIM_MASK | RCAL_OVRD_STABLE_EN,
+				   RCAL_OVRD_TRIM_EN | RCAL_OVRD_STABLE_VAL |
+				   FIELD_PREP(RCAL_OVRD_NTRIM, 0x6) |
+				   FIELD_PREP(RCAL_OVRD_PTRIM, 0xa) |
+				   RCAL_OVRD_STABLE_EN);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(k3_phy_calibrate);
+
+MODULE_DESCRIPTION("SpacemiT K3 PHY common ops");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/spacemit/phy-k3-common.h b/drivers/phy/spacemit/phy-k3-common.h
new file mode 100644
index 000000000000..49009c3c313a
--- /dev/null
+++ b/drivers/phy/spacemit/phy-k3-common.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PHY_K3_COMMON_H
+#define _PHY_K3_COMMON_H
+
+#include <linux/phy/phy.h>
+
+struct k3_phy_lane_group_data {
+	u32 lanes;
+	u8 config;
+	u8 mask;
+	u32 offsets[] __counted_by(lanes);
+};
+
+struct k3_lane_group {
+	const struct k3_phy_lane_group_data *data;
+	void __iomem *base;
+	struct phy *phy;
+	bool is_pcie;
+};
+
+extern const struct phy_ops k3_pcie_phy_ops;
+extern const struct phy_ops k3_usb3_phy_ops;
+
+int k3_phy_calibrate(struct regmap *apb_spare);
+
+#endif
-- 
2.54.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related

* Re: [PATCH 2/2] phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3
From: Ze Huang @ 2026-04-30  7:39 UTC (permalink / raw)
  To: Inochi Amaoto, Vinod Koul, Neil Armstrong, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Yixun Lan, Kees Cook,
	Gustavo A. R. Silva, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Ze Huang, Alex Elder
  Cc: linux-phy, devicetree, linux-riscv, spacemit, linux-kernel,
	linux-hardening, Yixun Lan, Longbin Li
In-Reply-To: <20260430022843.1090138-3-inochiama@gmail.com>

On Thu Apr 30, 2026 at 10:28 AM CST, Inochi Amaoto wrote:
> The comb PHY on K3 requires to configure a syscon device for the
> right mux configuration. And it requires calibration before any
> usage.
>
> Add USB3/PCIe comb PHY driver for Spacemit K3.
>
> Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
> ---
>  drivers/phy/spacemit/Kconfig          |  16 ++
>  drivers/phy/spacemit/Makefile         |   2 +
>  drivers/phy/spacemit/phy-k3-combphy.c | 250 ++++++++++++++++
>  drivers/phy/spacemit/phy-k3-common.c  | 398 ++++++++++++++++++++++++++
>  drivers/phy/spacemit/phy-k3-common.h  |  27 ++
>  5 files changed, 693 insertions(+)
>  create mode 100644 drivers/phy/spacemit/phy-k3-combphy.c
>  create mode 100644 drivers/phy/spacemit/phy-k3-common.c
>  create mode 100644 drivers/phy/spacemit/phy-k3-common.h
>
> diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
> index 50b0005acf66..5fdf18fce499 100644
> --- a/drivers/phy/spacemit/Kconfig
> +++ b/drivers/phy/spacemit/Kconfig
> @@ -23,3 +23,19 @@ config PHY_SPACEMIT_K1_USB2
>  	help
>  	  Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
>  	  enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.
> +
> +config PHY_SPACEMIT_K3_COMMON_OPS
> +	tristate
> +	select MFD_SYSCON
> +	select GENERIC_PHY
> +
> +config PHY_SPACEMIT_K3_COMBO_PHY
> +	tristate "SpacemiT K3 USB3/PCIe PHY support"
> +	depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
> +	depends on COMMON_CLK
> +	select PHY_SPACEMIT_K3_COMMON_OPS
> +	help
> +	  Enable this to support K3 USB3/PCIe combo PHY driver. This
> +	  driver takes care of enabling and clock setup and will be used
> +	  by K3 dwc3 driver.
> +	  If unsure, say N.
> diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
> index a821a21d6142..41be7b0388da 100644
> --- a/drivers/phy/spacemit/Makefile
> +++ b/drivers/phy/spacemit/Makefile
> @@ -1,3 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE)		+= phy-k1-pcie.o
>  obj-$(CONFIG_PHY_SPACEMIT_K1_USB2)		+= phy-k1-usb2.o
> +obj-$(CONFIG_PHY_SPACEMIT_K3_COMBO_PHY)		+= phy-k3-combphy.o
> +obj-$(CONFIG_PHY_SPACEMIT_K3_COMMON_OPS)	+= phy-k3-common.o
> diff --git a/drivers/phy/spacemit/phy-k3-combphy.c b/drivers/phy/spacemit/phy-k3-combphy.c
> new file mode 100644
> index 000000000000..66fa6330ad6e
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k3-combphy.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * phy-k3-usb3.c - SpacemiT K3 Type-C Orientation Switch Driver
> + *
> + * Copyright (c) 2025 SpacemiT Technology Co. Ltd
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/io.h>

...

> +
> +	phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare");
> +	if (IS_ERR(phy->apb_spare))
> +		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
> +				     "Failed to fine APB SPARE syscon");

typo, s/fine/find

> +
> +	apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config);
> +	if (IS_ERR(apmu))
> +		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
> +				     "Failed to fine APMU syscon");

1. typo, s/fine/find
2. PTR_ERR(phy->apb_spare) should be PTR_ERR(apmu)

> +
> +	ret = k3_comb_phy_update_config(apmu, config);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to set lane configuration");
> +
> +	phy->dev = dev;
> +	platform_set_drvdata(pdev, phy);
> +
> +	ret = k3_phy_calibrate(phy->apb_spare);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to calibrate phy");
> +
> +	ret = k3_comb_phy_init_lanes(phy, config);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to init lanes");
> +
> +	provider = devm_of_phy_provider_register(dev, k3_comb_phy_xlate);
> +	if (IS_ERR(provider))
> +		return dev_err_probe(dev, PTR_ERR(provider),
> +				     "Failed to register provider\n");
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id k3_comb_phy_of_match[] = {
> +	{ .compatible = "spacemit,k3-comb-phy" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, k3_comb_phy_of_match);
> +
> +static struct platform_driver k3_comb_phy_driver = {
> +	.probe = k3_comb_phy_probe,
> +	.driver = {
> +		.name = "spacemit,k3-comb-phy",
> +		.of_match_table = k3_comb_phy_of_match,
> +	},
> +};
> +module_platform_driver(k3_comb_phy_driver);
> +
> +MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe comb PHY driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/phy/spacemit/phy-k3-common.c b/drivers/phy/spacemit/phy-k3-common.c
> new file mode 100644
> index 000000000000..77c4b4073b96
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k3-common.c
> @@ -0,0 +1,398 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/bitfield.h>
> +#include <linux/cleanup.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/usb.h>
> +
> +#include <dt-bindings/phy/phy.h>
> +
> +#include "phy-k3-common.h"
> +
> +/* PHY Registers */
> +#define PHY_VERSION			0x0
> +
> +#define PHY_RESET_CFG			0x04
> +
> +#define PHY_RESET_RXBUF_RST		BIT(0)
> +#define PHY_RESET_SOFT_RST_PCS		BIT(1)
> +#define PHY_RESET_SOFT_RST_AHB		BIT(2)
> +#define PHY_RESET_EN_SD_AFTER_LOCK	BIT(6)
> +
> +#define PHY_CLK_CFG			0x08
> +
> +#define PHY_CLK_PLL_READY		BIT(0)
> +#define PHY_CLK_TXCLK_INV		BIT(2)
> +#define PHY_CLK_RXCLK_EN		BIT(3)
> +#define PHY_CLK_TXCLK_EN		BIT(4)
> +#define PHY_CLK_PCLK_EN			BIT(5)
> +#define PHY_CLK_PIPE_PCLK_EN		BIT(6)
> +#define PHY_CLK_REFCLK_FREQ		GENMASK(10, 7)
> +#define PHY_CLK_REFCLK_24M		2
> +#define PHY_CLK_SW_INIT_DONE		BIT(11)
> +#define PHY_CLK_PU_SSC_OUT		BIT(23)
> +
> +#define PHY_MODE_CFG			0x0C
> +
> +#define PHY_MODE_PCIE_INT_EN		BIT(0)
> +#define PHY_MODE_LFPS_TPERIOD		GENMASK(9, 8)
> +#define PHY_MODE_LFPS_TPERIOD_USB	3
> +
> +#define PHY_PU_SEL			0x40
> +
> +#define PHY_PU_CFG_STATUS		BIT(9)
> +#define PHY_PU_OVRD_STATUS		BIT(10)
> +
> +#define PHY_PU_CK_REG			0x54
> +
> +#define PHY_PU_REFCLK_100		BIT(25)
> +
> +#define PHY_PLL_REG1			0x58
> +
> +#define PHY_PLL_FREF_SEL		GENMASK(15, 13)
> +#define PHY_PLL_FREF_24M		0x1
> +#define PHY_PLL_SSC_DEP_SEL		GENMASK(27, 24)
> +#define PHY_PLL_SSC_5000PPM		0xa
> +#define PHY_PLL_SSC_MODE		GENMASK(29, 28)
> +#define PHY_PLL_SSC_MODE_CENTER_SPREAD	0
> +#define PHY_PLL_SSC_MODE_UP_SPREAD	1
> +#define PHY_PLL_SSC_MODE_DOWN_SPREAD	2
> +#define PHY_PLL_SSC_MODE_DOWN_SPREAD1	3
> +
> +#define PHY_PLL_REG2			0x5c
> +
> +#define PHY_PLL_SEL_REF100		BIT(21)
> +
> +/* PHY RX Register Definitions */
> +#define PHY_RX_REG_A			0x60
> +
> +#define PHY_RX_REG0_RLOAD		BIT(4)
> +#define PHY_RX_REG1_RTERM		GENMASK(11, 8)
> +#define PHY_RX_REG1_RC_CALI		GENMASK(15, 12)
> +#define PHY_RX_REG2_CSEL		GENMASK(19, 16)
> +#define PHY_RX_REG2_FORCE_CSEL		BIT(20)
> +#define PHY_RX_REG2_PSEL		GENMASK(23, 21)
> +#define PHY_RX_REG3_I_LOAD		GENMASK(26, 24)
> +#define PHY_RX_REG3_SEL_CBOOST_CODE	BIT(27)
> +#define PHY_RX_REG3_ADJ_BIAS		GENMASK(29, 28)
> +#define PHY_RX_REG3_RDEG1		GENMASK(31, 30)
> +
> +#define PHY_RX_REG_B			0x64
> +
> +#define PHY_RX_REGB_MASK		GENMASK(23, 0)
> +
> +#define PHY_RX_REG4_RDEG2		GENMASK(2, 1)
> +#define PHY_RX_REG4_ENVOS		BIT(4)
> +#define PHY_RX_REG4_RTERM_SEL		BIT(5)
> +#define PHY_RX_REG4_MANUAL_CFG		BIT(7)
> +#define PHY_RX_REG5_RCELL_VCM		GENMASK(11, 8)
> +#define PHY_RX_REG5_RCELL_BIAS		GENMASK(15, 12)
> +#define PHY_RX_REG6_H1_REG		GENMASK(19, 16)
> +#define PHY_RX_REG6_ADAPT_GAIN		GENMASK(21, 20)
> +#define PHY_RX_REG6_BYPASS_ADPT		BIT(22)
> +
> +#define PHY_ADPT_CFG0			0x140
> +#define PHY_ADPT_AFE_RST_OVRD_EN	BIT(1)
> +#define PHY_ADPT_AFE_RST_OVRD_VAL	BIT(4)
> +
> +#define PHY_RXEQ_TIME			0xb4
> +#define PHY_RXEQ_TIME_OVRD_POST_C_SOC	BIT(21)
> +#define PHY_RXEQ_TIME_CFG_AMP_SOC	GENMASK(23, 22)
> +#define PHY_RXEQ_TIME_AMP_SOC_650M	0
> +#define PHY_RXEQ_TIME_AMP_SOC_800M	1
> +#define PHY_RXEQ_TIME_AMP_SOC_870M	2
> +#define PHY_RXEQ_TIME_AMP_SOC_900M	3
> +#define PHY_RXEQ_TIME_OVRD_AMP_SOC	BIT(24)
> +
> +#define PCIE_PU_ADDR_CLK_CFG		0x0008
> +#define PHY_CLK_PLL_READY		BIT(0)
> +#define PCIE_INITAL_TIMER		GENMASK(6, 3)
> +#define CFG_INTERNAL_TIMER_ADJ		GENMASK(10, 7)
> +#define CFG_SW_PHY_INIT_DONE		BIT(11)
> +
> +/* Lane RX/TX configuration (per‑lane, at lane_base) */
> +#define PCIE_RX_REG1			0x050
> +#define PCIE_TX_REG1			0x064
> +
> +#define PCIE_PLL_TIMEOUT		500000
> +#define PCIE_POLL_DELAY			500
> +
> +

...

> +static int k3_pcie_phy_init(struct phy *phy)
> +{
> +	struct k3_lane_group *lg = phy_get_drvdata(phy);
> +	void __iomem *phy_base = lg->base + lg->data->offsets[0];
> +	u32 val;
> +	int ret;
> +	int i;
> +
> +	val = readl(phy_base + PHY_PLL_REG1);
> +	val = u32_replace_bits(val, 0x2, GENMASK(15, 12));
> +	writel(val, phy_base + PHY_PLL_REG1);
> +
> +	val = readl(phy_base + PHY_PLL_REG2);
> +	val = u32_replace_bits(val, 0, BIT(21));
> +	writel(val, phy_base + PHY_PLL_REG2);
> +
> +	for (i = 0; i < lg->data->lanes; i++) {
> +		void __iomem *lane_base = lg->base + lg->data->offsets[i];
> +

> +		val = readl(lane_base + PCIE_RX_REG1);
> +		val = u32_replace_bits(val, 0, 0x3);
> +		writel(val, phy_base + PCIE_RX_REG1);

This looks like a copy-paste bug.

Read from lane_base but write the modified value to phy_base.

> +	}
> +
> +	val = readl(phy_base + PHY_PLL_REG2);
> +	val |= BIT(20);
> +	writel(val, phy_base + PHY_PLL_REG2);
> +

> +	writel(0x00006505, phy_base + PCIE_RX_REG1);

Is it intentional? The loop above configured PCIE_RX_REG1, while the
hard-coded 0x00006505 overwrites what's done for lane0.

> +
> +	/* pll_reg1 of lane0, disable SSC: pll_reg4[3:0] = 0 */
> +	val = readl(phy_base + PHY_PLL_REG1);
> +	val = u32_replace_bits(val, 0, GENMASK(27, 24));
> +	writel(val, phy_base + PHY_PLL_REG1);

A little confusing here, comment says "pll_reg4[3:0] = 0" but the code is
modifying PHY_PLL_REG1[27:24]

> +
> +	for (i = 0; i < lg->data->lanes; i++) {
> +		void __iomem *lane_base = lg->base + lg->data->offsets[i];
> +
> +		/* set cfg_tx_send_dummy_data to be 1'b1 for disable dash data */
> +		val = readl(lane_base + PHY_PU_SEL);
> +		val = u32_replace_bits(val, 1, BIT(13));
> +		writel(val, lane_base + PHY_PU_SEL);
> +
> +		/* disable en_sample_data_after_cdr_locked */
> +		val = readl(lane_base + PHY_RESET_CFG);
> +		val = u32_replace_bits(val, 0, BIT(6));
> +		writel(val, lane_base + PHY_RESET_CFG);
> +
> +		/* Dynamic Lock */
> +		val = readl(lane_base + PHY_MODE_CFG);
> +		val = u32_replace_bits(val, 1, BIT(2));
> +		writel(val, lane_base + PHY_MODE_CFG);
> +
> +		val = FIELD_PREP(GENMASK(7, 0), 0x10) |
> +			FIELD_PREP(GENMASK(15, 8), 0x78) |
> +			FIELD_PREP(GENMASK(23, 16), 0x98) |
> +			FIELD_PREP(GENMASK(31, 24), 0xdf);
> +		writel(val, lane_base + PHY_RX_REG_A);
> +
> +		val = readl(lane_base + PHY_RX_REG_B);
> +		val &= ~PHY_RX_REGB_MASK;
> +		val |= FIELD_PREP(GENMASK(7, 0), 0xb4) |
> +			FIELD_PREP(GENMASK(15, 8), 0x88) |
> +			FIELD_PREP(GENMASK(23, 16), 0x28);
> +		writel(val, lane_base + PHY_RX_REG_B);

Can we define macros for these values? Just like you did for
PHY_CLK_CFG.

> +
> +		/* Set init done */
> +		val = readl(lane_base + PCIE_PU_ADDR_CLK_CFG);
> +		val = u32_replace_bits(val, 1, CFG_SW_PHY_INIT_DONE);
> +		writel(val, lane_base + PCIE_PU_ADDR_CLK_CFG);
> +	}
> +
> +	ret = readl_poll_timeout(phy_base + PCIE_PU_ADDR_CLK_CFG, val,
> +				 (val & PHY_CLK_PLL_READY), PCIE_POLL_DELAY,
> +				 PCIE_PLL_TIMEOUT);
> +	if (ret) {
> +		dev_err(&lg->phy->dev, "PHY PLL lock timeout\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* [RFC PATCH] phy: ti: tusb1210: Move long delayed work on system_dfl_long_wq
From: Marco Crivellari @ 2026-04-30 10:34 UTC (permalink / raw)
  To: linux-kernel, linux-phy
  Cc: Tejun Heo, Lai Jiangshan, Frederic Weisbecker,
	Sebastian Andrzej Siewior, Marco Crivellari, Michal Hocko,
	Vinod Koul, Neil Armstrong

Currently the code enqueue work items using {queue|mod}_delayed_work(),
using system_long_wq. This workqueue should be used when long works are
expected, but it is a per-cpu workqueue.

This is important because queue_delayed_work() queue the work using:

   queue_delayed_work_on(WORK_CPU_UNBOUND, ...);

Note that WORK_CPU_UNBOUND = NR_CPUS.

This would end up calling __queue_delayed_work() that does:

    if (housekeeping_enabled(HK_TYPE_TIMER)) {
    //      [....]
    } else {
            if (likely(cpu == WORK_CPU_UNBOUND))
                    add_timer_global(timer);
            else
                    add_timer_on(timer, cpu);
    }

So when cpu == WORK_CPU_UNBOUND the timer is global and is
not using a specific CPU. Later, when __queue_work() is called:

    if (req_cpu == WORK_CPU_UNBOUND) {
            if (wq->flags & WQ_UNBOUND)
                    cpu = wq_select_unbound_cpu(raw_smp_processor_id());
            else
                    cpu = raw_smp_processor_id();
    }

Because the wq is not unbound, it takes the CPU where the timer
fired and enqueue the work on that CPU.
The consequence of all of this is that the work can run anywhere,
depending on where the timer fired.

Recently, a new unbound workqueue specific for long running work has
been added:

    c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works")

So change system_long_wq with system_dfl_long_wq so that the work may
benefit from scheduler task placement.

Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
---
 drivers/phy/ti/phy-tusb1210.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c
index c3ae9d7948d7..9956921c094b 100644
--- a/drivers/phy/ti/phy-tusb1210.c
+++ b/drivers/phy/ti/phy-tusb1210.c
@@ -197,7 +197,7 @@ static void tusb1210_chg_det_set_state(struct tusb1210 *tusb,
 			tusb1210_chg_det_states[new_state], delay_ms);
 
 	tusb->chg_det_state = new_state;
-	mod_delayed_work(system_long_wq, &tusb->chg_det_work,
+	mod_delayed_work(system_dfl_long_wq, &tusb->chg_det_work,
 			 msecs_to_jiffies(delay_ms));
 }
 
@@ -380,7 +380,7 @@ static int tusb1210_psy_notifier(struct notifier_block *nb,
 	struct power_supply *psy = ptr;
 
 	if (psy != tusb->psy && psy->desc->type == POWER_SUPPLY_TYPE_USB)
-		queue_delayed_work(system_long_wq, &tusb->chg_det_work, 0);
+		queue_delayed_work(system_dfl_long_wq, &tusb->chg_det_work, 0);
 
 	return NOTIFY_OK;
 }
@@ -458,7 +458,7 @@ static void tusb1210_probe_charger_detect(struct tusb1210 *tusb)
 	 */
 	tusb->chg_det_state = TUSB1210_CHG_DET_DISCONNECTED;
 	INIT_DELAYED_WORK(&tusb->chg_det_work, tusb1210_chg_det_work);
-	queue_delayed_work(system_long_wq, &tusb->chg_det_work, 2 * HZ);
+	queue_delayed_work(system_dfl_long_wq, &tusb->chg_det_work, 2 * HZ);
 
 	tusb->psy_nb.notifier_call = tusb1210_psy_notifier;
 	power_supply_reg_notifier(&tusb->psy_nb);
-- 
2.53.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox