If dr_mode == "otg", we start by default in PERIPHERAL mode. Keep track of current role in "current_dr_role" whenever dwc3_set_mode() is called. When debugfs/mode is changed AND we're in dual-role mode, handle the switch by stopping and starting the respective host/gadget controllers. Signed-off-by: Roger Quadros <rogerq@xxxxxx> --- drivers/usb/dwc3/core.c | 21 +++++++++++--------- drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/debugfs.c | 49 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 369bab1..bf917c2 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -108,6 +108,8 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); reg |= DWC3_GCTL_PRTCAPDIR(mode); dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + dwc->current_dr_role = mode; } u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) @@ -277,7 +279,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_event_buffers_setup(struct dwc3 *dwc) +int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -862,13 +864,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) } break; case USB_DR_MODE_OTG: - ret = dwc3_host_init(dwc); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to initialize host\n"); - return ret; - } - + /* start in peripheral role by default */ + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { if (ret != -EPROBE_DEFER) @@ -894,7 +891,13 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) dwc3_host_exit(dwc); break; case USB_DR_MODE_OTG: - dwc3_host_exit(dwc); + /* role might have changed since start */ + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) { + dwc3_host_exit(dwc); + /* Add back UDC to match dwc3_gadget_exit() */ + usb_add_gadget_udc(dwc->dev, &dwc->gadget); + } + dwc3_gadget_exit(dwc); break; default: diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 7ffdee5..adb04af 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -785,6 +785,7 @@ struct dwc3_scratchpad_array { * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents * @dr_mode: requested mode of operation + * @current_dr_role: current role of operation when in dual-role mode * @hsphy_mode: UTMI phy mode, one of following: * - USBPHY_INTERFACE_MODE_UTMI * - USBPHY_INTERFACE_MODE_UTMIW @@ -901,6 +902,7 @@ struct dwc3 { size_t regs_size; enum usb_dr_mode dr_mode; + u32 current_dr_role; enum usb_phy_interface hsphy_mode; u32 fladj; @@ -1167,6 +1169,7 @@ struct dwc3_gadget_ep_cmd_params { /* prototypes */ void dwc3_set_mode(struct dwc3 *dwc, u32 mode); u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); +int dwc3_event_buffers_setup(struct dwc3 *dwc); /* check whether we are on the DWC_usb3 core */ static inline bool dwc3_is_usb3(struct dwc3 *dwc) diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 31926dd..a74a83b 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -327,19 +327,54 @@ static ssize_t dwc3_mode_write(struct file *file, return -EFAULT; if (!strncmp(buf, "host", 4)) - mode |= DWC3_GCTL_PRTCAP_HOST; + mode = DWC3_GCTL_PRTCAP_HOST; if (!strncmp(buf, "device", 6)) - mode |= DWC3_GCTL_PRTCAP_DEVICE; + mode = DWC3_GCTL_PRTCAP_DEVICE; if (!strncmp(buf, "otg", 3)) - mode |= DWC3_GCTL_PRTCAP_OTG; + mode = DWC3_GCTL_PRTCAP_OTG; - if (mode) { - spin_lock_irqsave(&dwc->lock, flags); - dwc3_set_mode(dwc, mode); - spin_unlock_irqrestore(&dwc->lock, flags); + if (!mode) + return -EINVAL; + + if (mode == dwc->current_dr_role) + goto exit; + + /* prevent role switching if we're not dual-role */ + if (dwc->dr_mode != USB_DR_MODE_OTG) + return -EINVAL; + + /* stop old role */ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_exit(dwc); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + usb_del_gadget_udc(&dwc->gadget); + break; + default: + break; + } + + /* switch PRTCAP mode. updates current_dr_role */ + spin_lock_irqsave(&dwc->lock, flags); + dwc3_set_mode(dwc, mode); + spin_unlock_irqrestore(&dwc->lock, flags); + + /* start new role */ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_init(dwc); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + dwc3_event_buffers_setup(dwc); + usb_add_gadget_udc(dwc->dev, &dwc->gadget); + break; + default: + break; } +exit: return count; } -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html