mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
usb: dwc3: improve gadget wakeup from resume signal
The dwc3 wakeup and suspend interrupt handler are not perfect,
and it can't support usb gadget auto suspend function to save
power. For UVC device, the auto suspend function is necessary
and helpful.
With this patch, it enable DWC3_DEVTEN_EOPFEN by default for
software to handle suspend interrupt. And for Rockchip platforms,
they usually power down DRAM when system enter deep sleep, so
this patch disable the dwc3 irq in dwc3_suspend to avoid dwc3
controller access the DRAM for handling dwc3 event if wakeup
from USB Host resume signal.
By default, the gadget wakeup from system suspend is disabled.
The user can add "wakeup-source" property in DTS dwc3 node to
enable it like this:
&usbdrd_dwc3 {
status = "okay";
wakeup-source;
};
Signed-off-by: William Wu <william.wu@rock-chips.com>
Change-Id: Iaf9d642ae1ef6ed12e66a15158706de6d73d5124
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user