[PATCH 3/4] usb: dwc2: Add USB DRD support on CV1800 series SoC
Inochi Amaoto
inochiama at gmail.com
Mon Nov 9 23:06:24 PST 2026
On CV1800 series SoC, it has a problem that it enters the role switch
with a persistent Session End Detected interrupt. As a result, GOTGINT
bit 2 remains set after write-one-to-clear accesses and prevents a
normal boot.
Keeping the BVALID override asserted in host mode avoids the observed
interrupt storm while allowing host enumeration and device operation.
As the BVALOVAL field is device only in the datasheet, this change is
added as a quirk.
Signed-off-by: Inochi Amaoto <inochiama at gmail.com>
---
drivers/usb/dwc2/core.h | 3 +++
drivers/usb/dwc2/drd.c | 29 +++++++++++++++++++++--------
drivers/usb/dwc2/params.c | 1 +
3 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 298b418e6859..52ac64f67f41 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -357,6 +357,8 @@ enum dwc2_ep0_state {
* case.
* 0 - No (default)
* 1 - Yes
+ * @force_bvalid_in_host: Set the BVALID override value to 1 in host mode to
+ * avoid persistent Session End Detected interrupts.
* @power_down: Specifies whether the controller support power_down.
* If power_down is enabled, the controller will enter
* power_down in both peripheral and host mode when
@@ -460,6 +462,7 @@ struct dwc2_core_params {
bool reload_ctl;
bool uframe_sched;
bool external_id_pin_ctl;
+ bool force_bvalid_in_host;
int power_down;
#define DWC2_POWER_DOWN_PARAM_NONE 0
diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c
index 1ad3804cbc80..4f7441a87c85 100644
--- a/drivers/usb/dwc2/drd.c
+++ b/drivers/usb/dwc2/drd.c
@@ -28,10 +28,13 @@ static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
gotgctl = dwc2_readl(hsotg, GOTGCTL);
dwc2_ovr_gotgctl(gotgctl);
gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
- if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
+ if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST) {
gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
- else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
+ if (hsotg->params.force_bvalid_in_host)
+ gotgctl |= GOTGCTL_BVALOVAL;
+ } else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL) {
gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
+ }
dwc2_writel(hsotg, gotgctl, GOTGCTL);
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -45,18 +48,26 @@ static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
/* Check if A-Session is already in the right state */
- if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
+ if ((valid && (gotgctl & GOTGCTL_ASESVLD) &&
+ (!hsotg->params.force_bvalid_in_host ||
+ (dwc2_is_host_mode(hsotg) && (gotgctl & GOTGCTL_BVALOVAL) &&
+ (gotgctl & GOTGCTL_AVALOVAL)))) ||
(!valid && !(gotgctl & GOTGCTL_ASESVLD)))
return -EALREADY;
/* Always enable overrides to handle the resume case */
dwc2_ovr_gotgctl(gotgctl);
- gotgctl &= ~GOTGCTL_BVALOVAL;
- if (valid)
+ if (valid) {
gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
- else
- gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
+ if (hsotg->params.force_bvalid_in_host)
+ gotgctl |= GOTGCTL_BVALOVAL;
+ else
+ gotgctl &= ~GOTGCTL_BVALOVAL;
+ } else {
+ gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL |
+ GOTGCTL_VBVALOVAL);
+ }
dwc2_writel(hsotg, gotgctl, GOTGCTL);
return 0;
@@ -67,7 +78,9 @@ static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
/* Check if B-Session is already in the right state */
- if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
+ if ((valid && (gotgctl & GOTGCTL_BSESVLD) &&
+ (!hsotg->params.force_bvalid_in_host ||
+ (dwc2_is_device_mode(hsotg) && !(gotgctl & GOTGCTL_AVALOVAL)))) ||
(!valid && !(gotgctl & GOTGCTL_BSESVLD)))
return -EALREADY;
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 091bfcfef753..16db9c3c1991 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -222,6 +222,7 @@ static void dwc2_set_cv1800_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
+ p->force_bvalid_in_host = true;
p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->host_dma = false;
--
2.56.0
More information about the linux-riscv
mailing list