[PATCH] usb: dwc3: cascade usb-role down to the phys with phy_set_mode_ext
From: Fabrice Gasnier
Date: Mon Aug 17 2026 - 13:33:27 EST
From: Pankaj Dev <pankaj.dev@xxxxxx>
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@xxxxxx>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@xxxxxxxxxxx>
---
Tested on top of:
https://lore.kernel.org/linux-phy/20260816213849.1044073-1-marex@xxxxxxxxxxxx/
---
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