USB host sends function suspend and resume notifications to device through SET_FEATURE setup packet. This packet is directed to the interface to which function suspend/resume is intended to. Add support to handle this packet by delegating the request to composite layer. To exit from function suspend the interface needs to trigger a function wakeup in case it wants to initate a data transfer. Expose a new gadget op so that an interface can send function wakeup request to the host. Signed-off-by: Elson Roy Serrao <quic_eserrao@xxxxxxxxxxx> --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/ep0.c | 12 ++++-------- drivers/usb/dwc3/gadget.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 3306b1c..e08e522 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -519,6 +519,7 @@ #define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04 #define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05 +#define DWC3_DGCMD_XMIT_DEV 0x07 #define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 #define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a #define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 4cc3d3a..cedc890 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -30,6 +30,8 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep); static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req); +static int dwc3_ep0_delegate_req(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl); static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep, dma_addr_t buf_dma, u32 len, u32 type, bool chain) @@ -365,7 +367,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, * Function Remote Wake Capable D0 * Function Remote Wakeup D1 */ - break; + return dwc3_ep0_delegate_req(dwc, ctrl); case USB_RECIP_ENDPOINT: dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); @@ -511,13 +513,7 @@ static int dwc3_ep0_handle_intf(struct dwc3 *dwc, switch (wValue) { case USB_INTRF_FUNC_SUSPEND: - /* - * REVISIT: Ideally we would enable some low power mode here, - * however it's unclear what we should be doing here. - * - * For now, we're not doing anything, just making sure we return - * 0 so USB Command Verifier tests pass without any errors. - */ + ret = dwc3_ep0_delegate_req(dwc, ctrl); break; default: ret = -EINVAL; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d6697da..0b2947e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2357,6 +2357,35 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) return ret; } +static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int interface_id) +{ + int ret = 0; + u32 reg; + struct dwc3 *dwc = gadget_to_dwc(g); + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + /* + * If the link is in LPM, first bring the link to U0 + * before triggering function wakeup. Ideally this + * needs to be expanded to other LPMs as well in + * addition to U3 + */ + if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U3) { + dwc3_gadget_wakeup(g); + return -EAGAIN; + } + + ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_XMIT_DEV, + 0x1 | (interface_id << 4)); + + if (ret) + dev_err(dwc->dev, "Function wakeup HW command failed, ret %d\n", + ret); + + return ret; +} + static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, int is_selfpowered) { @@ -2978,6 +3007,7 @@ static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable) static const struct usb_gadget_ops dwc3_gadget_ops = { .get_frame = dwc3_gadget_get_frame, .wakeup = dwc3_gadget_wakeup, + .func_wakeup = dwc3_gadget_func_wakeup, .set_selfpowered = dwc3_gadget_set_selfpowered, .pullup = dwc3_gadget_pullup, .udc_start = dwc3_gadget_start, -- 2.7.4