lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20250924095644.79259-2-victor.krawiec@arturia.com>
Date: Wed, 24 Sep 2025 11:56:45 +0200
From: Victor Krawiec <victor.krawiec@...uria.com>
To: heiko@...ech.de,
	vkoul@...nel.org,
	kishon@...nel.org
Cc: linux-rockchip@...ts.infradead.org,
	linux-phy@...ts.infradead.org,
	linux-kernel@...r.kernel.org,
	linux-arm-kernel@...ts.infradead.org,
	Victor Krawiec <victor.krawiec@...uria.com>
Subject: [PATCH] phy: rockchip-inno-usb2: fix otg state machine when in USB-C DFP mode

In rockchip-inno-usb2 phy driver, we use a state machine called
'otg_sm_work' to dynamically manage power consumption for phy otg-port.
This state machine relies on EXTCON_USB_HOST state to manage host role.
The EXTCON_USB_HOST state is set depending on the OTG ID pin

On a device that does role switching with a USB-C connector (for example
using a fusb302 detection chip) the ID pin does not exists. Therefore the
EXTCON_USB_HOST state is never set. When in DFP mode this causes the state
machine to go in the DCP state instead of going in the A_HOST state. The
end result for the user is that the system does not enumerate connected
FS/HS devices.

This patch adds a 'set_mode' callback that allows the phy driver to be
notified when the role changes. The state machine can now check if the
device is in DFP state and do work accordingly"

Signed-off-by: Victor Krawiec <victor.krawiec@...uria.com>
---
This patch has been made by analyzing RK3399 TRM and Rockchip's vendor
tree which is based on a 6.6 Android kernel. The patch feels a bit like
hacking the state machine by bypassing the EXTCON_USB_HOST. I'm new to USB
driver development so it is very likely that I missed something. Please
see this patch as opening a new topic rather than as a clever solution.

Instead of using a boolean 'data_role_dfp' in the 'set_role' callback I
tried using 'extcon_set_state_sync' to set EXTCON_USB_HOST state.
Unfortunately since the drivers subscribes to the event it triggers two
concurrent executions of the state machine which makes the code very hard
to follow (see schema below)

Lastly the patch only works when using the same sequence as Rockchip's
vendor tree to put the phy in charger detection mode. This sequence is 
provided as a comment in their tree:
/* Set the PHY in charger detection mode.
 * The conditions for charger detection:
 * 1. Set the PHY in normal mode to keep the UTMI_CLK on.
 * 2. Set the utmi_opmode in non-driving mode.
 * 3. Set the utmi_xcvrselect to FS speed.
 * 4. Set the utmi_termselect to FS speed.
 * 5. Enable the DP/DM pulldown resistor.
 */
Thus the change in RK3399 registers.

bvalid_irq
    |
    |
otg_sm_work
    |            set_mode
    |               |
    |               |
    |        EXTCON_USB_HOST
    |               |
    |               |
    |       rockchip_otg_event
    |               |
    |               |
    |          otg_sm_work
    |               |
    |               |

 drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 40 +++++++++++++++++--
 1 file changed, 37 insertions(+), 3 deletions(-)

diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index b0f23690ec30..e8d99f67086e 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -26,6 +26,7 @@
 #include <linux/mfd/syscon.h>
 #include <linux/usb/of.h>
 #include <linux/usb/otg.h>
+#include <linux/usb/role.h>
 
 #define BIT_WRITEABLE_SHIFT	16
 #define SCHEDULE_DELAY		(60 * HZ)
@@ -190,6 +191,7 @@ struct rockchip_usb2phy_cfg {
  * @suspended: phy suspended flag.
  * @vbus_attached: otg device vbus status.
  * @host_disconnect: usb host disconnect status.
+ * @data_role_dfp: dual role dfp/ufp status.
  * @bvalid_irq: IRQ number assigned for vbus valid rise detection.
  * @id_irq: IRQ number assigned for ID pin detection.
  * @ls_irq: IRQ number assigned for linestate detection.
@@ -210,6 +212,7 @@ struct rockchip_usb2phy_port {
 	bool		suspended;
 	bool		vbus_attached;
 	bool            host_disconnect;
+	bool		data_role_dfp;
 	int		bvalid_irq;
 	int		id_irq;
 	int		ls_irq;
@@ -648,11 +651,37 @@ static int rockchip_usb2phy_exit(struct phy *phy)
 	return 0;
 }
 
+static int rockchip_usb2phy_set_mode(struct phy *phy,
+				     enum phy_mode mode, int submode)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+
+	dev_dbg(&rport->phy->dev, "%s: mode=%d\n", __func__, (int)mode);
+
+	if (rport->port_id != USB2PHY_PORT_OTG)
+		return 0;
+
+	switch (mode) {
+	case PHY_MODE_USB_DEVICE:
+		rport->data_role_dfp = false;
+		break;
+	case PHY_MODE_USB_HOST:
+		rport->data_role_dfp = true;
+		break;
+	default:
+		dev_info(&rport->phy->dev, "illegal mode\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static const struct phy_ops rockchip_usb2phy_ops = {
 	.init		= rockchip_usb2phy_init,
 	.exit		= rockchip_usb2phy_exit,
 	.power_on	= rockchip_usb2phy_power_on,
 	.power_off	= rockchip_usb2phy_power_off,
+	.set_mode	= rockchip_usb2phy_set_mode,
 	.owner		= THIS_MODULE,
 };
 
@@ -682,9 +711,12 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
 			rockchip_usb2phy_power_off(rport->phy);
 		fallthrough;
 	case OTG_STATE_B_IDLE:
-		if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) {
+		if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0 ||
+			rport->data_role_dfp) {
 			dev_dbg(&rport->phy->dev, "usb otg host connect\n");
 			rport->state = OTG_STATE_A_HOST;
+			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
+			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
 			rockchip_usb2phy_power_on(rport->phy);
 			return;
 		} else if (vbus_attach) {
@@ -729,6 +761,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
 			notify_charger = true;
 			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
 			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
+			rockchip_usb2phy_power_off(rport->phy);
 		}
 
 		if (rport->vbus_attached != vbus_attach) {
@@ -759,7 +792,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
 		if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) {
 			dev_dbg(&rport->phy->dev, "usb otg host disconnect\n");
 			rport->state = OTG_STATE_B_IDLE;
-			rockchip_usb2phy_power_off(rport->phy);
+			sch_work = true;
 		}
 		break;
 	default:
@@ -1294,6 +1327,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
 	 */
 	rport->suspended = true;
 	rport->vbus_attached = false;
+	rport->data_role_dfp = false;
 
 	mutex_init(&rport->mutex);
 
@@ -1878,7 +1912,7 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
 			}
 		},
 		.chg_det = {
-			.opmode		= { 0xe454, 3, 0, 5, 1 },
+			.opmode		= { 0xe454, 8, 0, 0, 0x1d7 },
 			.cp_det		= { 0xe2ac, 2, 2, 0, 1 },
 			.dcp_det	= { 0xe2ac, 1, 1, 0, 1 },
 			.dp_det		= { 0xe2ac, 0, 0, 0, 1 },

base-commit: f83ec76bf285bea5727f478a68b894f5543ca76e
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ