rockchip: backport latest pending usbdp fixes
Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
+64
@@ -0,0 +1,64 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 1/35] dt-bindings: phy: rockchip-usbdp: add improved
|
||||
ports scheme
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:02 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-1-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Currently the Rockchip USBDP PHY is missing a documented port scheme.
|
||||
Meanwhile upstream RK3588 DTS files are a bit messy and use different
|
||||
port schemes. The upstream USBDP PHY Linux kernel driver does not yet
|
||||
parse the ports at all and thus does not create any implicit ABI either.
|
||||
|
||||
But with the current mess it is not possible to properly support USB-C
|
||||
DP AltMode. Thus this introduces a proper port scheme following roughly
|
||||
the ports design of the Qualcomm QMP USB4-USB3-DP PHY controller binding
|
||||
with a slight difference that there is an additional port for the
|
||||
USB-C SBU port as the Rockchip USB-DP PHY also contains the SBU mux.
|
||||
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
.../bindings/phy/phy-rockchip-usbdp.yaml | 24 ++++++++++++++++++++++
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
|
||||
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
|
||||
@@ -110,10 +110,34 @@ properties:
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
+ deprecated: true
|
||||
description:
|
||||
A port node to link the PHY to a TypeC controller for the purpose of
|
||||
handling orientation switching.
|
||||
|
||||
+ ports:
|
||||
+ $ref: /schemas/graph.yaml#/properties/ports
|
||||
+ properties:
|
||||
+ port@0:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description:
|
||||
+ Output endpoint of the PHY for USB (or DP when configured into 4 lane
|
||||
+ mode), which should point to the superspeed port of a USB connector.
|
||||
+
|
||||
+ port@1:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Incoming endpoint from the USB controller
|
||||
+
|
||||
+ port@2:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description: Incoming endpoint from the DisplayPort controller
|
||||
+
|
||||
+ port@3:
|
||||
+ $ref: /schemas/graph.yaml#/properties/port
|
||||
+ description:
|
||||
+ Output endpoint of the PHY for DP Auxiliary, which should either point to
|
||||
+ the SBU port of a USB-C connector or a DisplayPort connector input port.
|
||||
+
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
-241
@@ -1,241 +0,0 @@
|
||||
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
To: Vinod Koul <vkoul@kernel.org>,
|
||||
Kishon Vijay Abraham I <kishon@kernel.org>,
|
||||
Rob Herring <robh@kernel.org>,
|
||||
Krzysztof Kozlowski <krzk+dt@kernel.org>,
|
||||
Conor Dooley <conor+dt@kernel.org>,
|
||||
Heiko Stuebner <heiko@sntech.de>,
|
||||
Kever Yang <kever.yang@rock-chips.com>,
|
||||
Frank Wang <frank.wang@rock-chips.com>,
|
||||
Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Cc: devicetree@vger.kernel.org,
|
||||
Sebastian Reichel <sebastian.reichel@collabora.com>,
|
||||
linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org,
|
||||
Alexey Charkov <alchark@gmail.com>,
|
||||
linux-phy@lists.infradead.org, kernel@collabora.com,
|
||||
linux-arm-kernel@lists.infradead.org
|
||||
Subject: [PATCH v5 1/2] phy: rockchip: inno-usb2: add soft vbusvalid control
|
||||
Date: Thu, 19 Jun 2025 20:36:36 +0200 [thread overview]
|
||||
Message-ID: <20250619-rk3576-sige5-usb-v5-1-9069a7e750e1@collabora.com> (raw)
|
||||
In-Reply-To: <20250619-rk3576-sige5-usb-v5-0-9069a7e750e1@collabora.com>
|
||||
|
||||
With USB type C connectors, the vbus detect pin of the OTG controller
|
||||
attached to it is pulled high by a USB Type C controller chip such as
|
||||
the fusb302. This means USB enumeration on Type-C ports never works, as
|
||||
the vbus is always seen as high.
|
||||
|
||||
Rockchip added some GRF register flags to deal with this situation. The
|
||||
RK3576 TRM calls these "soft_vbusvalid_bvalid" (con0 bit index 15) and
|
||||
"soft_vbusvalid_bvalid_sel" (con0 bit index 14).
|
||||
|
||||
Downstream introduces a new vendor property which tells the USB 2 PHY
|
||||
that it's connected to a type C port, but we can do better. Since in
|
||||
such an arrangement, we'll have an OF graph connection from the USB
|
||||
controller to the USB connector anyway, we can walk said OF graph and
|
||||
check the connector's compatible to determine this without adding any
|
||||
further vendor properties.
|
||||
|
||||
Do keep in mind that the usbdp PHY driver seemingly fiddles with these
|
||||
register fields as well, but what it does doesn't appear to be enough
|
||||
for us to get working USB enumeration, presumably because the whole
|
||||
vbus_attach logic needs to be adjusted as well either way.
|
||||
|
||||
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 108 +++++++++++++++++++++++++-
|
||||
1 file changed, 104 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
+#include <linux/of_graph.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
@@ -114,6 +115,8 @@ struct rockchip_chg_det_reg {
|
||||
/**
|
||||
* struct rockchip_usb2phy_port_cfg - usb-phy port configuration.
|
||||
* @phy_sus: phy suspend register.
|
||||
+ * @svbus_en: soft vbus bvalid enable register.
|
||||
+ * @svbus_sel: soft vbus bvalid selection register.
|
||||
* @bvalid_det_en: vbus valid rise detection enable register.
|
||||
* @bvalid_det_st: vbus valid rise detection status register.
|
||||
* @bvalid_det_clr: vbus valid rise detection clear register.
|
||||
@@ -140,6 +143,8 @@ struct rockchip_chg_det_reg {
|
||||
*/
|
||||
struct rockchip_usb2phy_port_cfg {
|
||||
struct usb2phy_reg phy_sus;
|
||||
+ struct usb2phy_reg svbus_en;
|
||||
+ struct usb2phy_reg svbus_sel;
|
||||
struct usb2phy_reg bvalid_det_en;
|
||||
struct usb2phy_reg bvalid_det_st;
|
||||
struct usb2phy_reg bvalid_det_clr;
|
||||
@@ -204,6 +209,7 @@ struct rockchip_usb2phy_cfg {
|
||||
* @event_nb: hold event notification callback.
|
||||
* @state: define OTG enumeration states before device reset.
|
||||
* @mode: the dr_mode of the controller.
|
||||
+ * @typec_vbus_det: whether to apply Type C logic to OTG vbus detection.
|
||||
*/
|
||||
struct rockchip_usb2phy_port {
|
||||
struct phy *phy;
|
||||
@@ -223,6 +229,7 @@ struct rockchip_usb2phy_port {
|
||||
struct notifier_block event_nb;
|
||||
enum usb_otg_state state;
|
||||
enum usb_dr_mode mode;
|
||||
+ bool typec_vbus_det;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -510,6 +517,13 @@ static int rockchip_usb2phy_init(struct
|
||||
mutex_lock(&rport->mutex);
|
||||
|
||||
if (rport->port_id == USB2PHY_PORT_OTG) {
|
||||
+ if (rport->typec_vbus_det) {
|
||||
+ if (rport->port_cfg->svbus_en.enable &&
|
||||
+ rport->port_cfg->svbus_sel.enable) {
|
||||
+ property_enable(rphy->grf, &rport->port_cfg->svbus_en, true);
|
||||
+ property_enable(rphy->grf, &rport->port_cfg->svbus_sel, true);
|
||||
+ }
|
||||
+ }
|
||||
if (rport->mode != USB_DR_MODE_HOST &&
|
||||
rport->mode != USB_DR_MODE_UNKNOWN) {
|
||||
/* clear bvalid status and enable bvalid detect irq */
|
||||
@@ -550,8 +564,7 @@ static int rockchip_usb2phy_init(struct
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
- schedule_delayed_work(&rport->otg_sm_work,
|
||||
- OTG_SCHEDULE_DELAY * 3);
|
||||
+ schedule_delayed_work(&rport->otg_sm_work, 0);
|
||||
} else {
|
||||
/* If OTG works in host only mode, do nothing. */
|
||||
dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode);
|
||||
@@ -679,8 +692,12 @@ static void rockchip_usb2phy_otg_sm_work
|
||||
unsigned long delay;
|
||||
bool vbus_attach, sch_work, notify_charger;
|
||||
|
||||
- vbus_attach = property_enabled(rphy->grf,
|
||||
- &rport->port_cfg->utmi_bvalid);
|
||||
+ if (rport->port_cfg->svbus_en.enable && rport->typec_vbus_det) {
|
||||
+ vbus_attach = true;
|
||||
+ } else {
|
||||
+ vbus_attach = property_enabled(rphy->grf,
|
||||
+ &rport->port_cfg->utmi_bvalid);
|
||||
+ }
|
||||
|
||||
sch_work = false;
|
||||
notify_charger = false;
|
||||
@@ -1286,6 +1303,83 @@ static int rockchip_otg_event(struct not
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
+static const char *const rockchip_usb2phy_typec_cons[] = {
|
||||
+ "usb-c-connector",
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
+static struct device_node *rockchip_usb2phy_to_controller(struct rockchip_usb2phy *rphy)
|
||||
+{
|
||||
+ struct device_node *np;
|
||||
+ struct device_node *parent;
|
||||
+
|
||||
+ for_each_node_with_property(np, "phys") {
|
||||
+ struct of_phandle_iterator it;
|
||||
+ int ret;
|
||||
+
|
||||
+ of_for_each_phandle(&it, ret, np, "phys", NULL, 0) {
|
||||
+ parent = of_get_parent(it.node);
|
||||
+ if (it.node != rphy->dev->of_node && rphy->dev->of_node != parent) {
|
||||
+ if (parent)
|
||||
+ of_node_put(parent);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Either the PHY phandle we're iterating or its parent
|
||||
+ * matched, we don't care about which out of the two in
|
||||
+ * particular as we just need to know it's the right
|
||||
+ * USB controller for this PHY.
|
||||
+ */
|
||||
+ of_node_put(it.node);
|
||||
+ of_node_put(parent);
|
||||
+ return np;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static bool rockchip_usb2phy_otg_is_type_c(struct rockchip_usb2phy *rphy)
|
||||
+{
|
||||
+ struct device_node *controller = rockchip_usb2phy_to_controller(rphy);
|
||||
+ struct device_node *ports;
|
||||
+ struct device_node *ep = NULL;
|
||||
+ struct device_node *parent;
|
||||
+
|
||||
+ if (!controller)
|
||||
+ return false;
|
||||
+
|
||||
+ ports = of_get_child_by_name(controller, "ports");
|
||||
+ if (ports) {
|
||||
+ of_node_put(controller);
|
||||
+ controller = ports;
|
||||
+ }
|
||||
+
|
||||
+ for_each_of_graph_port(controller, port) {
|
||||
+ ep = of_get_child_by_name(port, "endpoint");
|
||||
+ if (!ep)
|
||||
+ continue;
|
||||
+
|
||||
+ parent = of_graph_get_remote_port_parent(ep);
|
||||
+ of_node_put(ep);
|
||||
+ if (!parent)
|
||||
+ continue;
|
||||
+
|
||||
+ if (of_device_compatible_match(parent, rockchip_usb2phy_typec_cons)) {
|
||||
+ of_node_put(parent);
|
||||
+ of_node_put(controller);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ of_node_put(parent);
|
||||
+ }
|
||||
+
|
||||
+ of_node_put(controller);
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
|
||||
struct rockchip_usb2phy_port *rport,
|
||||
struct device_node *child_np)
|
||||
@@ -1307,6 +1401,8 @@ static int rockchip_usb2phy_otg_port_ini
|
||||
|
||||
mutex_init(&rport->mutex);
|
||||
|
||||
+ rport->typec_vbus_det = rockchip_usb2phy_otg_is_type_c(rphy);
|
||||
+
|
||||
rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
|
||||
if (rport->mode == USB_DR_MODE_HOST ||
|
||||
rport->mode == USB_DR_MODE_UNKNOWN) {
|
||||
@@ -2134,6 +2230,8 @@ static const struct rockchip_usb2phy_cfg
|
||||
.port_cfgs = {
|
||||
[USB2PHY_PORT_OTG] = {
|
||||
.phy_sus = { 0x0000, 8, 0, 0, 0x1d1 },
|
||||
+ .svbus_en = { 0x0000, 15, 15, 0, 1 },
|
||||
+ .svbus_sel = { 0x0000, 14, 14, 0, 1 },
|
||||
.bvalid_det_en = { 0x00c0, 1, 1, 0, 1 },
|
||||
.bvalid_det_st = { 0x00c4, 1, 1, 0, 1 },
|
||||
.bvalid_det_clr = { 0x00c8, 1, 1, 0, 1 },
|
||||
@@ -2171,6 +2269,8 @@ static const struct rockchip_usb2phy_cfg
|
||||
.port_cfgs = {
|
||||
[USB2PHY_PORT_OTG] = {
|
||||
.phy_sus = { 0x2000, 8, 0, 0, 0x1d1 },
|
||||
+ .svbus_en = { 0x2000, 15, 15, 0, 1 },
|
||||
+ .svbus_sel = { 0x2000, 14, 14, 0, 1 },
|
||||
.bvalid_det_en = { 0x20c0, 1, 1, 0, 1 },
|
||||
.bvalid_det_st = { 0x20c4, 1, 1, 0, 1 },
|
||||
.bvalid_det_clr = { 0x20c8, 1, 1, 0, 1 },
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 2/35] phy: rockchip: usbdp: Update mode_change after
|
||||
error handling
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:03 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-2-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
If rk_udphy_init() or rk_udphy_setup() fails, the reinit will not be
|
||||
tried again. Fix this by only updating the variable after all potential
|
||||
errors have been handled.
|
||||
|
||||
Note, that no errors have been seen on real hardware and failures would
|
||||
most likely be fatal and require at least a full reboot as the function
|
||||
already asserts the PHY reset lines. So this is more of a theoretical
|
||||
issue.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://lore.kernel.org/linux-phy/20260612163835.8D5471F000E9@smtp.kernel.org/
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1000,15 +1000,14 @@ static int rk_udphy_power_on(struct rk_u
|
||||
}
|
||||
|
||||
if (udphy->status == UDPHY_MODE_NONE) {
|
||||
- udphy->mode_change = false;
|
||||
ret = rk_udphy_setup(udphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (udphy->mode & UDPHY_MODE_USB)
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
- } else if (udphy->mode_change) {
|
||||
udphy->mode_change = false;
|
||||
+ } else if (udphy->mode_change) {
|
||||
udphy->status = UDPHY_MODE_NONE;
|
||||
if (udphy->mode == UDPHY_MODE_DP)
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
@@ -1017,6 +1016,7 @@ static int rk_udphy_power_on(struct rk_u
|
||||
ret = rk_udphy_setup(udphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
+ udphy->mode_change = false;
|
||||
}
|
||||
|
||||
udphy->status |= mode;
|
||||
-147
@@ -1,147 +0,0 @@
|
||||
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
To: Vinod Koul <vkoul@kernel.org>,
|
||||
Kishon Vijay Abraham I <kishon@kernel.org>,
|
||||
Rob Herring <robh@kernel.org>,
|
||||
Krzysztof Kozlowski <krzk+dt@kernel.org>,
|
||||
Conor Dooley <conor+dt@kernel.org>,
|
||||
Heiko Stuebner <heiko@sntech.de>,
|
||||
Kever Yang <kever.yang@rock-chips.com>,
|
||||
Frank Wang <frank.wang@rock-chips.com>
|
||||
Cc: devicetree@vger.kernel.org,
|
||||
Sebastian Reichel <sebastian.reichel@collabora.com>,
|
||||
linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org,
|
||||
Alexey Charkov <alchark@gmail.com>,
|
||||
linux-phy@lists.infradead.org, kernel@collabora.com,
|
||||
linux-arm-kernel@lists.infradead.org
|
||||
Subject: [PATCH v4 2/4] phy: rockchip: usbdp: move orientation handling further down
|
||||
Date: Tue, 10 Jun 2025 16:07:10 +0200 [thread overview]
|
||||
Message-ID: <20250610-rk3576-sige5-usb-v4-2-7e7f779619c1@collabora.com> (raw)
|
||||
In-Reply-To: <20250610-rk3576-sige5-usb-v4-0-7e7f779619c1@collabora.com>
|
||||
|
||||
The orientation handling code isn't referenced until very far down the
|
||||
driver code.
|
||||
|
||||
Move it down some ways so it can later reference other driver functions
|
||||
without needing forward declarations.
|
||||
|
||||
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 100 +++++++++++++++---------------
|
||||
1 file changed, 50 insertions(+), 50 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -651,56 +651,6 @@ static void rk_udphy_set_typec_default_m
|
||||
udphy->mode = UDPHY_MODE_DP_USB;
|
||||
}
|
||||
|
||||
-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);
|
||||
-
|
||||
- mutex_lock(&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;
|
||||
- }
|
||||
-
|
||||
- udphy->flip = orien == TYPEC_ORIENTATION_REVERSE;
|
||||
- rk_udphy_set_typec_default_mapping(udphy);
|
||||
- rk_udphy_usb_bvalid_enable(udphy, true);
|
||||
-
|
||||
-unlock_ret:
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static void rk_udphy_orien_switch_unregister(void *data)
|
||||
-{
|
||||
- struct rk_udphy *udphy = data;
|
||||
-
|
||||
- typec_switch_unregister(udphy->sw);
|
||||
-}
|
||||
-
|
||||
-static int rk_udphy_setup_orien_switch(struct rk_udphy *udphy)
|
||||
-{
|
||||
- struct typec_switch_desc sw_desc = { };
|
||||
-
|
||||
- sw_desc.drvdata = udphy;
|
||||
- sw_desc.fwnode = dev_fwnode(udphy->dev);
|
||||
- sw_desc.set = rk_udphy_orien_sw_set;
|
||||
-
|
||||
- udphy->sw = typec_switch_register(udphy->dev, &sw_desc);
|
||||
- if (IS_ERR(udphy->sw)) {
|
||||
- dev_err(udphy->dev, "Error register typec orientation switch: %ld\n",
|
||||
- PTR_ERR(udphy->sw));
|
||||
- return PTR_ERR(udphy->sw);
|
||||
- }
|
||||
-
|
||||
- return devm_add_action_or_reset(udphy->dev,
|
||||
- rk_udphy_orien_switch_unregister, udphy);
|
||||
-}
|
||||
-
|
||||
static int rk_udphy_refclk_set(struct rk_udphy *udphy)
|
||||
{
|
||||
unsigned long rate;
|
||||
@@ -1450,6 +1400,56 @@ static struct phy *rk_udphy_phy_xlate(st
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
+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);
|
||||
+
|
||||
+ mutex_lock(&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;
|
||||
+ }
|
||||
+
|
||||
+ udphy->flip = orien == TYPEC_ORIENTATION_REVERSE;
|
||||
+ rk_udphy_set_typec_default_mapping(udphy);
|
||||
+ rk_udphy_usb_bvalid_enable(udphy, true);
|
||||
+
|
||||
+unlock_ret:
|
||||
+ mutex_unlock(&udphy->mutex);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void rk_udphy_orien_switch_unregister(void *data)
|
||||
+{
|
||||
+ struct rk_udphy *udphy = data;
|
||||
+
|
||||
+ typec_switch_unregister(udphy->sw);
|
||||
+}
|
||||
+
|
||||
+static int rk_udphy_setup_orien_switch(struct rk_udphy *udphy)
|
||||
+{
|
||||
+ struct typec_switch_desc sw_desc = { };
|
||||
+
|
||||
+ sw_desc.drvdata = udphy;
|
||||
+ sw_desc.fwnode = dev_fwnode(udphy->dev);
|
||||
+ sw_desc.set = rk_udphy_orien_sw_set;
|
||||
+
|
||||
+ udphy->sw = typec_switch_register(udphy->dev, &sw_desc);
|
||||
+ if (IS_ERR(udphy->sw)) {
|
||||
+ dev_err(udphy->dev, "Error register typec orientation switch: %ld\n",
|
||||
+ PTR_ERR(udphy->sw));
|
||||
+ return PTR_ERR(udphy->sw);
|
||||
+ }
|
||||
+
|
||||
+ return devm_add_action_or_reset(udphy->dev,
|
||||
+ rk_udphy_orien_switch_unregister, udphy);
|
||||
+}
|
||||
+
|
||||
static int rk_udphy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 3/35] phy: rockchip: usbdp: Do not lose USB3 PHY status
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:04 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-3-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
By default (i.e. without manually enabling runtime PM) DWC3 requests the
|
||||
USB3 PHY once and keeps it enabled all the time. When DisplayPort is
|
||||
being requested later on, a mode change is needed. This re-initializes
|
||||
the PHY. During re-initialization the status variable has incorrectly
|
||||
been cleared, which means the tracking information for USB3 is lost.
|
||||
|
||||
This is not an immediate problem, since the DP side keeps the PHY
|
||||
enabled. But once DP is toggled off, the whole PHY will be disabled.
|
||||
This is a problem, because the USB side still needs it powered.
|
||||
|
||||
Fix things by not clearing the status flags.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1008,7 +1008,6 @@ static int rk_udphy_power_on(struct rk_u
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
udphy->mode_change = false;
|
||||
} else if (udphy->mode_change) {
|
||||
- udphy->status = UDPHY_MODE_NONE;
|
||||
if (udphy->mode == UDPHY_MODE_DP)
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
To: Vinod Koul <vkoul@kernel.org>,
|
||||
Kishon Vijay Abraham I <kishon@kernel.org>,
|
||||
Rob Herring <robh@kernel.org>,
|
||||
Krzysztof Kozlowski <krzk+dt@kernel.org>,
|
||||
Conor Dooley <conor+dt@kernel.org>,
|
||||
Heiko Stuebner <heiko@sntech.de>,
|
||||
Kever Yang <kever.yang@rock-chips.com>,
|
||||
Frank Wang <frank.wang@rock-chips.com>
|
||||
Cc: devicetree@vger.kernel.org,
|
||||
Sebastian Reichel <sebastian.reichel@collabora.com>,
|
||||
linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org,
|
||||
Alexey Charkov <alchark@gmail.com>,
|
||||
linux-phy@lists.infradead.org, kernel@collabora.com,
|
||||
linux-arm-kernel@lists.infradead.org
|
||||
Subject: [PATCH v4 3/4] phy: rockchip: usbdp: reset USB3 and reinit on orientation switch
|
||||
Date: Tue, 10 Jun 2025 16:07:11 +0200 [thread overview]
|
||||
Message-ID: <20250610-rk3576-sige5-usb-v4-3-7e7f779619c1@collabora.com> (raw)
|
||||
In-Reply-To: <20250610-rk3576-sige5-usb-v4-0-7e7f779619c1@collabora.com>
|
||||
|
||||
Until now, super speed on Type-C only worked in one orientation. This is
|
||||
because on an orientation switch, the UDPHY was never reinitialised.
|
||||
|
||||
Heiko presented a patch to do this[1], but there were concerns over the
|
||||
correctness of it[2]. Experimentally using Heiko's patch on RK3576 did
|
||||
make me run into issues, though they seemed to be related to the
|
||||
orientation switch actually happening while a clock driving a GRF
|
||||
register was disabled.
|
||||
|
||||
The key issue is that the hardware wants the USB 3 controller to be held
|
||||
in reset while the PHY is being reconfigured, otherwise we may run into
|
||||
hard-to-catch race conditions.
|
||||
|
||||
Either way, this patch implements the required ordering in a somewhat
|
||||
unpleasant way: we get the USB 3 controller from the DT, and use runtime
|
||||
power management to forcibly suspend it while the UDPHY is being
|
||||
reconfigured, and then forcibly resume it later. As an added pain in the
|
||||
rear, the suspend/resume of the USB 3 controller also tries fiddling
|
||||
with the USB 3 PHY part of the UDPHY, which means we introduce an atomic
|
||||
flag to skip suspending/resuming the UDPHY if we're resetting the USB 3
|
||||
controller. We may just need to skip trying to acquire the mutex again,
|
||||
but both ways work for me in practice.
|
||||
|
||||
This solution may in fact be complete rubbish, but it works to get USB 3
|
||||
Super Speed working in both cable orientations on my board.
|
||||
|
||||
Link: https://lore.kernel.org/all/20250226103810.3746018-3-heiko@sntech.de/ [1]
|
||||
Link: https://lore.kernel.org/linux-rockchip/h57ok2hw6os7bcafqkrqknfvm7hnu25m2oe54qmrsuzdwqlos3@m4och2fcdm7s/ [2]
|
||||
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 54 +++++++++++++++++++++++++++++++
|
||||
1 file changed, 54 insertions(+)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -200,6 +200,10 @@ struct rk_udphy {
|
||||
/* PHY devices */
|
||||
struct phy *phy_dp;
|
||||
struct phy *phy_u3;
|
||||
+
|
||||
+ /* USB 3 controller device */
|
||||
+ struct device *ctrl_u3;
|
||||
+ atomic_t ctrl_resetting;
|
||||
};
|
||||
|
||||
static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr[4][4] = {
|
||||
@@ -1255,6 +1259,9 @@ static int rk_udphy_usb3_phy_init(struct
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
+ if (atomic_read(&udphy->ctrl_resetting))
|
||||
+ return 0;
|
||||
+
|
||||
mutex_lock(&udphy->mutex);
|
||||
/* DP only or high-speed, disable U3 port */
|
||||
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
|
||||
@@ -1273,6 +1280,9 @@ static int rk_udphy_usb3_phy_exit(struct
|
||||
{
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
|
||||
+ if (atomic_read(&udphy->ctrl_resetting))
|
||||
+ return 0;
|
||||
+
|
||||
mutex_lock(&udphy->mutex);
|
||||
/* DP only or high-speed */
|
||||
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
|
||||
@@ -1400,10 +1410,31 @@ static struct phy *rk_udphy_phy_xlate(st
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
+static struct device_node *rk_udphy_to_controller(struct rk_udphy *udphy)
|
||||
+{
|
||||
+ struct device_node *np;
|
||||
+
|
||||
+ for_each_node_with_property(np, "phys") {
|
||||
+ struct of_phandle_iterator it;
|
||||
+ int ret;
|
||||
+
|
||||
+ of_for_each_phandle(&it, ret, np, "phys", NULL, 0) {
|
||||
+ if (it.node != udphy->dev->of_node)
|
||||
+ continue;
|
||||
+
|
||||
+ of_node_put(it.node);
|
||||
+ return np;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
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);
|
||||
+ int ret;
|
||||
|
||||
mutex_lock(&udphy->mutex);
|
||||
|
||||
@@ -1419,6 +1450,18 @@ static int rk_udphy_orien_sw_set(struct
|
||||
rk_udphy_set_typec_default_mapping(udphy);
|
||||
rk_udphy_usb_bvalid_enable(udphy, true);
|
||||
|
||||
+ if (udphy->status != UDPHY_MODE_NONE && udphy->ctrl_u3) {
|
||||
+ atomic_set(&udphy->ctrl_resetting, 1);
|
||||
+ pm_runtime_force_suspend(udphy->ctrl_u3);
|
||||
+
|
||||
+ ret = rk_udphy_setup(udphy);
|
||||
+ if (!ret)
|
||||
+ clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
|
||||
+
|
||||
+ pm_runtime_force_resume(udphy->ctrl_u3);
|
||||
+ atomic_set(&udphy->ctrl_resetting, 0);
|
||||
+ }
|
||||
+
|
||||
unlock_ret:
|
||||
mutex_unlock(&udphy->mutex);
|
||||
return 0;
|
||||
@@ -1429,12 +1472,22 @@ static void rk_udphy_orien_switch_unregi
|
||||
struct rk_udphy *udphy = data;
|
||||
|
||||
typec_switch_unregister(udphy->sw);
|
||||
+ put_device(udphy->ctrl_u3);
|
||||
}
|
||||
|
||||
static int rk_udphy_setup_orien_switch(struct rk_udphy *udphy)
|
||||
{
|
||||
+ struct device_node *ctrl = rk_udphy_to_controller(udphy);
|
||||
struct typec_switch_desc sw_desc = { };
|
||||
|
||||
+ if (ctrl) {
|
||||
+ udphy->ctrl_u3 = bus_find_device_by_of_node(udphy->dev->bus, ctrl);
|
||||
+ of_node_put(ctrl);
|
||||
+ }
|
||||
+
|
||||
+ if (!udphy->ctrl_u3)
|
||||
+ dev_info(udphy->dev, "couldn't find this PHY's USB3 controller\n");
|
||||
+
|
||||
sw_desc.drvdata = udphy;
|
||||
sw_desc.fwnode = dev_fwnode(udphy->dev);
|
||||
sw_desc.set = rk_udphy_orien_sw_set;
|
||||
@@ -1498,6 +1551,7 @@ static int rk_udphy_probe(struct platfor
|
||||
return ret;
|
||||
|
||||
mutex_init(&udphy->mutex);
|
||||
+ atomic_set(&udphy->ctrl_resetting, 0);
|
||||
platform_set_drvdata(pdev, udphy);
|
||||
|
||||
if (device_property_present(dev, "orientation-switch")) {
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 4/35] phy: rockchip: usbdp: Fix devm_clk_bulk_get_all
|
||||
check
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:05 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-4-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
If devm_clk_bulk_get_all() returns -EPROBE_DEFER, it is replaced with
|
||||
-ENODEV, permanently failing the driver probe instead of allowing it to
|
||||
defer. Avoid masking the error code to fix the issue.
|
||||
|
||||
This effectively drops returning -ENODEV in case no clocks are being
|
||||
described in DT. This special case will now be handled by the follow-up
|
||||
check searching for "refclk" and exit with -EINVAL.
|
||||
|
||||
None of this will be hit in practice, since the driver is only used by
|
||||
RK3588 and RK3576 - on these platforms the DT is validated to contain
|
||||
the clocks and the clock driver is force probed early. Thus there is
|
||||
no need to backport this.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://lore.kernel.org/linux-phy/20260612164107.C7DB21F000E9@smtp.kernel.org/
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -427,8 +427,8 @@ static int rk_udphy_clk_init(struct rk_u
|
||||
int i;
|
||||
|
||||
udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks);
|
||||
- if (udphy->num_clks < 1)
|
||||
- return -ENODEV;
|
||||
+ if (udphy->num_clks < 0)
|
||||
+ return udphy->num_clks;
|
||||
|
||||
/* used for configure phy reference clock frequency */
|
||||
for (i = 0; i < udphy->num_clks; i++) {
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 5/35] phy: rockchip: usbdp: Handle missing clock-names
|
||||
DT property gracefully
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:06 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-5-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
The rk_udphy_clk_init() function would currently try to do a strncmp for
|
||||
a NULL pointer, if DT specifies 'clocks' property, but no 'clock-names'
|
||||
property. Fix this by making sure the clock has an id string set.
|
||||
|
||||
Note that DT binding requires setting clock-names, so this is only a
|
||||
problem when booting a non-compliant device tree.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://sashiko.dev/#/message/20260619154349.071321F000E9%40smtp.kernel.org
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -432,6 +432,9 @@ static int rk_udphy_clk_init(struct rk_u
|
||||
|
||||
/* used for configure phy reference clock frequency */
|
||||
for (i = 0; i < udphy->num_clks; i++) {
|
||||
+ if (!udphy->clks[i].id)
|
||||
+ continue;
|
||||
+
|
||||
if (!strncmp(udphy->clks[i].id, "refclk", 6)) {
|
||||
udphy->refclk = udphy->clks[i].clk;
|
||||
break;
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 6/35] phy: rockchip: usbdp: Drop seamless DP takeover
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:07 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-6-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Right now the DRM drivers do not support seamless DP takeover and I'm
|
||||
I'm not aware of any bootloader implementing this feature either.
|
||||
|
||||
In any case this feature would be limited to boards using the USBDP PHY
|
||||
for a DP or eDP connection instead of the more commonly USB-C connector.
|
||||
With USB-C's DP AltMode a seamless DP takeover requires handing over the
|
||||
state of the TCPM state machine from the bootloader to the kernel. This
|
||||
in turn requires a huge amount of work to keep the state machine
|
||||
implementations synchronized. It's very unlikely we will see somebody
|
||||
implementing that in the foreseeable future.
|
||||
|
||||
As the current code is obviously buggy and untested, let's simply drop
|
||||
support for seamless DP takeover. It can be re-implemented cleanly once
|
||||
somebody adds all missing bits.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://lore.kernel.org/linux-phy/20260612164107.C7DB21F000E9@smtp.kernel.org/
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 31 -------------------------------
|
||||
1 file changed, 31 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -452,11 +452,6 @@ static int rk_udphy_reset_assert_all(str
|
||||
return reset_control_bulk_assert(udphy->num_rsts, udphy->rsts);
|
||||
}
|
||||
|
||||
-static int rk_udphy_reset_deassert_all(struct rk_udphy *udphy)
|
||||
-{
|
||||
- return reset_control_bulk_deassert(udphy->num_rsts, udphy->rsts);
|
||||
-}
|
||||
-
|
||||
static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name)
|
||||
{
|
||||
struct reset_control_bulk_data *list = udphy->rsts;
|
||||
@@ -924,28 +919,6 @@ static int rk_udphy_parse_lane_mux_data(
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
|
||||
-{
|
||||
- int ret;
|
||||
- u32 value;
|
||||
-
|
||||
- ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
|
||||
- if (ret) {
|
||||
- dev_err(udphy->dev, "failed to enable clk\n");
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- rk_udphy_reset_deassert_all(udphy);
|
||||
-
|
||||
- regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
|
||||
- if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value))
|
||||
- udphy->status = UDPHY_MODE_DP;
|
||||
- else
|
||||
- rk_udphy_disable(udphy);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static int rk_udphy_parse_dt(struct rk_udphy *udphy)
|
||||
{
|
||||
struct device *dev = udphy->dev;
|
||||
@@ -1495,10 +1468,6 @@ static int rk_udphy_probe(struct platfor
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = rk_udphy_get_initial_status(udphy);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
mutex_init(&udphy->mutex);
|
||||
platform_set_drvdata(pdev, udphy);
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 7/35] phy: rockchip: usbdp: Keep clocks running on PHY
|
||||
re-init
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:08 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-7-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
When a mode change is required rk_udphy_power_on() disables
|
||||
the clocks and then calls rk_udphy_setup(), which then enables
|
||||
all the clocks again before continuing with rk_udphy_init().
|
||||
|
||||
Considering that rk_udphy_init() does assert the reset lines,
|
||||
re-enabling the clocks is just delaying things. Avoid it by
|
||||
directly calling rk_udphy_init().
|
||||
|
||||
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -987,8 +987,7 @@ static int rk_udphy_power_on(struct rk_u
|
||||
if (udphy->mode == UDPHY_MODE_DP)
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
|
||||
- rk_udphy_disable(udphy);
|
||||
- ret = rk_udphy_setup(udphy);
|
||||
+ ret = rk_udphy_init(udphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
udphy->mode_change = false;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 8/35] phy: rockchip: usbdp: Amend SSC modulation
|
||||
deviation
|
||||
From: Frank Wang <frank.wang@rock-chips.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:09 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-8-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Move SSC modulation deviation into private config of clock
|
||||
|
||||
- 24M: 0x00d4[5:0] = 0x30
|
||||
- 26M: 0x00d4[5:0] = 0x33
|
||||
|
||||
[Taken over from rockchip's kernel tree; register 0x00d4 is not
|
||||
described in the TRM]
|
||||
|
||||
Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
|
||||
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
--- 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_udph
|
||||
{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_udph
|
||||
{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_udph
|
||||
{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,
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 9/35] phy: rockchip: usbdp: Fix LFPS detect threshold
|
||||
control
|
||||
From: William Wu <william.wu@rock-chips.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:10 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-9-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
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
|
||||
|
||||
[Taken over from rockchip's kernel tree; the registers are not described
|
||||
in the TRM]
|
||||
|
||||
Signed-off-by: William Wu <william.wu@rock-chips.com>
|
||||
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- 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_udph
|
||||
{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,
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 10/35] phy: rockchip: usbdp: Add missing mode_change
|
||||
update
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:11 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-10-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
rk_udphy_set_typec_default_mapping() updates the available modes,
|
||||
but does not set the mode_change as required. This results in
|
||||
missing re-initialization and thus non-working DisplayPort.
|
||||
|
||||
Fix this issue by introducing a new helper to update the available
|
||||
modes.
|
||||
|
||||
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 16 +++++++++++-----
|
||||
1 file changed, 11 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -617,6 +617,15 @@ static void rk_udphy_dp_hpd_event_trigge
|
||||
rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
|
||||
}
|
||||
|
||||
+static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
|
||||
+{
|
||||
+ if (udphy->mode == mode)
|
||||
+ return;
|
||||
+
|
||||
+ udphy->mode_change = true;
|
||||
+ udphy->mode = mode;
|
||||
+}
|
||||
+
|
||||
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
|
||||
{
|
||||
if (udphy->flip) {
|
||||
@@ -647,7 +656,7 @@ static void rk_udphy_set_typec_default_m
|
||||
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
|
||||
}
|
||||
|
||||
- udphy->mode = UDPHY_MODE_DP_USB;
|
||||
+ rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB);
|
||||
}
|
||||
|
||||
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
|
||||
@@ -1361,10 +1370,7 @@ static int rk_udphy_typec_mux_set(struct
|
||||
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);
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 11/35] phy: rockchip: usbdp: Support single-lane DP
|
||||
From: Zhang Yubing <yubing.zhang@rock-chips.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:12 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-11-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Implement support for using just a single DisplayPort line.
|
||||
|
||||
Signed-off-by: Zhang Yubing <yubing.zhang@rock-chips.com>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 64 +++++++++++++------------------
|
||||
1 file changed, 27 insertions(+), 37 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -193,6 +193,7 @@ struct rk_udphy {
|
||||
int id;
|
||||
|
||||
bool dp_in_use;
|
||||
+ int dp_lanes;
|
||||
|
||||
/* PHY const config */
|
||||
const struct rk_udphy_cfg *cfgs;
|
||||
@@ -535,6 +536,13 @@ static void rk_udphy_usb_bvalid_enable(s
|
||||
* <0 1> dpln0 dpln1 usbrx usbtx
|
||||
* <2 3> usbrx usbtx dpln0 dpln1
|
||||
* ---------------------------------------------------------------------------
|
||||
+ * if 1 lane for dp function, 2 lane for usb function, define rockchip,dp-lane-mux = <x>;
|
||||
+ * sample as follow:
|
||||
+ * ---------------------------------------------------------------------------
|
||||
+ * B11-B10 A2-A3 A11-A10 B2-B3
|
||||
+ * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
|
||||
+ * <0> dpln0 \ usbrx usbtx
|
||||
+ * ---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void rk_udphy_dplane_select(struct rk_udphy *udphy)
|
||||
@@ -542,18 +550,18 @@ static void rk_udphy_dplane_select(struc
|
||||
const struct rk_udphy_cfg *cfg = udphy->cfgs;
|
||||
u32 value = 0;
|
||||
|
||||
- switch (udphy->mode) {
|
||||
- case UDPHY_MODE_DP:
|
||||
- value |= 2 << udphy->dp_lane_sel[2] * 2;
|
||||
+ switch (udphy->dp_lanes) {
|
||||
+ case 4:
|
||||
value |= 3 << udphy->dp_lane_sel[3] * 2;
|
||||
+ value |= 2 << udphy->dp_lane_sel[2] * 2;
|
||||
fallthrough;
|
||||
|
||||
- case UDPHY_MODE_DP_USB:
|
||||
- value |= 0 << udphy->dp_lane_sel[0] * 2;
|
||||
+ case 2:
|
||||
value |= 1 << udphy->dp_lane_sel[1] * 2;
|
||||
- break;
|
||||
+ fallthrough;
|
||||
|
||||
- case UDPHY_MODE_USB:
|
||||
+ case 1:
|
||||
+ value |= 0 << udphy->dp_lane_sel[0] * 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -566,28 +574,6 @@ static void rk_udphy_dplane_select(struc
|
||||
FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
|
||||
}
|
||||
|
||||
-static int rk_udphy_dplane_get(struct rk_udphy *udphy)
|
||||
-{
|
||||
- int dp_lanes;
|
||||
-
|
||||
- switch (udphy->mode) {
|
||||
- case UDPHY_MODE_DP:
|
||||
- dp_lanes = 4;
|
||||
- break;
|
||||
-
|
||||
- case UDPHY_MODE_DP_USB:
|
||||
- dp_lanes = 2;
|
||||
- break;
|
||||
-
|
||||
- case UDPHY_MODE_USB:
|
||||
- default:
|
||||
- dp_lanes = 0;
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- return dp_lanes;
|
||||
-}
|
||||
-
|
||||
static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
|
||||
{
|
||||
u32 val = 0;
|
||||
@@ -657,6 +643,7 @@ static void rk_udphy_set_typec_default_m
|
||||
}
|
||||
|
||||
rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB);
|
||||
+ udphy->dp_lanes = 2;
|
||||
}
|
||||
|
||||
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
|
||||
@@ -895,7 +882,7 @@ static int rk_udphy_parse_lane_mux_data(
|
||||
return 0;
|
||||
}
|
||||
|
||||
- if (num_lanes != 2 && num_lanes != 4)
|
||||
+ if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4)
|
||||
return dev_err_probe(udphy->dev, -EINVAL,
|
||||
"invalid number of lane mux\n");
|
||||
|
||||
@@ -921,9 +908,11 @@ static int rk_udphy_parse_lane_mux_data(
|
||||
}
|
||||
|
||||
udphy->mode = UDPHY_MODE_DP;
|
||||
- if (num_lanes == 2) {
|
||||
+ udphy->dp_lanes = num_lanes;
|
||||
+ if (num_lanes == 1 || num_lanes == 2) {
|
||||
udphy->mode |= UDPHY_MODE_USB;
|
||||
- udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP);
|
||||
+ udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP) ||
|
||||
+ (udphy->lane_mux_sel[1] == PHY_LANE_MUX_DP);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1050,18 +1039,17 @@ static int rk_udphy_dp_phy_exit(struct p
|
||||
static int rk_udphy_dp_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
- int ret, dp_lanes;
|
||||
+ int ret;
|
||||
|
||||
mutex_lock(&udphy->mutex);
|
||||
|
||||
- dp_lanes = rk_udphy_dplane_get(udphy);
|
||||
- phy_set_bus_width(phy, dp_lanes);
|
||||
+ phy_set_bus_width(phy, udphy->dp_lanes);
|
||||
|
||||
ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
- rk_udphy_dplane_enable(udphy, dp_lanes);
|
||||
+ rk_udphy_dplane_enable(udphy, udphy->dp_lanes);
|
||||
|
||||
rk_udphy_dplane_select(udphy);
|
||||
|
||||
@@ -1341,6 +1329,7 @@ static int rk_udphy_typec_mux_set(struct
|
||||
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
mode = UDPHY_MODE_DP;
|
||||
+ udphy->dp_lanes = 4;
|
||||
break;
|
||||
|
||||
case TYPEC_DP_STATE_D:
|
||||
@@ -1357,6 +1346,7 @@ static int rk_udphy_typec_mux_set(struct
|
||||
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
}
|
||||
mode = UDPHY_MODE_DP_USB;
|
||||
+ udphy->dp_lanes = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1501,7 +1491,7 @@ static int rk_udphy_probe(struct platfor
|
||||
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);
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 12/35] phy: rockchip: usbdp: Limit DP lane count to
|
||||
muxed lanes
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:13 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-12-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
In theory the DP controller could request 4 lanes when the PHY is
|
||||
restricted to 2 lanes as the other half is used by USB3.
|
||||
|
||||
With the current user (DW-DP) this cannot happen, but as the check is
|
||||
cheap and users might change in the future protect things accordingly.
|
||||
Not doing so would corrupt USB3 usage by the following code configuring
|
||||
the voltages.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://lore.kernel.org/linux-phy/20260612165546.98E1F1F000E9@smtp.kernel.org/
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1099,6 +1099,9 @@ static int rk_udphy_dp_phy_verify_link_r
|
||||
static int rk_udphy_dp_phy_verify_lanes(struct rk_udphy *udphy,
|
||||
struct phy_configure_opts_dp *dp)
|
||||
{
|
||||
+ if (dp->lanes > udphy->dp_lanes)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
switch (dp->lanes) {
|
||||
case 1:
|
||||
case 2:
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 13/35] phy: rockchip: usbdp: Rename DP lane functions
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:14 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-13-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
The common prefix for DisplayPort related functions is rk_udphy_dp_
|
||||
(with a final _), so update the two DP lane functions to follow that
|
||||
scheme.
|
||||
|
||||
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -545,7 +545,7 @@ static void rk_udphy_usb_bvalid_enable(s
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
-static void rk_udphy_dplane_select(struct rk_udphy *udphy)
|
||||
+static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
|
||||
{
|
||||
const struct rk_udphy_cfg *cfg = udphy->cfgs;
|
||||
u32 value = 0;
|
||||
@@ -574,7 +574,7 @@ static void rk_udphy_dplane_select(struc
|
||||
FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
|
||||
}
|
||||
|
||||
-static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
|
||||
+static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
|
||||
{
|
||||
u32 val = 0;
|
||||
int i;
|
||||
@@ -1049,9 +1049,9 @@ static int rk_udphy_dp_phy_power_on(stru
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
- rk_udphy_dplane_enable(udphy, udphy->dp_lanes);
|
||||
+ rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
|
||||
|
||||
- rk_udphy_dplane_select(udphy);
|
||||
+ rk_udphy_dp_lane_select(udphy);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&udphy->mutex);
|
||||
@@ -1069,7 +1069,7 @@ static int rk_udphy_dp_phy_power_off(str
|
||||
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);
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 14/35] phy: rockchip: usbdp: Use FIELD_PREP_WM16_CONST
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:15 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-14-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Cleanup code by replacing open-coded version of FIELD_PREP_WM16_CONST
|
||||
with the existing helper macro.
|
||||
|
||||
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
--- 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) \
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 15/35] phy: rockchip: usbdp: Cleanup DP lane selection
|
||||
function
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:16 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-15-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Use FIELD_PREP_WM16() helpers to simplify the DP lane selection
|
||||
logic.
|
||||
|
||||
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 28 +++++++---------------------
|
||||
1 file changed, 7 insertions(+), 21 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -548,30 +548,16 @@ static void rk_udphy_usb_bvalid_enable(s
|
||||
static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
|
||||
{
|
||||
const struct rk_udphy_cfg *cfg = udphy->cfgs;
|
||||
- u32 value = 0;
|
||||
+ u32 value = FIELD_PREP_WM16(DP_LANE_SEL_ALL, 0);
|
||||
+ int i;
|
||||
|
||||
- switch (udphy->dp_lanes) {
|
||||
- case 4:
|
||||
- value |= 3 << udphy->dp_lane_sel[3] * 2;
|
||||
- value |= 2 << udphy->dp_lane_sel[2] * 2;
|
||||
- fallthrough;
|
||||
+ for (i = 0; i < udphy->dp_lanes; i++)
|
||||
+ value |= field_prep(DP_LANE_SEL_N(udphy->dp_lane_sel[i]), i);
|
||||
|
||||
- case 2:
|
||||
- value |= 1 << udphy->dp_lane_sel[1] * 2;
|
||||
- fallthrough;
|
||||
+ 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);
|
||||
|
||||
- case 1:
|
||||
- value |= 0 << udphy->dp_lane_sel[0] * 2;
|
||||
- break;
|
||||
-
|
||||
- default:
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- 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)
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 16/35] phy: rockchip: usbdp: Register DP aux bridge
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:17 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-16-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Add support to use USB-C connectors with the DP altmode helper code on
|
||||
devicetree based platforms. To get this working there must be a DRM
|
||||
bridge chain from the DisplayPort controller to the USB-C connector.
|
||||
E.g. on Rockchip RK3576:
|
||||
|
||||
root@rk3576 # cat /sys/kernel/debug/dri/0/encoder-0/bridges
|
||||
bridge[0]: dw_dp_bridge_funcs
|
||||
refcount: 7
|
||||
type: [10] DP
|
||||
OF: /soc/dp@27e40000:rockchip,rk3576-dp
|
||||
ops: [0x47] detect edid hpd
|
||||
bridge[1]: drm_aux_bridge_funcs
|
||||
refcount: 4
|
||||
type: [0] Unknown
|
||||
OF: /soc/phy@2b010000:rockchip,rk3576-usbdp-phy
|
||||
ops: [0x0]
|
||||
bridge[2]: drm_aux_hpd_bridge_funcs
|
||||
refcount: 5
|
||||
type: [10] DP
|
||||
OF: /soc/i2c@2ac50000/typec-portc@22/connector:usb-c-connector
|
||||
ops: [0x4] hpd
|
||||
|
||||
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/Kconfig | 2 ++
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 17 +++++++++++++++++
|
||||
2 files changed, 19 insertions(+)
|
||||
|
||||
--- 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.
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -6,6 +6,7 @@
|
||||
* Copyright (C) 2024 Collabora Ltd
|
||||
*/
|
||||
|
||||
+#include <drm/bridge/aux-bridge.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
@@ -1414,6 +1415,7 @@ static int rk_udphy_probe(struct platfor
|
||||
{
|
||||
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;
|
||||
@@ -1467,6 +1469,21 @@ static int rk_udphy_probe(struct platfor
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
+
|
||||
+ /*
|
||||
+ * Only register the DRM bridge, if the DP aux channel is connected.
|
||||
+ * Some boards use the USBDP PHY only for its USB3 capabilities. The
|
||||
+ * aux bridge itself will be registered using port 0, endpoint 0, which
|
||||
+ * is fine as that is the actual superspeed data connection shared by
|
||||
+ * USB3 and DP based on the mux config.
|
||||
+ */
|
||||
+ dp_aux_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 3, 0, 0);
|
||||
+ if (dp_aux_ep) {
|
||||
+ ret = drm_aux_bridge_register(dev);
|
||||
+ fwnode_handle_put(dp_aux_ep);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops);
|
||||
if (IS_ERR(udphy->phy_u3)) {
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 17/35] phy: rockchip: usbdp: Drop DP HPD handling
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:18 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-17-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Drop the HPD handling logic from the USBDP PHY. The registers involved
|
||||
require the display controller power domain being enabled and thus the
|
||||
HPD signal should be handled by the displayport controller itself.
|
||||
Apart from that the HPD handling as it is done here is incorrect and
|
||||
misses hotplug events happening after the USB-C connector (e.g. when
|
||||
a USB-C to HDMI adapter is involved and the HDMI cable is replugged).
|
||||
|
||||
Proper USB-C DP HPD support requires some restructuring of the DP
|
||||
controller driver, which will happen independent of this patch. The
|
||||
mainline kernel does not yet support USB-C DP AltMode on RK3588 and
|
||||
RK3576, so it is fine to drop this code without adding the counterpart
|
||||
in the DRM in an atomic change.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 85 ++++---------------------------
|
||||
1 file changed, 9 insertions(+), 76 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -128,7 +128,6 @@ struct rk_udphy_grf_cfg {
|
||||
|
||||
struct rk_udphy_vogrf_cfg {
|
||||
/* vo-grf */
|
||||
- struct rk_udphy_grf_reg hpd_trigger;
|
||||
u32 dp_lane_reg;
|
||||
};
|
||||
|
||||
@@ -186,14 +185,11 @@ struct rk_udphy {
|
||||
u32 dp_lane_sel[4];
|
||||
u32 dp_aux_dout_sel;
|
||||
u32 dp_aux_din_sel;
|
||||
- bool dp_sink_hpd_sel;
|
||||
- bool dp_sink_hpd_cfg;
|
||||
unsigned int link_rate;
|
||||
unsigned int lanes;
|
||||
u8 bw;
|
||||
int id;
|
||||
|
||||
- bool dp_in_use;
|
||||
int dp_lanes;
|
||||
|
||||
/* PHY const config */
|
||||
@@ -577,19 +573,6 @@ static void rk_udphy_dp_lane_enable(stru
|
||||
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
|
||||
}
|
||||
|
||||
-static void rk_udphy_dp_hpd_event_trigger(struct rk_udphy *udphy, bool hpd)
|
||||
-{
|
||||
- const struct rk_udphy_cfg *cfg = udphy->cfgs;
|
||||
-
|
||||
- udphy->dp_sink_hpd_sel = true;
|
||||
- udphy->dp_sink_hpd_cfg = hpd;
|
||||
-
|
||||
- if (!udphy->dp_in_use)
|
||||
- return;
|
||||
-
|
||||
- rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
|
||||
-}
|
||||
-
|
||||
static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
|
||||
{
|
||||
if (udphy->mode == mode)
|
||||
@@ -1000,29 +983,6 @@ static void rk_udphy_power_off(struct rk
|
||||
rk_udphy_disable(udphy);
|
||||
}
|
||||
|
||||
-static int rk_udphy_dp_phy_init(struct phy *phy)
|
||||
-{
|
||||
- struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
-
|
||||
- mutex_lock(&udphy->mutex);
|
||||
-
|
||||
- udphy->dp_in_use = true;
|
||||
-
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int rk_udphy_dp_phy_exit(struct phy *phy)
|
||||
-{
|
||||
- struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
-
|
||||
- mutex_lock(&udphy->mutex);
|
||||
- udphy->dp_in_use = false;
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static int rk_udphy_dp_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
@@ -1254,8 +1214,6 @@ static int rk_udphy_dp_phy_configure(str
|
||||
}
|
||||
|
||||
static const struct phy_ops rk_udphy_dp_phy_ops = {
|
||||
- .init = rk_udphy_dp_phy_init,
|
||||
- .exit = rk_udphy_dp_phy_exit,
|
||||
.power_on = rk_udphy_dp_phy_power_on,
|
||||
.power_off = rk_udphy_dp_phy_power_off,
|
||||
.configure = rk_udphy_dp_phy_configure,
|
||||
@@ -1309,6 +1267,14 @@ static int rk_udphy_typec_mux_set(struct
|
||||
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
|
||||
u8 mode;
|
||||
|
||||
+ /*
|
||||
+ * Ignore mux events not involving DP AltMode, because
|
||||
+ * the mode field is being reused, e.g. state->mode == 4
|
||||
+ * could be either TYPEC_MODE_USB4 or TYPEC_DP_STATE_C.
|
||||
+ */
|
||||
+ if (!state->alt || state->alt->svid != USB_TYPEC_DP_SID)
|
||||
+ return 0;
|
||||
+
|
||||
mutex_lock(&udphy->mutex);
|
||||
|
||||
switch (state->mode) {
|
||||
@@ -1340,22 +1306,7 @@ static int rk_udphy_typec_mux_set(struct
|
||||
break;
|
||||
}
|
||||
|
||||
- if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
|
||||
- struct typec_displayport_data *data = state->data;
|
||||
-
|
||||
- if (!data) {
|
||||
- rk_udphy_dp_hpd_event_trigger(udphy, false);
|
||||
- } else if (data->status & DP_STATUS_IRQ_HPD) {
|
||||
- rk_udphy_dp_hpd_event_trigger(udphy, false);
|
||||
- usleep_range(750, 800);
|
||||
- rk_udphy_dp_hpd_event_trigger(udphy, true);
|
||||
- } else if (data->status & DP_STATUS_HPD_STATE) {
|
||||
- rk_udphy_mode_set(udphy, mode);
|
||||
- rk_udphy_dp_hpd_event_trigger(udphy, true);
|
||||
- } else {
|
||||
- rk_udphy_dp_hpd_event_trigger(udphy, false);
|
||||
- }
|
||||
- }
|
||||
+ rk_udphy_mode_set(udphy, mode);
|
||||
|
||||
mutex_unlock(&udphy->mutex);
|
||||
return 0;
|
||||
@@ -1510,20 +1461,6 @@ static int rk_udphy_probe(struct platfor
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int __maybe_unused rk_udphy_resume(struct device *dev)
|
||||
-{
|
||||
- struct rk_udphy *udphy = dev_get_drvdata(dev);
|
||||
-
|
||||
- if (udphy->dp_sink_hpd_sel)
|
||||
- rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static const struct dev_pm_ops rk_udphy_pm_ops = {
|
||||
- SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, rk_udphy_resume)
|
||||
-};
|
||||
-
|
||||
static const char * const rk_udphy_rst_list[] = {
|
||||
"init", "cmn", "lane", "pcs_apb", "pma_apb"
|
||||
};
|
||||
@@ -1547,7 +1484,6 @@ static const struct rk_udphy_cfg rk3576_
|
||||
},
|
||||
.vogrfcfg = {
|
||||
{
|
||||
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
|
||||
.dp_lane_reg = 0x0000,
|
||||
},
|
||||
},
|
||||
@@ -1588,11 +1524,9 @@ static const struct rk_udphy_cfg rk3588_
|
||||
},
|
||||
.vogrfcfg = {
|
||||
{
|
||||
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
|
||||
.dp_lane_reg = 0x0000,
|
||||
},
|
||||
{
|
||||
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0008, 11, 10, 1, 3),
|
||||
.dp_lane_reg = 0x0008,
|
||||
},
|
||||
},
|
||||
@@ -1628,7 +1562,6 @@ static struct platform_driver rk_udphy_d
|
||||
.driver = {
|
||||
.name = "rockchip-usbdp-phy",
|
||||
.of_match_table = rk_udphy_dt_match,
|
||||
- .pm = &rk_udphy_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(rk_udphy_driver);
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 18/35] phy: rockchip: usbdp: Rename mode_change to
|
||||
phy_needs_reinit
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:19 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-18-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
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(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -171,7 +171,7 @@ struct rk_udphy {
|
||||
|
||||
/* PHY status management */
|
||||
bool flip;
|
||||
- bool mode_change;
|
||||
+ bool phy_needs_reinit;
|
||||
u8 mode;
|
||||
u8 status;
|
||||
|
||||
@@ -578,7 +578,7 @@ static void rk_udphy_mode_set(struct rk_
|
||||
if (udphy->mode == mode)
|
||||
return;
|
||||
|
||||
- udphy->mode_change = true;
|
||||
+ udphy->phy_needs_reinit = true;
|
||||
udphy->mode = mode;
|
||||
}
|
||||
|
||||
@@ -951,15 +951,15 @@ static int rk_udphy_power_on(struct rk_u
|
||||
|
||||
if (udphy->mode & UDPHY_MODE_USB)
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
- udphy->mode_change = false;
|
||||
- } else if (udphy->mode_change) {
|
||||
+ udphy->phy_needs_reinit = false;
|
||||
+ } else if (udphy->phy_needs_reinit) {
|
||||
if (udphy->mode == UDPHY_MODE_DP)
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
|
||||
ret = rk_udphy_init(udphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
- udphy->mode_change = false;
|
||||
+ udphy->phy_needs_reinit = false;
|
||||
}
|
||||
|
||||
udphy->status |= mode;
|
||||
@@ -0,0 +1,41 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 19/35] phy: rockchip: usbdp: Re-init the PHY on
|
||||
orientation change
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:20 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-19-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
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(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -620,6 +620,7 @@ static int rk_udphy_orien_sw_set(struct
|
||||
enum typec_orientation orien)
|
||||
{
|
||||
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
|
||||
+ bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
|
||||
|
||||
mutex_lock(&udphy->mutex);
|
||||
|
||||
@@ -631,7 +632,10 @@ static int rk_udphy_orien_sw_set(struct
|
||||
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);
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 20/35] phy: rockchip: usbdp: Factor out lane_mux_sel
|
||||
setup
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:21 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-20-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
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(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -582,6 +582,42 @@ static void rk_udphy_mode_set(struct rk_
|
||||
udphy->mode = mode;
|
||||
}
|
||||
|
||||
+static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
|
||||
+{
|
||||
+ u8 mode;
|
||||
+
|
||||
+ switch (state) {
|
||||
+ case TYPEC_DP_STATE_C:
|
||||
+ case TYPEC_DP_STATE_E:
|
||||
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
|
||||
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
||||
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
||||
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
+ mode = UDPHY_MODE_DP;
|
||||
+ udphy->dp_lanes = 4;
|
||||
+ break;
|
||||
+
|
||||
+ case TYPEC_DP_STATE_D:
|
||||
+ default:
|
||||
+ if (udphy->flip) {
|
||||
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
|
||||
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
||||
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
|
||||
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
|
||||
+ } else {
|
||||
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
|
||||
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
|
||||
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
||||
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
+ }
|
||||
+ mode = UDPHY_MODE_DP_USB;
|
||||
+ udphy->dp_lanes = 2;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ rk_udphy_mode_set(udphy, mode);
|
||||
+}
|
||||
+
|
||||
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
|
||||
{
|
||||
if (udphy->flip) {
|
||||
@@ -589,10 +625,6 @@ static void rk_udphy_set_typec_default_m
|
||||
udphy->dp_lane_sel[1] = 1;
|
||||
udphy->dp_lane_sel[2] = 3;
|
||||
udphy->dp_lane_sel[3] = 2;
|
||||
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
|
||||
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
||||
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
|
||||
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
|
||||
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT;
|
||||
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT;
|
||||
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1);
|
||||
@@ -602,18 +634,14 @@ static void rk_udphy_set_typec_default_m
|
||||
udphy->dp_lane_sel[1] = 3;
|
||||
udphy->dp_lane_sel[2] = 1;
|
||||
udphy->dp_lane_sel[3] = 0;
|
||||
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
|
||||
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
|
||||
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
||||
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL;
|
||||
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL;
|
||||
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
|
||||
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
|
||||
}
|
||||
|
||||
- rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB);
|
||||
- udphy->dp_lanes = 2;
|
||||
+ /* default to USB3 + DP as 4 lane USB is not supported */
|
||||
+ rk_udphy_set_typec_state(udphy, TYPEC_DP_STATE_D);
|
||||
}
|
||||
|
||||
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
|
||||
@@ -1269,7 +1297,6 @@ static int rk_udphy_typec_mux_set(struct
|
||||
struct typec_mux_state *state)
|
||||
{
|
||||
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
|
||||
- u8 mode;
|
||||
|
||||
/*
|
||||
* Ignore mux events not involving DP AltMode, because
|
||||
@@ -1281,38 +1308,10 @@ static int rk_udphy_typec_mux_set(struct
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 21/35] phy: rockchip: usbdp: Properly handle
|
||||
TYPEC_STATE_SAFE and TYPEC_STATE_USB
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:22 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-21-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Handle TYPEC_STATE_SAFE and TYPEC_STATE_USB Type-C state events,
|
||||
so that the muxing is properly updated when exiting DP AltMode.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://sashiko.dev/#/message/20260619155020.CC7361F000E9%40smtp.kernel.org
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 21 +++++++++++++++------
|
||||
1 file changed, 15 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1293,17 +1293,26 @@ static const struct phy_ops rk_udphy_usb
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
+static bool rk_udphy_is_supported_mode(struct typec_mux_state *state)
|
||||
+{
|
||||
+ /* Handle Safe State and USB State */
|
||||
+ if (state->mode < TYPEC_STATE_MODAL)
|
||||
+ return true;
|
||||
+
|
||||
+ /* Handle DP AltMode */
|
||||
+ if (state->alt && state->alt->svid == USB_TYPEC_DP_SID)
|
||||
+ return true;
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
|
||||
struct typec_mux_state *state)
|
||||
{
|
||||
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
|
||||
|
||||
- /*
|
||||
- * Ignore mux events not involving DP AltMode, because
|
||||
- * the mode field is being reused, e.g. state->mode == 4
|
||||
- * could be either TYPEC_MODE_USB4 or TYPEC_DP_STATE_C.
|
||||
- */
|
||||
- if (!state->alt || state->alt->svid != USB_TYPEC_DP_SID)
|
||||
+ /* Ignore mux events not involving USB or DP */
|
||||
+ if (!rk_udphy_is_supported_mode(state))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&udphy->mutex);
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 22/35] phy: rockchip: usbdp: Use guard functions for
|
||||
mutex
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:23 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-22-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Convert the driver to use guard functions for mutex handling as
|
||||
a small cleanup. There is a small functional change in the DP PHY
|
||||
power up function, which no longer sleeps if the internal powerup
|
||||
code returns an error. This is not a problem as the sleep is only
|
||||
relevant for successful power-up.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 54 +++++++++++++------------------
|
||||
1 file changed, 23 insertions(+), 31 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
+#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
@@ -650,14 +651,15 @@ static int rk_udphy_orien_sw_set(struct
|
||||
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
|
||||
bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
|
||||
|
||||
- mutex_lock(&udphy->mutex);
|
||||
+ guard(mutex)(&udphy->mutex);
|
||||
|
||||
if (orien == TYPEC_ORIENTATION_NONE) {
|
||||
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
|
||||
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
|
||||
/* unattached */
|
||||
rk_udphy_usb_bvalid_enable(udphy, false);
|
||||
- goto unlock_ret;
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
if (udphy->flip != flipped)
|
||||
@@ -667,8 +669,6 @@ static int rk_udphy_orien_sw_set(struct
|
||||
rk_udphy_set_typec_default_mapping(udphy);
|
||||
rk_udphy_usb_bvalid_enable(udphy, true);
|
||||
|
||||
-unlock_ret:
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1020,26 +1020,25 @@ static int rk_udphy_dp_phy_power_on(stru
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
- mutex_lock(&udphy->mutex);
|
||||
+ scoped_guard(mutex, &udphy->mutex) {
|
||||
+ phy_set_bus_width(phy, udphy->dp_lanes);
|
||||
|
||||
- phy_set_bus_width(phy, udphy->dp_lanes);
|
||||
-
|
||||
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
|
||||
- if (ret)
|
||||
- goto unlock;
|
||||
+ ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
|
||||
+ rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
|
||||
|
||||
- rk_udphy_dp_lane_select(udphy);
|
||||
+ rk_udphy_dp_lane_select(udphy);
|
||||
+ }
|
||||
|
||||
-unlock:
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
/*
|
||||
* If data send by aux channel too fast after phy power on,
|
||||
* the aux may be not ready which will cause aux error. Adding
|
||||
* delay to avoid this issue.
|
||||
*/
|
||||
usleep_range(10000, 11000);
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1047,10 +1046,10 @@ static int rk_udphy_dp_phy_power_off(str
|
||||
{
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
|
||||
- mutex_lock(&udphy->mutex);
|
||||
+ guard(mutex)(&udphy->mutex);
|
||||
+
|
||||
rk_udphy_dp_lane_enable(udphy, 0);
|
||||
rk_udphy_power_off(udphy, UDPHY_MODE_DP);
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1255,35 +1254,30 @@ static const struct phy_ops rk_udphy_dp_
|
||||
static int rk_udphy_usb3_phy_init(struct phy *phy)
|
||||
{
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
- int ret = 0;
|
||||
|
||||
- mutex_lock(&udphy->mutex);
|
||||
+ guard(mutex)(&udphy->mutex);
|
||||
+
|
||||
/* DP only or high-speed, disable U3 port */
|
||||
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
- goto unlock;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_USB);
|
||||
-
|
||||
-unlock:
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
- return ret;
|
||||
+ return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
|
||||
}
|
||||
|
||||
static int rk_udphy_usb3_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
|
||||
- mutex_lock(&udphy->mutex);
|
||||
+ guard(mutex)(&udphy->mutex);
|
||||
+
|
||||
/* DP only or high-speed */
|
||||
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
|
||||
- goto unlock;
|
||||
+ return 0;
|
||||
|
||||
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
|
||||
|
||||
-unlock:
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1315,12 +1309,10 @@ static int rk_udphy_typec_mux_set(struct
|
||||
if (!rk_udphy_is_supported_mode(state))
|
||||
return 0;
|
||||
|
||||
- mutex_lock(&udphy->mutex);
|
||||
+ guard(mutex)(&udphy->mutex);
|
||||
|
||||
rk_udphy_set_typec_state(udphy, state->mode);
|
||||
|
||||
- mutex_unlock(&udphy->mutex);
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 23/35] phy: rockchip: usbdp: Hold mutex in DP PHY
|
||||
configure
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:24 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-23-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
rk_udphy_dp_phy_configure() accesses some variables from the struct
|
||||
rk_udphy, which are updated independently from the USB-C framework.
|
||||
|
||||
The USB-C mux/orientation switch functions already hold a mutex to
|
||||
ensure mutual exclusive access to the struct rk_udphy states, so
|
||||
simply hold the same one in the DP PHY configuration function.
|
||||
|
||||
Reproducing problems due to this on real hardware would be really hard,
|
||||
but could be possible when quickly re-connecting the USB-C connector.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://lore.kernel.org/linux-phy/20260612164627.23D391F000E9@smtp.kernel.org/
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1154,6 +1154,8 @@ static int rk_udphy_dp_phy_configure(str
|
||||
u32 i, val, lane;
|
||||
int ret;
|
||||
|
||||
+ guard(mutex)(&udphy->mutex);
|
||||
+
|
||||
if (dp->set_rate) {
|
||||
ret = rk_udphy_dp_phy_verify_link_rate(udphy, dp);
|
||||
if (ret)
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 24/35] phy: rockchip: usbdp: Add some extra debug
|
||||
messages
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:25 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-24-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
It's useful to log PHY reinit to ease debugging issues around
|
||||
USB-C hotplugging.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 23 +++++++++++++++++++++--
|
||||
1 file changed, 21 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
+#include <linux/string_choices.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/typec_dp.h>
|
||||
#include <linux/usb/typec_mux.h>
|
||||
@@ -462,6 +463,8 @@ static int rk_udphy_reset_deassert(struc
|
||||
return reset_control_deassert(list[idx].rstc);
|
||||
}
|
||||
|
||||
+ dev_err(udphy->dev, "failed to de-assert missing reset line: %s\n", name);
|
||||
+
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -488,6 +491,8 @@ static void rk_udphy_u3_port_disable(str
|
||||
const struct rk_udphy_cfg *cfg = udphy->cfgs;
|
||||
const struct rk_udphy_grf_reg *preg;
|
||||
|
||||
+ dev_dbg(udphy->dev, "USB3 port %s\n", str_on_off(!disable));
|
||||
+
|
||||
preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg;
|
||||
rk_udphy_grfreg_write(udphy->usbgrf, preg, disable);
|
||||
}
|
||||
@@ -662,8 +667,10 @@ static int rk_udphy_orien_sw_set(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
- if (udphy->flip != flipped)
|
||||
+ if (udphy->flip != flipped) {
|
||||
+ dev_dbg(udphy->dev, "cable orientation changed, PHY re-init required.\n");
|
||||
udphy->phy_needs_reinit = true;
|
||||
+ }
|
||||
|
||||
udphy->flip = flipped;
|
||||
rk_udphy_set_typec_default_mapping(udphy);
|
||||
@@ -781,6 +788,11 @@ static int rk_udphy_init(struct rk_udphy
|
||||
const struct rk_udphy_cfg *cfg = udphy->cfgs;
|
||||
int ret;
|
||||
|
||||
+ dev_dbg(udphy->dev, "reinit PHY with USB3=%s and DP=%s (%u lanes) flipped=%s\n",
|
||||
+ str_on_off(udphy->mode & UDPHY_MODE_USB),
|
||||
+ str_on_off(udphy->mode & UDPHY_MODE_DP),
|
||||
+ udphy->dp_lanes, str_yes_no(udphy->flip));
|
||||
+
|
||||
rk_udphy_reset_assert_all(udphy);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
@@ -851,6 +863,8 @@ static int rk_udphy_setup(struct rk_udph
|
||||
{
|
||||
int ret;
|
||||
|
||||
+ dev_dbg(udphy->dev, "enable PHY\n");
|
||||
+
|
||||
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
|
||||
if (ret) {
|
||||
dev_err(udphy->dev, "failed to enable clk\n");
|
||||
@@ -869,6 +883,7 @@ static int rk_udphy_setup(struct rk_udph
|
||||
|
||||
static void rk_udphy_disable(struct rk_udphy *udphy)
|
||||
{
|
||||
+ dev_dbg(udphy->dev, "disable PHY\n");
|
||||
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
|
||||
rk_udphy_reset_assert_all(udphy);
|
||||
}
|
||||
@@ -1308,8 +1323,12 @@ static int rk_udphy_typec_mux_set(struct
|
||||
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
|
||||
|
||||
/* Ignore mux events not involving USB or DP */
|
||||
- if (!rk_udphy_is_supported_mode(state))
|
||||
+ if (!rk_udphy_is_supported_mode(state)) {
|
||||
+ dev_dbg(udphy->dev, "ignore mux event with mode=%lu\n", state->mode);
|
||||
return 0;
|
||||
+ }
|
||||
+
|
||||
+ dev_dbg(udphy->dev, "new mode: %lu\n", state->mode);
|
||||
|
||||
guard(mutex)(&udphy->mutex);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 25/35] phy: rockchip: usbdp: Avoid xHCI SErrors
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:26 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-25-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
The USBDP PHY provides the PIPE clock to the USB3 controller, which
|
||||
means the PHY must be fully running when anything tries to access
|
||||
the xHCI registers.
|
||||
|
||||
When switching between USB3-only, USB3 + DP and DP-only mode, the
|
||||
PHY must be re-initialized resulting in a short period of the PHY
|
||||
being disabled. If the DWC3 driver decides to access the xHCI at
|
||||
this point the system will fail with an SError.
|
||||
|
||||
This patch avoids the problems by disabling the USB3 port before
|
||||
re-initializing it. This does a couple of things:
|
||||
|
||||
- forces phystatus to 0 from GRF (not from PHY)
|
||||
- switches PIPE clock source from PHY to UTMI (safe fallback clock)
|
||||
- num_u3_port=0
|
||||
|
||||
The last part will be ignored, as DWC3 already probed, but the
|
||||
clock re-routing will avoid the SError. There is a small delay
|
||||
afterwards to make sure the mux happened. The datasheet gives
|
||||
no hints how long it takes, so delay time is a guess.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 7 +++++--
|
||||
1 file changed, 5 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1000,12 +1000,15 @@ static int rk_udphy_power_on(struct rk_u
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
udphy->phy_needs_reinit = false;
|
||||
} else if (udphy->phy_needs_reinit) {
|
||||
- if (udphy->mode == UDPHY_MODE_DP)
|
||||
- rk_udphy_u3_port_disable(udphy, true);
|
||||
+ rk_udphy_u3_port_disable(udphy, true);
|
||||
+ udelay(10);
|
||||
|
||||
ret = rk_udphy_init(udphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
+
|
||||
+ if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
|
||||
+ rk_udphy_u3_port_disable(udphy, false);
|
||||
udphy->phy_needs_reinit = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 26/35] phy: rockchip: usbdp: Handle
|
||||
rk_udphy_reset_deassert errors
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:27 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-26-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Handle rk_udphy_reset_deassert returning errors to avoid theoretical
|
||||
(Rockchip reset controller driver does not return errors) SError.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://sashiko.dev/#/message/20260626211151.2332F1F000E9%40smtp.kernel.org
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 25 +++++++++++++++++++------
|
||||
1 file changed, 19 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -803,8 +803,12 @@ static int rk_udphy_init(struct rk_udphy
|
||||
/* Step 1: power on pma and deassert apb rstn */
|
||||
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.low_pwrn, true);
|
||||
|
||||
- rk_udphy_reset_deassert(udphy, "pma_apb");
|
||||
- rk_udphy_reset_deassert(udphy, "pcs_apb");
|
||||
+ ret = rk_udphy_reset_deassert(udphy, "pma_apb");
|
||||
+ if (ret)
|
||||
+ goto assert_resets;
|
||||
+ ret = rk_udphy_reset_deassert(udphy, "pcs_apb");
|
||||
+ if (ret)
|
||||
+ goto assert_resets;
|
||||
|
||||
/* Step 2: set init sequence and phy refclk */
|
||||
ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_init_sequence,
|
||||
@@ -830,8 +834,11 @@ static int rk_udphy_init(struct rk_udphy
|
||||
FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
|
||||
|
||||
/* Step 4: deassert init rstn and wait for 200ns from datasheet */
|
||||
- if (udphy->mode & UDPHY_MODE_USB)
|
||||
- rk_udphy_reset_deassert(udphy, "init");
|
||||
+ if (udphy->mode & UDPHY_MODE_USB) {
|
||||
+ ret = rk_udphy_reset_deassert(udphy, "init");
|
||||
+ if (ret)
|
||||
+ goto assert_resets;
|
||||
+ }
|
||||
|
||||
if (udphy->mode & UDPHY_MODE_DP) {
|
||||
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
|
||||
@@ -843,8 +850,14 @@ static int rk_udphy_init(struct rk_udphy
|
||||
|
||||
/* Step 5: deassert cmn/lane rstn */
|
||||
if (udphy->mode & UDPHY_MODE_USB) {
|
||||
- rk_udphy_reset_deassert(udphy, "cmn");
|
||||
- rk_udphy_reset_deassert(udphy, "lane");
|
||||
+ ret = rk_udphy_reset_deassert(udphy, "cmn");
|
||||
+ if (ret)
|
||||
+ goto assert_resets;
|
||||
+
|
||||
+ ret = rk_udphy_reset_deassert(udphy, "lane");
|
||||
+ if (ret)
|
||||
+ goto assert_resets;
|
||||
+
|
||||
}
|
||||
|
||||
/* Step 6: wait for lock done of pll */
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 27/35] phy: rockchip: usbdp: Only enable USB3 when not
|
||||
in high-speed mode
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:28 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-27-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Ensure that USB3 mode is not accidently enabled during PHY re-init
|
||||
for systems that are configured as high-speed only via DT.
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Reported-by: Sashiko <sashiko-bot@kernel.org>
|
||||
Closes: https://sashiko.dev/#/message/20260626212424.C215E1F000E9%40smtp.kernel.org
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1009,7 +1009,7 @@ static int rk_udphy_power_on(struct rk_u
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- if (udphy->mode & UDPHY_MODE_USB)
|
||||
+ if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
udphy->phy_needs_reinit = false;
|
||||
} else if (udphy->phy_needs_reinit) {
|
||||
@@ -0,0 +1,192 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 28/35] phy: core: add notifier infrastructure
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:29 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-28-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Some PHY devices with multiple ports (e.g. USB3 and DP) require a reset
|
||||
if the configuration changes or cable orientation changes. This is a
|
||||
problem, as the consumer device will run into undefined behavior.
|
||||
|
||||
With the new PHY notifier API introduced in this patch, the consumer
|
||||
driver can hook into reset events coming from a PHY device to handle the
|
||||
PHY going down gracefully.
|
||||
|
||||
Note that this uses -ENOSYS instead of the more sensible -ENOTSUP for
|
||||
the stub functions when GENERIC_PHY is disabled to stay consistent with
|
||||
the existing ones.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/phy-core.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/phy/phy.h | 40 ++++++++++++++++++++++++++++++
|
||||
2 files changed, 105 insertions(+)
|
||||
|
||||
--- a/drivers/phy/phy-core.c
|
||||
+++ b/drivers/phy/phy-core.c
|
||||
@@ -521,6 +521,70 @@ int phy_notify_disconnect(struct phy *ph
|
||||
EXPORT_SYMBOL_GPL(phy_notify_disconnect);
|
||||
|
||||
/**
|
||||
+ * phy_register_notifier() - register a notifier for PHY events
|
||||
+ * @phy: the phy returned by phy_get()
|
||||
+ * @nb: notifier block to register
|
||||
+ *
|
||||
+ * Allows PHY consumers to receive notifications about PHY reset events.
|
||||
+ * PHY providers can signal these events using phy_notify_reset().
|
||||
+ *
|
||||
+ * Returns: %0 if successful, a negative error code otherwise
|
||||
+ */
|
||||
+int phy_register_notifier(struct phy *phy, struct notifier_block *nb)
|
||||
+{
|
||||
+ if (!phy)
|
||||
+ return 0;
|
||||
+
|
||||
+ return blocking_notifier_chain_register(&phy->notifier, nb);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phy_register_notifier);
|
||||
+
|
||||
+/**
|
||||
+ * phy_unregister_notifier() - unregister a notifier for PHY events
|
||||
+ * @phy: the phy returned by phy_get()
|
||||
+ * @nb: notifier block to unregister
|
||||
+ *
|
||||
+ * Returns: %0 if successful, a negative error code otherwise
|
||||
+ */
|
||||
+int phy_unregister_notifier(struct phy *phy, struct notifier_block *nb)
|
||||
+{
|
||||
+ if (!phy)
|
||||
+ return 0;
|
||||
+
|
||||
+ return blocking_notifier_chain_unregister(&phy->notifier, nb);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phy_unregister_notifier);
|
||||
+
|
||||
+/**
|
||||
+ * phy_notify_reset() - notify consumers of a PHY reset event
|
||||
+ * @phy: the phy that is being reset
|
||||
+ * @event: the notification event (PRE_RESET or POST_RESET)
|
||||
+ *
|
||||
+ * Called by PHY providers to notify consumers that the PHY is about to
|
||||
+ * be reset or has completed a reset. This allows consumers to quiesce
|
||||
+ * hardware before the PHY becomes unavailable.
|
||||
+ *
|
||||
+ * This may be called from within PHY provider callbacks (e.g. set_mode,
|
||||
+ * power_on) where phy->mutex is held. Consumer notification handlers must
|
||||
+ * therefore NOT call back into the PHY framework (e.g. phy_power_off,
|
||||
+ * phy_exit) on the same PHY, as this would result in a deadlock.
|
||||
+ *
|
||||
+ * Returns: %0 if successful or no notifiers registered, a negative error
|
||||
+ * code if a notifier returns an error (for PRE_RESET only)
|
||||
+ */
|
||||
+int phy_notify_reset(struct phy *phy, enum phy_notification event)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!phy)
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = blocking_notifier_call_chain(&phy->notifier, event, phy);
|
||||
+ return notifier_to_errno(ret);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phy_notify_reset);
|
||||
+
|
||||
+/**
|
||||
* phy_configure() - Changes the phy parameters
|
||||
* @phy: the phy returned by phy_get()
|
||||
* @opts: New configuration to apply
|
||||
@@ -996,6 +1060,7 @@ struct phy *phy_create(struct device *de
|
||||
device_initialize(&phy->dev);
|
||||
lockdep_register_key(&phy->lockdep_key);
|
||||
mutex_init_with_key(&phy->mutex, &phy->lockdep_key);
|
||||
+ BLOCKING_INIT_NOTIFIER_HEAD(&phy->notifier);
|
||||
|
||||
phy->dev.class = &phy_class;
|
||||
phy->dev.parent = dev;
|
||||
--- a/include/linux/phy/phy.h
|
||||
+++ b/include/linux/phy/phy.h
|
||||
@@ -11,6 +11,7 @@
|
||||
#define __DRIVERS_PHY_H
|
||||
|
||||
#include <linux/err.h>
|
||||
+#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@@ -54,6 +55,16 @@ enum phy_media {
|
||||
};
|
||||
|
||||
/**
|
||||
+ * enum phy_notification - PHY notification events
|
||||
+ * @PHY_NOTIFY_PRE_RESET: PHY is about to be reset, consumers should quiesce
|
||||
+ * @PHY_NOTIFY_POST_RESET: PHY reset is complete, consumers may resume
|
||||
+ */
|
||||
+enum phy_notification {
|
||||
+ PHY_NOTIFY_PRE_RESET,
|
||||
+ PHY_NOTIFY_POST_RESET,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
* union phy_configure_opts - Opaque generic phy configuration
|
||||
*
|
||||
* @mipi_dphy: Configuration set applicable for phys supporting
|
||||
@@ -159,6 +170,7 @@ struct phy_attrs {
|
||||
* @power_count: used to protect when the PHY is used by multiple consumers
|
||||
* @attrs: used to specify PHY specific attributes
|
||||
* @pwr: power regulator associated with the phy
|
||||
+ * @notifier: notifier head for PHY reset events
|
||||
* @debugfs: debugfs directory
|
||||
*/
|
||||
struct phy {
|
||||
@@ -171,6 +183,7 @@ struct phy {
|
||||
int power_count;
|
||||
struct phy_attrs attrs;
|
||||
struct regulator *pwr;
|
||||
+ struct blocking_notifier_head notifier;
|
||||
struct dentry *debugfs;
|
||||
};
|
||||
|
||||
@@ -255,6 +268,9 @@ int phy_reset(struct phy *phy);
|
||||
int phy_calibrate(struct phy *phy);
|
||||
int phy_notify_connect(struct phy *phy, int port);
|
||||
int phy_notify_disconnect(struct phy *phy, int port);
|
||||
+int phy_register_notifier(struct phy *phy, struct notifier_block *nb);
|
||||
+int phy_unregister_notifier(struct phy *phy, struct notifier_block *nb);
|
||||
+int phy_notify_reset(struct phy *phy, enum phy_notification event);
|
||||
static inline int phy_get_bus_width(struct phy *phy)
|
||||
{
|
||||
return phy->attrs.bus_width;
|
||||
@@ -409,6 +425,30 @@ static inline int phy_notify_disconnect(
|
||||
{
|
||||
if (!phy)
|
||||
return 0;
|
||||
+ return -ENOSYS;
|
||||
+}
|
||||
+
|
||||
+static inline int phy_register_notifier(struct phy *phy,
|
||||
+ struct notifier_block *nb)
|
||||
+{
|
||||
+ if (!phy)
|
||||
+ return 0;
|
||||
+ return -ENOSYS;
|
||||
+}
|
||||
+
|
||||
+static inline int phy_unregister_notifier(struct phy *phy,
|
||||
+ struct notifier_block *nb)
|
||||
+{
|
||||
+ if (!phy)
|
||||
+ return 0;
|
||||
+ return -ENOSYS;
|
||||
+}
|
||||
+
|
||||
+static inline int phy_notify_reset(struct phy *phy,
|
||||
+ enum phy_notification event)
|
||||
+{
|
||||
+ if (!phy)
|
||||
+ return 0;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 29/35] usb: dwc3: core: support PHY reset notifications
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:30 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-29-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
On recent Rockchip platforms (at least RK3588 & RK3576), DWC3 IP is used
|
||||
with a USBDP PHY providing USB3 and DP. This PHY needs to be reset when
|
||||
the mode changes, which may happen when plugging in different USB-C
|
||||
devices.
|
||||
|
||||
If the USBDP PHY resets with the DWC3 IP running, its internal state
|
||||
corrupts resulting in the USBDP PHY not being able to lock some PLL
|
||||
clocks, which effectively renders USB3 unusable.
|
||||
|
||||
To fix the issue this adds handling for the new PHY framework reset
|
||||
notifications, which will assert PHYSOFTRST before the actual PHY
|
||||
is disabled and will deassert it once the PHY returns.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/usb/dwc3/core.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/usb/dwc3/core.h | 18 +++++++++
|
||||
2 files changed, 119 insertions(+)
|
||||
|
||||
--- a/drivers/usb/dwc3/core.c
|
||||
+++ b/drivers/usb/dwc3/core.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <linux/pinctrl/devinfo.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/bitfield.h>
|
||||
+#include <linux/phy/phy.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
@@ -865,6 +866,103 @@ err_exit_usb2_phy:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int dwc3_usb3_phy_notify(struct notifier_block *nb,
|
||||
+ unsigned long action, void *data)
|
||||
+{
|
||||
+ struct dwc3_phy_nb *pnb = container_of(nb, struct dwc3_phy_nb, nb);
|
||||
+ struct dwc3 *dwc = pnb->dwc;
|
||||
+ int port = pnb->port_index;
|
||||
+ unsigned long flags;
|
||||
+ u32 reg;
|
||||
+ int ret;
|
||||
+
|
||||
+ switch (action) {
|
||||
+ case PHY_NOTIFY_PRE_RESET:
|
||||
+ /*
|
||||
+ * If already suspended, the resume path will reinit GUSB3PIPECTL
|
||||
+ * via dwc3_core_init(). A forced resume is not possible as that
|
||||
+ * would call phy_init() resulting in a deadlock. Due to the
|
||||
+ * phy_init() in the resume path there is also no need to block
|
||||
+ * async RPM resume on our side, since the PHY synchronizes it
|
||||
+ * for us.
|
||||
+ *
|
||||
+ * pm_runtime_get_if_active() returns 0 when suspended (skip),
|
||||
+ * 1 when active (ref held), or -EINVAL when PM is disabled
|
||||
+ * (device always active). In the -EINVAL case PM ref counting
|
||||
+ * is a no-op, so the unconditional put in POST_RESET is safe.
|
||||
+ */
|
||||
+ ret = pm_runtime_get_if_active(dwc->dev);
|
||||
+ if (!ret)
|
||||
+ return NOTIFY_OK;
|
||||
+
|
||||
+ /*
|
||||
+ * Assert USB3 PHY soft reset within DWC3 before the external
|
||||
+ * PHY resets. This disconnects the PIPE interface, preventing
|
||||
+ * the DWC3 from interfering with PHY reinitialization and
|
||||
+ * avoiding LCPLL lock failures.
|
||||
+ */
|
||||
+ spin_lock_irqsave(&dwc->lock, flags);
|
||||
+ dwc->phy_reset_active |= BIT(port);
|
||||
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(port));
|
||||
+ reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
|
||||
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
|
||||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
+ break;
|
||||
+
|
||||
+ case PHY_NOTIFY_POST_RESET:
|
||||
+ spin_lock_irqsave(&dwc->lock, flags);
|
||||
+ if (!(dwc->phy_reset_active & BIT(port))) {
|
||||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
+ return NOTIFY_OK;
|
||||
+ }
|
||||
+
|
||||
+ dwc->phy_reset_active &= ~BIT(port);
|
||||
+
|
||||
+ /*
|
||||
+ * Deassert PHY soft reset to reconnect the PIPE interface
|
||||
+ * after PHY reinitialization.
|
||||
+ */
|
||||
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(port));
|
||||
+ reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
|
||||
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
|
||||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
+
|
||||
+ pm_runtime_put_autosuspend(dwc->dev);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return NOTIFY_OK;
|
||||
+}
|
||||
+
|
||||
+static void dwc3_phy_register_notifiers(struct dwc3 *dwc)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
|
||||
+ dwc->usb3_phy_nb[i].nb.notifier_call = dwc3_usb3_phy_notify;
|
||||
+ dwc->usb3_phy_nb[i].dwc = dwc;
|
||||
+ dwc->usb3_phy_nb[i].port_index = i;
|
||||
+ phy_register_notifier(dwc->usb3_generic_phy[i],
|
||||
+ &dwc->usb3_phy_nb[i].nb);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void dwc3_phy_unregister_notifiers(struct dwc3 *dwc)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
+ phy_unregister_notifier(dwc->usb3_generic_phy[i],
|
||||
+ &dwc->usb3_phy_nb[i].nb);
|
||||
+
|
||||
+ /* Release any PM references from in-flight resets */
|
||||
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
|
||||
+ if (dwc->phy_reset_active & BIT(i))
|
||||
+ pm_runtime_put_autosuspend(dwc->dev);
|
||||
+ }
|
||||
+ dwc->phy_reset_active = 0;
|
||||
+}
|
||||
+
|
||||
static void dwc3_phy_exit(struct dwc3 *dwc)
|
||||
{
|
||||
int i;
|
||||
@@ -2316,6 +2414,7 @@ int dwc3_core_probe(const struct dwc3_pr
|
||||
|
||||
dwc3_check_params(dwc);
|
||||
dwc3_debugfs_init(dwc);
|
||||
+ dwc3_phy_register_notifiers(dwc);
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
@@ -2328,6 +2427,7 @@ int dwc3_core_probe(const struct dwc3_pr
|
||||
return 0;
|
||||
|
||||
err_exit_debugfs:
|
||||
+ dwc3_phy_unregister_notifiers(dwc);
|
||||
dwc3_debugfs_exit(dwc);
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
dwc3_phy_power_off(dwc);
|
||||
@@ -2384,6 +2484,7 @@ void dwc3_core_remove(struct dwc3 *dwc)
|
||||
|
||||
dwc3_core_exit_mode(dwc);
|
||||
dwc3_debugfs_exit(dwc);
|
||||
+ dwc3_phy_unregister_notifiers(dwc);
|
||||
|
||||
dwc3_core_exit(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
--- a/drivers/usb/dwc3/core.h
|
||||
+++ b/drivers/usb/dwc3/core.h
|
||||
@@ -11,6 +11,7 @@
|
||||
#ifndef __DRIVERS_USB_DWC3_CORE_H
|
||||
#define __DRIVERS_USB_DWC3_CORE_H
|
||||
|
||||
+#include <linux/atomic.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
@@ -1001,6 +1002,20 @@ struct dwc3_glue_ops {
|
||||
void (*pre_run_stop)(struct dwc3 *dwc, bool is_on);
|
||||
};
|
||||
|
||||
+struct dwc3;
|
||||
+
|
||||
+/**
|
||||
+ * struct dwc3_phy_nb - wrapper for PHY notifier block
|
||||
+ * @nb: notifier block
|
||||
+ * @dwc: back-pointer to the DWC3 controller
|
||||
+ * @port_index: USB3 port index this notifier is registered for
|
||||
+ */
|
||||
+struct dwc3_phy_nb {
|
||||
+ struct notifier_block nb;
|
||||
+ struct dwc3 *dwc;
|
||||
+ u8 port_index;
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* struct dwc3 - representation of our controller
|
||||
* @drd_work: workqueue used for role swapping
|
||||
@@ -1064,6 +1079,7 @@ struct dwc3_glue_ops {
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to array of USB2 PHYs
|
||||
* @usb3_generic_phy: pointer to array of USB3 PHYs
|
||||
+ * @usb3_phy_nb: notifier blocks for USB3 PHY reset events
|
||||
* @num_usb2_ports: number of USB2 ports
|
||||
* @num_usb3_ports: number of USB3 ports
|
||||
* @phys_ready: flag to indicate that PHYs are ready
|
||||
@@ -1224,6 +1240,8 @@ struct dwc3 {
|
||||
|
||||
struct phy *usb2_generic_phy[DWC3_USB2_MAX_PORTS];
|
||||
struct phy *usb3_generic_phy[DWC3_USB3_MAX_PORTS];
|
||||
+ struct dwc3_phy_nb usb3_phy_nb[DWC3_USB3_MAX_PORTS];
|
||||
+ u8 phy_reset_active;
|
||||
|
||||
u8 num_usb2_ports;
|
||||
u8 num_usb3_ports;
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 30/35] phy: rockchip: usbdp: Add phy reset notification
|
||||
support
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:31 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-30-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
To resolve issues with running into permanent "cmn ana lcpll lock
|
||||
timeout" errors after a few device replugs, add support for reset
|
||||
notifications, which will be handled by the DWC3 driver to gracefully
|
||||
handle the PHY being disabled. This avoids corrupting the controller's
|
||||
internal state and the PIPE interface between the USB3 controller and
|
||||
the PHY, thus fixing the issue.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 19 +++++++++++++++++--
|
||||
1 file changed, 17 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1005,24 +1005,39 @@ static int rk_udphy_power_on(struct rk_u
|
||||
}
|
||||
|
||||
if (udphy->status == UDPHY_MODE_NONE) {
|
||||
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
|
||||
+
|
||||
+ rk_udphy_u3_port_disable(udphy, true);
|
||||
+ udelay(10);
|
||||
+
|
||||
ret = rk_udphy_setup(udphy);
|
||||
- if (ret)
|
||||
+ if (ret) {
|
||||
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
return ret;
|
||||
+ }
|
||||
|
||||
if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
udphy->phy_needs_reinit = false;
|
||||
+
|
||||
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
} else if (udphy->phy_needs_reinit) {
|
||||
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
|
||||
+
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
udelay(10);
|
||||
|
||||
ret = rk_udphy_init(udphy);
|
||||
- if (ret)
|
||||
+ if (ret) {
|
||||
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
return ret;
|
||||
+ }
|
||||
|
||||
if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
udphy->phy_needs_reinit = false;
|
||||
+
|
||||
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
}
|
||||
|
||||
udphy->status |= mode;
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 31/35] phy: rockchip: usbdp: Drop -EPROBE_DEFER hack
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:32 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-31-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
The hack to return -EPROBE_DEFER when the lcpll lock timeouts is no
|
||||
longer needed. The driver now does a reset during its PHY init, which
|
||||
avoids the problem.
|
||||
|
||||
Since rk_udphy_status_check() is called after the probe, it should not
|
||||
return -EPROBE_DEFER.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 12 +-----------
|
||||
1 file changed, 1 insertion(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -750,17 +750,7 @@ static int rk_udphy_status_check(struct
|
||||
(val & CMN_ANA_LCPLL_LOCK_DONE), 200, 100000);
|
||||
if (ret) {
|
||||
dev_err(udphy->dev, "cmn ana lcpll lock timeout\n");
|
||||
- /*
|
||||
- * If earlier software (U-Boot) enabled USB once already
|
||||
- * the PLL may have problems locking on the first try.
|
||||
- * It will be successful on the second try, so for the
|
||||
- * time being a -EPROBE_DEFER will solve the issue.
|
||||
- *
|
||||
- * This requires further investigation to understand the
|
||||
- * root cause, especially considering that the driver is
|
||||
- * asserting all reset lines at probe time.
|
||||
- */
|
||||
- return -EPROBE_DEFER;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
if (!udphy->flip) {
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 32/35] phy: rockchip: usbdp: Rename mode to hw_mode
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:33 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-32-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Rename mode field to hw_mode to make clear that this is the modes
|
||||
currently supported by the hardware, but not necessarily requested
|
||||
by software. I.e. it is only set by either the USB-C state machine
|
||||
or device-tree if the PHY is used in a fixed routing setup.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 48 +++++++++++++++----------------
|
||||
1 file changed, 24 insertions(+), 24 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -174,7 +174,7 @@ struct rk_udphy {
|
||||
/* PHY status management */
|
||||
bool flip;
|
||||
bool phy_needs_reinit;
|
||||
- u8 mode;
|
||||
+ u8 hw_mode; /* modes currently supported by hardware */
|
||||
u8 status;
|
||||
|
||||
/* utilized for USB */
|
||||
@@ -579,18 +579,18 @@ static void rk_udphy_dp_lane_enable(stru
|
||||
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
|
||||
}
|
||||
|
||||
-static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
|
||||
+static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 hw_mode)
|
||||
{
|
||||
- if (udphy->mode == mode)
|
||||
+ if (udphy->hw_mode == hw_mode)
|
||||
return;
|
||||
|
||||
udphy->phy_needs_reinit = true;
|
||||
- udphy->mode = mode;
|
||||
+ udphy->hw_mode = hw_mode;
|
||||
}
|
||||
|
||||
static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
|
||||
{
|
||||
- u8 mode;
|
||||
+ u8 hw_mode;
|
||||
|
||||
switch (state) {
|
||||
case TYPEC_DP_STATE_C:
|
||||
@@ -599,7 +599,7 @@ static void rk_udphy_set_typec_state(str
|
||||
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
- mode = UDPHY_MODE_DP;
|
||||
+ hw_mode = UDPHY_MODE_DP;
|
||||
udphy->dp_lanes = 4;
|
||||
break;
|
||||
|
||||
@@ -616,12 +616,12 @@ static void rk_udphy_set_typec_state(str
|
||||
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
}
|
||||
- mode = UDPHY_MODE_DP_USB;
|
||||
+ hw_mode = UDPHY_MODE_DP_USB;
|
||||
udphy->dp_lanes = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
- rk_udphy_mode_set(udphy, mode);
|
||||
+ rk_udphy_mode_set(udphy, hw_mode);
|
||||
}
|
||||
|
||||
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
|
||||
@@ -744,7 +744,7 @@ static int rk_udphy_status_check(struct
|
||||
int ret;
|
||||
|
||||
/* LCPLL check */
|
||||
- if (udphy->mode & UDPHY_MODE_USB) {
|
||||
+ if (udphy->hw_mode & UDPHY_MODE_USB) {
|
||||
ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_LCPLL_DONE_OFFSET,
|
||||
val, (val & CMN_ANA_LCPLL_AFC_DONE) &&
|
||||
(val & CMN_ANA_LCPLL_LOCK_DONE), 200, 100000);
|
||||
@@ -779,15 +779,15 @@ static int rk_udphy_init(struct rk_udphy
|
||||
int ret;
|
||||
|
||||
dev_dbg(udphy->dev, "reinit PHY with USB3=%s and DP=%s (%u lanes) flipped=%s\n",
|
||||
- str_on_off(udphy->mode & UDPHY_MODE_USB),
|
||||
- str_on_off(udphy->mode & UDPHY_MODE_DP),
|
||||
+ str_on_off(udphy->hw_mode & UDPHY_MODE_USB),
|
||||
+ str_on_off(udphy->hw_mode & UDPHY_MODE_DP),
|
||||
udphy->dp_lanes, str_yes_no(udphy->flip));
|
||||
|
||||
rk_udphy_reset_assert_all(udphy);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
/* enable rx lfps for usb */
|
||||
- if (udphy->mode & UDPHY_MODE_USB)
|
||||
+ if (udphy->hw_mode & UDPHY_MODE_USB)
|
||||
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.rx_lfps, true);
|
||||
|
||||
/* Step 1: power on pma and deassert apb rstn */
|
||||
@@ -824,13 +824,13 @@ static int rk_udphy_init(struct rk_udphy
|
||||
FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
|
||||
|
||||
/* Step 4: deassert init rstn and wait for 200ns from datasheet */
|
||||
- if (udphy->mode & UDPHY_MODE_USB) {
|
||||
+ if (udphy->hw_mode & UDPHY_MODE_USB) {
|
||||
ret = rk_udphy_reset_deassert(udphy, "init");
|
||||
if (ret)
|
||||
goto assert_resets;
|
||||
}
|
||||
|
||||
- if (udphy->mode & UDPHY_MODE_DP) {
|
||||
+ if (udphy->hw_mode & UDPHY_MODE_DP) {
|
||||
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
|
||||
CMN_DP_INIT_RSTN,
|
||||
FIELD_PREP(CMN_DP_INIT_RSTN, 0x1));
|
||||
@@ -839,7 +839,7 @@ static int rk_udphy_init(struct rk_udphy
|
||||
udelay(1);
|
||||
|
||||
/* Step 5: deassert cmn/lane rstn */
|
||||
- if (udphy->mode & UDPHY_MODE_USB) {
|
||||
+ if (udphy->hw_mode & UDPHY_MODE_USB) {
|
||||
ret = rk_udphy_reset_deassert(udphy, "cmn");
|
||||
if (ret)
|
||||
goto assert_resets;
|
||||
@@ -898,7 +898,7 @@ static int rk_udphy_parse_lane_mux_data(
|
||||
num_lanes = device_property_count_u32(udphy->dev, "rockchip,dp-lane-mux");
|
||||
if (num_lanes < 0) {
|
||||
dev_dbg(udphy->dev, "no dp-lane-mux, following dp alt mode\n");
|
||||
- udphy->mode = UDPHY_MODE_USB;
|
||||
+ udphy->hw_mode = UDPHY_MODE_USB;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -927,10 +927,10 @@ static int rk_udphy_parse_lane_mux_data(
|
||||
}
|
||||
}
|
||||
|
||||
- udphy->mode = UDPHY_MODE_DP;
|
||||
+ udphy->hw_mode = UDPHY_MODE_DP;
|
||||
udphy->dp_lanes = num_lanes;
|
||||
if (num_lanes == 1 || num_lanes == 2) {
|
||||
- udphy->mode |= UDPHY_MODE_USB;
|
||||
+ udphy->hw_mode |= UDPHY_MODE_USB;
|
||||
udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP) ||
|
||||
(udphy->lane_mux_sel[1] == PHY_LANE_MUX_DP);
|
||||
}
|
||||
@@ -989,7 +989,7 @@ static int rk_udphy_power_on(struct rk_u
|
||||
{
|
||||
int ret;
|
||||
|
||||
- if (!(udphy->mode & mode)) {
|
||||
+ if (!(udphy->hw_mode & mode)) {
|
||||
dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
|
||||
return 0;
|
||||
}
|
||||
@@ -1006,7 +1006,7 @@ static int rk_udphy_power_on(struct rk_u
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
|
||||
+ if (!udphy->hs && udphy->hw_mode & UDPHY_MODE_USB)
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
udphy->phy_needs_reinit = false;
|
||||
|
||||
@@ -1023,7 +1023,7 @@ static int rk_udphy_power_on(struct rk_u
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
|
||||
+ if (!udphy->hs && udphy->hw_mode & UDPHY_MODE_USB)
|
||||
rk_udphy_u3_port_disable(udphy, false);
|
||||
udphy->phy_needs_reinit = false;
|
||||
|
||||
@@ -1037,7 +1037,7 @@ static int rk_udphy_power_on(struct rk_u
|
||||
|
||||
static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
|
||||
{
|
||||
- if (!(udphy->mode & mode)) {
|
||||
+ if (!(udphy->hw_mode & mode)) {
|
||||
dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
|
||||
return;
|
||||
}
|
||||
@@ -1296,7 +1296,7 @@ static int rk_udphy_usb3_phy_init(struct
|
||||
guard(mutex)(&udphy->mutex);
|
||||
|
||||
/* DP only or high-speed, disable U3 port */
|
||||
- if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
|
||||
+ if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs) {
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
return 0;
|
||||
}
|
||||
@@ -1311,7 +1311,7 @@ static int rk_udphy_usb3_phy_exit(struct
|
||||
guard(mutex)(&udphy->mutex);
|
||||
|
||||
/* DP only or high-speed */
|
||||
- if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
|
||||
+ if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs)
|
||||
return 0;
|
||||
|
||||
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 33/35] phy: rockchip: usbdp: Fix power state handling
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:34 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-33-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Restructure power state handling by introducing sw_mode in addition
|
||||
to the hw_mode field, so that the PHY knows about the currently
|
||||
supported modes from the hardware perspective, the current modes
|
||||
requested by software and the actual hardware status.
|
||||
|
||||
Now anything updating either the hardware or software state can simply
|
||||
update the status field and call rk_udphy_update_power_state().
|
||||
|
||||
This makes it a lot more obvious what is going on and also fixes a few
|
||||
potential resource leaks identified by Sashiko as a side-effect. For
|
||||
example if USB3 is requested by software while the USB-C is in DP-only
|
||||
mode, things are decently handled after this.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 143 +++++++++++++++++++-----------
|
||||
1 file changed, 90 insertions(+), 53 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -173,9 +173,10 @@ struct rk_udphy {
|
||||
|
||||
/* PHY status management */
|
||||
bool flip;
|
||||
- bool phy_needs_reinit;
|
||||
+ bool phy_needs_reinit; /* lane mux changed */
|
||||
u8 hw_mode; /* modes currently supported by hardware */
|
||||
- u8 status;
|
||||
+ u8 sw_mode; /* modes currently requested */
|
||||
+ u8 status; /* current PHY power state */
|
||||
|
||||
/* utilized for USB */
|
||||
bool hs; /* flag for high-speed */
|
||||
@@ -985,70 +986,95 @@ static int rk_udphy_parse_dt(struct rk_u
|
||||
return rk_udphy_reset_init(udphy, dev);
|
||||
}
|
||||
|
||||
-static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
|
||||
+static int rk_udphy_update_power_state(struct rk_udphy *udphy)
|
||||
{
|
||||
+ bool usb3_port_enable;
|
||||
+ u8 target_mode;
|
||||
int ret;
|
||||
|
||||
- if (!(udphy->hw_mode & mode)) {
|
||||
- dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
|
||||
+ /*
|
||||
+ * Initialize PHY mode according to the hardware setup (either described
|
||||
+ * in DT or negotiated via the Type-C controller) instead of requesting
|
||||
+ * only the needed PHY side, because that would break the USB/DP data
|
||||
+ * streams when the other PHY is being requested. This is not an issue
|
||||
+ * during the Type-C negotiation as that happens during the hotplug phase
|
||||
+ * and not during normal operation. Also disable everything if the
|
||||
+ * software has not requested anything, as there shouldn't be any active
|
||||
+ * data streams in that case.
|
||||
+ */
|
||||
+ target_mode = udphy->hw_mode;
|
||||
+ if (udphy->sw_mode == UDPHY_MODE_NONE)
|
||||
+ target_mode = UDPHY_MODE_NONE;
|
||||
+
|
||||
+ usb3_port_enable = !udphy->hs && (target_mode & UDPHY_MODE_USB);
|
||||
+
|
||||
+ if (!udphy->phy_needs_reinit && udphy->status == target_mode) {
|
||||
+ if (udphy->sw_mode & UDPHY_MODE_USB)
|
||||
+ rk_udphy_u3_port_disable(udphy, !usb3_port_enable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
- if (udphy->status == UDPHY_MODE_NONE) {
|
||||
- phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
|
||||
+ /* Avoid to re-init disabled PHY */
|
||||
+ if (udphy->status == target_mode && target_mode == UDPHY_MODE_NONE)
|
||||
+ return 0;
|
||||
|
||||
+ /*
|
||||
+ * Inform DWC3 driver, that we are about to reset the PHY, so that it can
|
||||
+ * assert its PIPE reset lines and avoid DWC3 getting into a buggy state.
|
||||
+ * This is intentionally done for a PHY disable, since that also changes
|
||||
+ * the clocks routed to the PHY.
|
||||
+ */
|
||||
+ ret = phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /*
|
||||
+ * Disable USB3 port, which among other things re-routes a DWC3 clock to
|
||||
+ * avoid SErrors when the DWC3 registers are accessed while the PHY is
|
||||
+ * disabled. This is only done, when the DWC3 is running as the accessed
|
||||
+ * GRF registers and in PD_USB.
|
||||
+ */
|
||||
+ if (udphy->sw_mode & UDPHY_MODE_USB) {
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
udelay(10);
|
||||
+ }
|
||||
|
||||
+ if (udphy->status == UDPHY_MODE_NONE) {
|
||||
+ /* Power up (incl. clocks) */
|
||||
ret = rk_udphy_setup(udphy);
|
||||
if (ret) {
|
||||
phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
return ret;
|
||||
}
|
||||
-
|
||||
- if (!udphy->hs && udphy->hw_mode & UDPHY_MODE_USB)
|
||||
- rk_udphy_u3_port_disable(udphy, false);
|
||||
- udphy->phy_needs_reinit = false;
|
||||
-
|
||||
- phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
- } else if (udphy->phy_needs_reinit) {
|
||||
- phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_PRE_RESET);
|
||||
-
|
||||
- rk_udphy_u3_port_disable(udphy, true);
|
||||
- udelay(10);
|
||||
-
|
||||
+ } else if (target_mode == UDPHY_MODE_NONE) {
|
||||
+ /* Power down (incl. clocks) */
|
||||
+ rk_udphy_disable(udphy);
|
||||
+ } else {
|
||||
+ /* Mode change => re-init */
|
||||
ret = rk_udphy_init(udphy);
|
||||
if (ret) {
|
||||
phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
return ret;
|
||||
}
|
||||
-
|
||||
- if (!udphy->hs && udphy->hw_mode & UDPHY_MODE_USB)
|
||||
- rk_udphy_u3_port_disable(udphy, false);
|
||||
- udphy->phy_needs_reinit = false;
|
||||
-
|
||||
- phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
}
|
||||
|
||||
- udphy->status |= mode;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
+ /* Ensure USB3 support is enabled when supported */
|
||||
+ if (udphy->sw_mode & UDPHY_MODE_USB)
|
||||
+ rk_udphy_u3_port_disable(udphy, !usb3_port_enable);
|
||||
|
||||
-static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
|
||||
-{
|
||||
- if (!(udphy->hw_mode & mode)) {
|
||||
- dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- if (!udphy->status)
|
||||
- return;
|
||||
+ /*
|
||||
+ * Inform DWC3, that we are done with the reset, so that it can deassert
|
||||
+ * its PIPE reset line. This is sent in pair with a PRE_RESET allowing
|
||||
+ * consumer driver to do paired resource requests (e.g. clocks) in their
|
||||
+ * notification handlers. As we reroute the clocks, its also fine to
|
||||
+ * send this after completely disabling the PHY.
|
||||
+ */
|
||||
+ phy_notify_reset(udphy->phy_u3, PHY_NOTIFY_POST_RESET);
|
||||
|
||||
- udphy->status &= ~mode;
|
||||
+ udphy->status = target_mode;
|
||||
+ udphy->phy_needs_reinit = false;
|
||||
|
||||
- if (udphy->status == UDPHY_MODE_NONE)
|
||||
- rk_udphy_disable(udphy);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int rk_udphy_dp_phy_power_on(struct phy *phy)
|
||||
@@ -1057,11 +1083,15 @@ static int rk_udphy_dp_phy_power_on(stru
|
||||
int ret;
|
||||
|
||||
scoped_guard(mutex, &udphy->mutex) {
|
||||
+ udphy->sw_mode |= UDPHY_MODE_DP;
|
||||
+
|
||||
phy_set_bus_width(phy, udphy->dp_lanes);
|
||||
|
||||
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
|
||||
- if (ret)
|
||||
+ ret = rk_udphy_update_power_state(udphy);
|
||||
+ if (ret) {
|
||||
+ udphy->sw_mode &= ~UDPHY_MODE_DP;
|
||||
return ret;
|
||||
+ }
|
||||
|
||||
rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
|
||||
|
||||
@@ -1084,10 +1114,10 @@ static int rk_udphy_dp_phy_power_off(str
|
||||
|
||||
guard(mutex)(&udphy->mutex);
|
||||
|
||||
- rk_udphy_dp_lane_enable(udphy, 0);
|
||||
- rk_udphy_power_off(udphy, UDPHY_MODE_DP);
|
||||
+ udphy->sw_mode &= ~UDPHY_MODE_DP;
|
||||
|
||||
- return 0;
|
||||
+ rk_udphy_dp_lane_enable(udphy, 0);
|
||||
+ return rk_udphy_update_power_state(udphy);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1292,16 +1322,24 @@ static const struct phy_ops rk_udphy_dp_
|
||||
static int rk_udphy_usb3_phy_init(struct phy *phy)
|
||||
{
|
||||
struct rk_udphy *udphy = phy_get_drvdata(phy);
|
||||
+ int ret;
|
||||
|
||||
guard(mutex)(&udphy->mutex);
|
||||
|
||||
- /* DP only or high-speed, disable U3 port */
|
||||
- if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs) {
|
||||
+ if (udphy->hs) {
|
||||
rk_udphy_u3_port_disable(udphy, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
- return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
|
||||
+ udphy->sw_mode |= UDPHY_MODE_USB;
|
||||
+
|
||||
+ ret = rk_udphy_update_power_state(udphy);
|
||||
+ if (ret) {
|
||||
+ udphy->sw_mode &= ~UDPHY_MODE_USB;
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int rk_udphy_usb3_phy_exit(struct phy *phy)
|
||||
@@ -1310,13 +1348,12 @@ static int rk_udphy_usb3_phy_exit(struct
|
||||
|
||||
guard(mutex)(&udphy->mutex);
|
||||
|
||||
- /* DP only or high-speed */
|
||||
- if (!(udphy->hw_mode & UDPHY_MODE_USB) || udphy->hs)
|
||||
+ if (udphy->hs)
|
||||
return 0;
|
||||
|
||||
- rk_udphy_power_off(udphy, UDPHY_MODE_USB);
|
||||
+ udphy->sw_mode &= ~UDPHY_MODE_USB;
|
||||
|
||||
- return 0;
|
||||
+ return rk_udphy_update_power_state(udphy);
|
||||
}
|
||||
|
||||
static const struct phy_ops rk_udphy_usb3_phy_ops = {
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 34/35] phy: rockchip: usbdp: Re-init PHY on mux change
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:35 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-34-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Ensure that the right part of the PHY are powered up when the
|
||||
mode changes. This ensures the PHY is re-initialized in the
|
||||
following two scenarios, which are currently broken:
|
||||
|
||||
- cable orientation changes without DP being involved
|
||||
- switching from DP-only into a mode with USB support
|
||||
|
||||
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -1392,7 +1392,7 @@ static int rk_udphy_typec_mux_set(struct
|
||||
|
||||
rk_udphy_set_typec_state(udphy, state->mode);
|
||||
|
||||
- return 0;
|
||||
+ return rk_udphy_update_power_state(udphy);
|
||||
}
|
||||
|
||||
static void rk_udphy_typec_mux_unregister(void *data)
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v13 35/35] phy: rockchip: usbdp: Add USB-C state without DP
|
||||
enabled
|
||||
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Date: Tue, 14 Jul 2026 21:26:36 +0200
|
||||
Message-Id: <20260714-rockchip-usbdp-cleanup-v13-35-6cb3e769d4c5@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
The driver currently only differs between 4 lanes DP mode or combined DP
|
||||
+ USB3 mode. This makes sense from a lane routing point of view, as the
|
||||
hardware only has 2 lanes of USB3.
|
||||
|
||||
But adding a separate state for USB-only helps with power management,
|
||||
since we always power up all PHY parts according to the current hardware
|
||||
setup to avoid data stream interruptions. Even if some lanes are muxed
|
||||
to the DP controller there is no need to keep the DP side enabled if
|
||||
something without DP AltMode is plugged into USB-C.
|
||||
|
||||
This potentially triggers some more USB reconnections during the PD
|
||||
AltMode negotiation when switching from USB-only to combined USB+DP
|
||||
mode. This should be fine, as the cable is freshly plugged at this
|
||||
point.
|
||||
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 57 ++++++++++++++++++-------------
|
||||
1 file changed, 33 insertions(+), 24 deletions(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -580,32 +580,14 @@ static void rk_udphy_dp_lane_enable(stru
|
||||
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
|
||||
}
|
||||
|
||||
-static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 hw_mode)
|
||||
+static void rk_udphy_set_lane_mux(struct rk_udphy *udphy)
|
||||
{
|
||||
- if (udphy->hw_mode == hw_mode)
|
||||
- return;
|
||||
-
|
||||
- udphy->phy_needs_reinit = true;
|
||||
- udphy->hw_mode = hw_mode;
|
||||
-}
|
||||
-
|
||||
-static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
|
||||
-{
|
||||
- u8 hw_mode;
|
||||
-
|
||||
- switch (state) {
|
||||
- case TYPEC_DP_STATE_C:
|
||||
- case TYPEC_DP_STATE_E:
|
||||
+ if (udphy->dp_lanes == 4) {
|
||||
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
- hw_mode = UDPHY_MODE_DP;
|
||||
- udphy->dp_lanes = 4;
|
||||
- break;
|
||||
-
|
||||
- case TYPEC_DP_STATE_D:
|
||||
- default:
|
||||
+ } else {
|
||||
if (udphy->flip) {
|
||||
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
||||
@@ -617,12 +599,39 @@ static void rk_udphy_set_typec_state(str
|
||||
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
||||
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
||||
}
|
||||
- hw_mode = UDPHY_MODE_DP_USB;
|
||||
- udphy->dp_lanes = 2;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 hw_mode, u8 dp_lanes)
|
||||
+{
|
||||
+ if (udphy->hw_mode == hw_mode && udphy->dp_lanes == dp_lanes)
|
||||
+ return;
|
||||
+
|
||||
+ udphy->phy_needs_reinit = true;
|
||||
+ udphy->hw_mode = hw_mode;
|
||||
+ udphy->dp_lanes = dp_lanes;
|
||||
+}
|
||||
+
|
||||
+static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
|
||||
+{
|
||||
+ switch (state) {
|
||||
+ case TYPEC_DP_STATE_C:
|
||||
+ case TYPEC_DP_STATE_E:
|
||||
+ rk_udphy_mode_set(udphy, UDPHY_MODE_DP, 4);
|
||||
+ break;
|
||||
+
|
||||
+ case TYPEC_DP_STATE_D:
|
||||
+ rk_udphy_mode_set(udphy, UDPHY_MODE_DP_USB, 2);
|
||||
+ break;
|
||||
+
|
||||
+ case TYPEC_STATE_SAFE:
|
||||
+ case TYPEC_STATE_USB:
|
||||
+ default:
|
||||
+ rk_udphy_mode_set(udphy, UDPHY_MODE_USB, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
- rk_udphy_mode_set(udphy, hw_mode);
|
||||
+ rk_udphy_set_lane_mux(udphy);
|
||||
}
|
||||
|
||||
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
From: https://github.com/ntbowen
|
||||
Date: Sun, 2 Feb 2025 17:00:00 +0800
|
||||
Subject: [PATCH] phy: rockchip: usbdp: fix probe defer handling in clock init
|
||||
|
||||
The devm_clk_bulk_get_all() function returns a negative error code on
|
||||
failure, including -EPROBE_DEFER when a clock provider is not yet ready.
|
||||
However, rk_udphy_clk_init() only checks if the return value is less
|
||||
than 1 and returns -ENODEV, which prevents the deferred probe mechanism
|
||||
from retrying later.
|
||||
|
||||
This causes USB initialization to fail on RK3576 boards like Photonicat2
|
||||
where the USBDP PHY depends on the 'utmi' clock from u2phy0, which may
|
||||
not be ready during the initial probe attempt.
|
||||
|
||||
Fix this by properly propagating the error code from devm_clk_bulk_get_all().
|
||||
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-usbdp.c | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
||||
@@ -431,7 +431,10 @@ static int rk_udphy_clk_init(struct rk_u
|
||||
int i;
|
||||
|
||||
udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks);
|
||||
- if (udphy->num_clks < 1)
|
||||
+ if (udphy->num_clks < 0)
|
||||
+ return dev_err_probe(dev, udphy->num_clks,
|
||||
+ "failed to get clocks\n");
|
||||
+ if (udphy->num_clks == 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* used for configure phy reference clock frequency */
|
||||
Reference in New Issue
Block a user