169 lines
5.7 KiB
Diff
169 lines
5.7 KiB
Diff
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")) {
|