Most link changes will, of course, happen with the help of a matching host HW, but in some cases we might want to debug very low level details about the link and exposing this to debugfs sounds like a good plan. This is a preparation for such setup. Signed-off-by: Felipe Balbi <balbi@xxxxxx> --- drivers/usb/dwc3/gadget.c | 52 +++++++++++++++++++++++++++++++++++--------- drivers/usb/dwc3/gadget.h | 1 + 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 70a390e..2bbba94 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -89,6 +89,42 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) return 0; } +/** + * dwc3_gadget_set_link_state - Sets USB Link to a particular State + * @dwc: pointer to our context structure + * @state: the state to put link into + * + * Caller should take care of locking. This function will + * return 0 on success or -EINVAL. + */ +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) +{ + int retries = 100; + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + + /* set requested state */ + reg |= DWC3_DCTL_ULSTCHNGREQ(state); + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* wait for a change in DSTS */ + while (--retries) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + /* in HS, means ON */ + if (DWC3_DSTS_USBLNKST(reg) == state) + return 0; + + usleep_range(500, 1500); + } + + dev_vdbg(dwc->dev, "link state change request timed out\n"); + + return -ETIMEDOUT; +} + void dwc3_map_buffer_to_dma(struct dwc3_request *req) { struct dwc3 *dwc = req->dep->dwc; @@ -1076,17 +1112,11 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) goto out; } - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - - /* - * Switch link state to Recovery. In HS/FS/LS this means - * RemoteWakeup Request - */ - reg |= DWC3_DCTL_ULSTCHNG_RECOVERY; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); - - /* wait for at least 2000us */ - usleep_range(2000, 2500); + ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV); + if (ret < 0) { + dev_err(dwc->dev, "failed to put link in Recovery\n"); + goto out; + } /* write zeroes to Link Change Request */ reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 2cf6f29..152b6de 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -101,6 +101,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); -- 1.7.8.2 -- 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