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

Fabrice Gasnier <[email protected]>
Newsgroups org.kernel.vger.linux-usb,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Pankaj Dev <[email protected]>

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 <[email protected]>
Signed-off-by: Fabrice Gasnier <[email protected]>
---
Tested on top of:
https://lore.kernel.org/linux-phy/[email protected]/
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.