[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1491222031-18120-3-git-send-email-rogerq@ti.com>
Date: Mon, 3 Apr 2017 15:20:30 +0300
From: Roger Quadros <rogerq@...com>
To: <balbi@...nel.org>
CC: <vivek.gautam@...eaurora.org>, <linux-usb@...r.kernel.org>,
<linux-kernel@...r.kernel.org>, Roger Quadros <rogerq@...com>
Subject: [PATCH v3 2/3] usb: dwc3: make role-switching work with debugfs/mode
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@...com>
---
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
Powered by blists - more mailing lists