[PATCH 3/3] usb: dwc2: Handle OTG interrupts that GINTSTS does not flag
From: Joseph Telaak via B4 Relay
Date: Wed Nov 04 2026 - 02:25:32 EST
From: Chuanhong Guo <gch981213@xxxxxxxxx>
The DWC OTG 3.30a found on Siflower SF19A2890 has battery charger
support enabled. It triggers MultVallpChng interrupt (bit 20 of
GOTGINT) but doesn't set OTGInt in GINTSTS. As a result, this
interrupt is never handled, and Linux disables USB interrupt
because "nobody cares".
Handle OTG interrupt in IRQ handler regardless of whether the
OTGInt bit in GINTSTS is set or not.
On a GL.iNet GL-SFT1200 it shows differently: after a few replugs of a
USB stick the system stops making progress, the console repeating its
last message with the same timestamp, until a power cycle.
Signed-off-by: Chuanhong Guo <gch981213@xxxxxxxxx>
[jt: only on cores with the new otg_int_unflagged parameter, which
SF19A2890 sets, in the one place that already handles flagged OTG
interrupts and with IRQ_HANDLED returned; the others still handle OTG
interrupts only when GINTSTS flags them; what a GL-SFT1200 does without
the patch, subject]
Signed-off-by: Joseph Telaak <joseph@xxxxxxxxxx>
---
drivers/usb/dwc2/core.h | 6 ++++++
drivers/usb/dwc2/core_intr.c | 5 ++++-
drivers/usb/dwc2/params.c | 1 +
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 015a5fb78..288baaaf4 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -367,6 +367,11 @@ enum dwc2_ep0_state {
* @no_clock_gating: Specifies whether to avoid clock gating feature.
* 0 - No (use clock gating)
* 1 - Yes (avoid it)
+ * @otg_int_unflagged: Specifies whether the core raises OTG interrupts
+ * without setting OTGINT in GINTSTS, so that GOTGINT
+ * has to be checked on every interrupt.
+ * 0 - No
+ * 1 - Yes
* @lpm: Enable LPM support.
* 0 - No
* 1 - Yes
@@ -466,6 +471,7 @@ struct dwc2_core_params {
#define DWC2_POWER_DOWN_PARAM_PARTIAL 1
#define DWC2_POWER_DOWN_PARAM_HIBERNATION 2
bool no_clock_gating;
+ bool otg_int_unflagged;
bool lpm;
bool lpm_clock_gating;
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 9565e6a52..e7bb9709c 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -874,8 +874,11 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
if (gintsts & GINTSTS_MODEMIS)
dwc2_handle_mode_mismatch_intr(hsotg);
- if (gintsts & GINTSTS_OTGINT)
+ if ((gintsts & GINTSTS_OTGINT) ||
+ (hsotg->params.otg_int_unflagged && dwc2_readl(hsotg, GOTGINT))) {
dwc2_handle_otg_intr(hsotg);
+ retval = IRQ_HANDLED;
+ }
if (gintsts & GINTSTS_CONIDSTSCHNG)
dwc2_handle_conn_id_status_change_intr(hsotg);
if (gintsts & GINTSTS_DISCONNINT)
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index f6379e616..22ab9ce13 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -243,6 +243,7 @@ static void dwc2_set_sf19a2890_params(struct dwc2_hsotg *hsotg)
p->max_transfer_size = 65535;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT;
+ p->otg_int_unflagged = true;
}
static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
--
Git-155)