[PATCH] usb: dwc3: cascade usb-role down to the phys with phy_set_mode_ext

Fabrice Gasnier posted 1 patch 1 month, 1 week ago
drivers/usb/dwc3/core.c | 44 +++++++++++++++++++++++++++++++----------
drivers/usb/dwc3/core.h |  5 ++++-
drivers/usb/dwc3/drd.c  | 10 ++++++++--
3 files changed, 46 insertions(+), 13 deletions(-)
[PATCH] usb: dwc3: cascade usb-role down to the phys with phy_set_mode_ext
Posted by Fabrice Gasnier 1 month, 1 week ago
From: Pankaj Dev <pankaj.dev@st.com>

dwc3_usb_role_switch_set callback provides usb_role data for
each cable change event, usb_role information needs to be passed
to the phy interface (phy_set_mode), each time the event happens.

Currently usb_role "none" is not used, also the phy_set_mode is only
called when there is a mode change.

Modifications here pass the usb_role to phy_set_mode, also phy_set_mode
is called each time the role switch event happens.

Aim is to support role-switch for stm32mp25, to properly enable/disable
pull-up on D+ and Vbus valid entry of the PHYs depending on usb-role:
USB_ROLE_NONE, USB_ROLE_DEVICE or USB_ROLE_HOST.

Signed-off-by: Pankaj Dev <pankaj.dev@st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
---
Tested on top of:
https://lore.kernel.org/linux-phy/20260816213849.1044073-1-marex@nabladev.com/
---
 drivers/usb/dwc3/core.c | 44 +++++++++++++++++++++++++++++++----------
 drivers/usb/dwc3/core.h |  5 ++++-
 drivers/usb/dwc3/drd.c  | 10 ++++++++--
 3 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index ceb49f2f8004..82a061e0d5f4 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -184,8 +184,27 @@ static void __dwc3_set_mode(struct work_struct *work)
 	if (!desired_dr_role)
 		goto out;
 
-	if (desired_dr_role == dwc->current_dr_role)
+	if (desired_dr_role == dwc->current_dr_role) {
+		switch (dwc->current_dr_role) {
+		case DWC3_GCTL_PRTCAP_HOST:
+			for (i = 0; i < dwc->num_usb2_ports; i++)
+				phy_set_mode_ext(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST,
+						 dwc->submode);
+			for (i = 0; i < dwc->num_usb3_ports; i++)
+				phy_set_mode_ext(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST,
+						 dwc->submode);
+			break;
+		case DWC3_GCTL_PRTCAP_DEVICE:
+			phy_set_mode_ext(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE,
+					 dwc->submode);
+			phy_set_mode_ext(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE,
+					 dwc->submode);
+			break;
+		default:
+			break;
+		}
 		goto out;
+	}
 
 	if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
 		goto out;
@@ -249,9 +268,11 @@ static void __dwc3_set_mode(struct work_struct *work)
 				otg_set_vbus(dwc->usb2_phy->otg, true);
 
 			for (i = 0; i < dwc->num_usb2_ports; i++)
-				phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
+				phy_set_mode_ext(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST,
+						 dwc->submode);
 			for (i = 0; i < dwc->num_usb3_ports; i++)
-				phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
+				phy_set_mode_ext(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST,
+						 dwc->submode);
 
 			if (dwc->dis_split_quirk) {
 				reg = dwc3_readl(dwc, DWC3_GUCTL3);
@@ -267,8 +288,8 @@ static void __dwc3_set_mode(struct work_struct *work)
 
 		if (dwc->usb2_phy)
 			otg_set_vbus(dwc->usb2_phy->otg, false);
-		phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
-		phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
+		phy_set_mode_ext(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE, dwc->submode);
+		phy_set_mode_ext(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE, dwc->submode);
 
 		ret = dwc3_gadget_init(dwc);
 		if (ret)
@@ -287,7 +308,7 @@ static void __dwc3_set_mode(struct work_struct *work)
 	mutex_unlock(&dwc->mutex);
 }
 
-void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
+void dwc3_set_mode_ext(struct dwc3 *dwc, u32 mode, int submode)
 {
 	unsigned long flags;
 
@@ -296,6 +317,7 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
 
 	spin_lock_irqsave(&dwc->lock, flags);
 	dwc->desired_dr_role = mode;
+	dwc->submode = submode;
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
 	queue_work(system_freezable_wq, &dwc->drd_work);
@@ -1628,8 +1650,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 
 		if (dwc->usb2_phy)
 			otg_set_vbus(dwc->usb2_phy->otg, false);
-		phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
-		phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
+		phy_set_mode_ext(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE, USB_ROLE_DEVICE);
+		phy_set_mode_ext(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE, USB_ROLE_DEVICE);
 
 		ret = dwc3_gadget_init(dwc);
 		if (ret)
@@ -1641,9 +1663,11 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 		if (dwc->usb2_phy)
 			otg_set_vbus(dwc->usb2_phy->otg, true);
 		for (i = 0; i < dwc->num_usb2_ports; i++)
-			phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
+			phy_set_mode_ext(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST,
+					 USB_ROLE_HOST);
 		for (i = 0; i < dwc->num_usb3_ports; i++)
-			phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
+			phy_set_mode_ext(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST,
+					 USB_ROLE_HOST);
 
 		ret = dwc3_host_init(dwc);
 		if (ret)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e0dee9d28740..6da52676ba3d 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1244,6 +1244,7 @@ struct dwc3 {
 	enum usb_dr_mode	dr_mode;
 	u32			current_dr_role;
 	u32			desired_dr_role;
+	int			submode;
 	struct extcon_dev	*edev;
 	struct notifier_block	edev_nb;
 	enum usb_phy_interface	hsphy_mode;
@@ -1589,7 +1590,9 @@ struct dwc3_gadget_ep_cmd_params {
 
 /* prototypes */
 void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
-void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
+#define dwc3_set_mode(dwc3, mode) \
+	dwc3_set_mode_ext(dwc3, mode, USB_ROLE_NONE)
+void dwc3_set_mode_ext(struct dwc3 *dwc, u32 mode, int submode);
 u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
 
 #define DWC3_IP_IS(_ip)							\
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index f3e37d383627..55533b2d2343 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -447,6 +447,7 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
 
 #if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
 #define ROLE_SWITCH 1
+static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw);
 static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
 				    enum usb_role role)
 {
@@ -469,7 +470,8 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
 	}
 
 	dwc3_pre_set_role(dwc, role);
-	dwc3_set_mode(dwc, mode);
+	dwc3_set_mode_ext(dwc, mode, role);
+
 	return 0;
 }
 
@@ -505,15 +507,19 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
 {
 	struct usb_role_switch_desc dwc3_role_switch = {NULL};
 	u32 mode;
+	int submode = USB_ROLE_NONE;
 
 	dwc->role_switch_default_mode = usb_get_role_switch_default_mode(dwc->dev);
 	if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) {
 		mode = DWC3_GCTL_PRTCAP_HOST;
+		submode = USB_ROLE_HOST;
 	} else {
+		if (dwc->role_switch_default_mode == USB_DR_MODE_PERIPHERAL)
+			submode = USB_ROLE_DEVICE;
 		dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
 		mode = DWC3_GCTL_PRTCAP_DEVICE;
 	}
-	dwc3_set_mode(dwc, mode);
+	dwc3_set_mode_ext(dwc, mode, submode);
 
 	dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
 	dwc3_role_switch.set = dwc3_usb_role_switch_set;
-- 
2.43.0