diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 53b13d9e55fb..aec7ad30aebc 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1150,6 +1150,7 @@ struct dwc3_scratchpad_array { * @bh_handled_evt_cnt: no. of events handled by tasklet per interrupt * @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt * @last_run_stop: timestamp denoting the last run_stop update + * @is_remote_wakeup_enabled: remote wakeup status from host perspective */ struct dwc3 { struct work_struct drd_work; @@ -1395,6 +1396,7 @@ struct dwc3 { u32 gen2_tx_de_emph2; u32 gen2_tx_de_emph3; ktime_t last_run_stop; + bool is_remote_wakeup_enabled; }; #define INCRX_BURST_MODE 0 diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 893337090574..980eae30e933 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1838,6 +1838,21 @@ int usb_gsi_ep_op(struct usb_ep *ep, void *op_data, enum gsi_ep_op op) } EXPORT_SYMBOL(usb_gsi_ep_op); +/* Return true if host is supporting remote wakeup functionality. */ +bool usb_get_remote_wakeup_status(struct usb_gadget *gadget) +{ + struct dwc3 *dwc = gadget_to_dwc(gadget); + unsigned long flags; + bool rw = false; + + spin_lock_irqsave(&dwc->lock, flags); + rw = dwc->is_remote_wakeup_enabled ? true : false; + spin_unlock_irqrestore(&dwc->lock, flags); + + return rw; +} +EXPORT_SYMBOL(usb_get_remote_wakeup_status); + /** * Configure a USB DBM ep to work in BAM mode. * diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 26dbbb9494db..bce34a0b6d0e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -340,6 +340,9 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, usb_status |= 1 << USB_DEV_STAT_U1_ENABLED; if (reg & DWC3_DCTL_INITU2ENA) usb_status |= 1 << USB_DEV_STAT_U2_ENABLED; + } else { + usb_status |= dwc->is_remote_wakeup_enabled << + USB_DEVICE_REMOTE_WAKEUP; } break; @@ -460,6 +463,9 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc, switch (wValue) { case USB_DEVICE_REMOTE_WAKEUP: + dbg_log_string("remote wakeup :%s", + (set ? "enabled" : "disabled")); + dwc->is_remote_wakeup_enabled = set; break; /* * 9.4.1 says only only for SS, in AddressState only for diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 33db59228a56..8db92f03d551 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1993,6 +1993,12 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) int ret; spin_lock_irqsave(&dwc->lock, flags); + if (!dwc->is_remote_wakeup_enabled) { + spin_unlock_irqrestore(&dwc->lock, flags); + dbg_log_string("remote wakeup not supported\n"); + return -EINVAL; + } + ret = __dwc3_gadget_wakeup(dwc); spin_unlock_irqrestore(&dwc->lock, flags); @@ -2508,6 +2514,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, } dwc->gadget_driver = driver; + dwc->is_remote_wakeup_enabled = false; /* * For DRD, this might get called by gadget driver during bootup @@ -2539,6 +2546,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g) spin_lock_irqsave(&dwc->lock, flags); dwc->gadget_driver = NULL; + dwc->is_remote_wakeup_enabled = false; spin_unlock_irqrestore(&dwc->lock, flags); dbg_event(0xFF, "fwq_started", 0); @@ -3362,6 +3370,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->link_state = DWC3_LINK_STATE_U0; + dwc->is_remote_wakeup_enabled = false; } static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) diff --git a/include/linux/usb/dwc3-msm.h b/include/linux/usb/dwc3-msm.h index 5cb818ce4809..6762da8c2c5f 100644 --- a/include/linux/usb/dwc3-msm.h +++ b/include/linux/usb/dwc3-msm.h @@ -136,6 +136,7 @@ int msm_data_fifo_config(struct usb_ep *ep, unsigned long addr, u32 size, bool msm_dwc3_reset_ep_after_lpm(struct usb_gadget *gadget); int msm_dwc3_reset_dbm_ep(struct usb_ep *ep); int dwc3_msm_release_ss_lane(struct device *dev); +bool usb_get_remote_wakeup_status(struct usb_gadget *gadget); #else static inline struct usb_ep *usb_ep_autoconfig_by_name( struct usb_gadget *gadget, struct usb_endpoint_descriptor *desc, @@ -163,6 +164,8 @@ static inline int msm_dwc3_reset_dbm_ep(struct usb_ep *ep) { return -ENODEV; } static inline int dwc3_msm_release_ss_lane(struct device *dev) { return -ENODEV; } +static bool __maybe_unused usb_get_remote_wakeup_status(struct usb_gadget *gadget) +{ return false; } #endif #endif /* __LINUX_USB_DWC3_MSM_H */