diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index d4f2caac39d8..bc0a1114852f 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1267,6 +1267,9 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dev_err(dev, "failed to initialize gadget\n"); return ret; } + + if (dwc->uwk_en) + device_init_wakeup(dev, true); break; case USB_DR_MODE_HOST: /* @@ -1317,6 +1320,9 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dev_err(dev, "failed to initialize dual-role\n"); return ret; } + + if (dwc->uwk_en) + device_init_wakeup(dev, true); break; default: dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); @@ -1460,6 +1466,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) "snps,tx-fifo-resize"); dwc->xhci_warm_reset_on_suspend_quirk = device_property_read_bool(dev, "snps,xhci-warm-reset-on-suspend-quirk"); + dwc->uwk_en = device_property_read_bool(dev, + "wakeup-source"); dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; @@ -2014,6 +2022,12 @@ static int dwc3_suspend(struct device *dev) struct dwc3 *dwc = dev_get_drvdata(dev); int ret; + if (dwc->uwk_en) { + dwc3_gadget_disable_irq(dwc); + synchronize_irq(dwc->irq_gadget); + return 0; + } + if (pm_runtime_suspended(dwc->dev)) return 0; @@ -2053,6 +2067,11 @@ static int dwc3_resume(struct device *dev) struct dwc3 *dwc = dev_get_drvdata(dev); int ret; + if (dwc->uwk_en) { + dwc3_gadget_enable_irq(dwc); + return 0; + } + if (pm_runtime_suspended(dwc->dev)) return 0; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b2a489510336..c98fbfe14478 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1056,6 +1056,7 @@ struct dwc3_scratchpad_array { * false otherwise. * @en_runtime: true when need runtime PM management. For example, RK3399 need * reset dwc3 and usb3phy to support typec interface. + * @uwk_en: true when enable usb wakeup from host resume signal. * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. */ @@ -1251,6 +1252,7 @@ struct dwc3 { unsigned fifo_resize_status:1; unsigned drd_connected:1; unsigned en_runtime:1; + unsigned uwk_en:1; u16 imod_interval; }; @@ -1453,6 +1455,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); +void dwc3_gadget_disable_irq(struct dwc3 *dwc); +void dwc3_gadget_enable_irq(struct dwc3 *dwc); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } @@ -1472,6 +1476,10 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) { return 0; } +static inline void dwc3_gadget_enable_irq(struct dwc3 *dwc) +{ } +static inline void dwc3_gadget_disable_irq(struct dwc3 *dwc) +{ } #endif #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 534a3bf4c679..bf8d854e93bf 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2001,6 +2001,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) link_state = DWC3_DSTS_USBLNKST(reg); switch (link_state) { + case DWC3_LINK_STATE_U0: case DWC3_LINK_STATE_RESET: case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ @@ -2010,6 +2011,15 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) return -EINVAL; } + /* + * dwc3 gadget wakeup from host resume signal + * when the whole system enter suspend. + */ + if (link_state == DWC3_LINK_STATE_U0) { + dwc->link_state = link_state; + return 0; + } + 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"); @@ -2145,7 +2155,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) return ret; } -static void dwc3_gadget_enable_irq(struct dwc3 *dwc) +void dwc3_gadget_enable_irq(struct dwc3 *dwc) { u32 reg; @@ -2157,7 +2167,8 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc) DWC3_DEVTEN_WKUPEVTEN | DWC3_DEVTEN_CONNECTDONEEN | DWC3_DEVTEN_USBRSTEN | - DWC3_DEVTEN_DISCONNEVTEN); + DWC3_DEVTEN_DISCONNEVTEN | + DWC3_DEVTEN_EOPFEN); if (dwc->revision < DWC3_REVISION_250A) reg |= DWC3_DEVTEN_ULSTCNGEN; @@ -2165,7 +2176,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); } -static void dwc3_gadget_disable_irq(struct dwc3 *dwc) +void dwc3_gadget_disable_irq(struct dwc3 *dwc) { /* mask all interrupts */ dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); @@ -3296,18 +3307,21 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) */ } -static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) +static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, unsigned int evtinfo) { + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; /* * TODO take core out of low power mode when that's * implemented. */ - if (dwc->gadget_driver && dwc->gadget_driver->resume) { + if (dwc->gadget_driver && dwc->gadget_driver->resume && dwc->uwk_en) { spin_unlock(&dwc->lock); dwc->gadget_driver->resume(&dwc->gadget); spin_lock(&dwc->lock); } + + dwc->link_state = next; } static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, @@ -3413,7 +3427,8 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc, { enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; - if (dwc->link_state != next && next == DWC3_LINK_STATE_U3) + if (dwc->link_state != next && next == DWC3_LINK_STATE_U3 && + dwc->uwk_en) dwc3_suspend_gadget(dwc); dwc->link_state = next; @@ -3460,7 +3475,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, break; case DWC3_DEVICE_EVENT_WAKEUP: dev_info(dwc->dev, "device wakeup\n"); - dwc3_gadget_wakeup_interrupt(dwc); + dwc3_gadget_wakeup_interrupt(dwc, event->event_info); break; case DWC3_DEVICE_EVENT_HIBER_REQ: if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,