[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