* [PATCH 1/4] usb: dwc2: gadget: honor pullup requests in core_connect
2026-11-10 7:06 [PATCH 0/4] usb: dwc2: Add USB DRD support on CV1800 Series SoC Inochi Amaoto
@ 2026-11-10 7:06 ` Inochi Amaoto
2026-11-10 7:06 ` [PATCH 2/4] usb: dwc2: drd: Sync the generic PHY mode with role requests Inochi Amaoto
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Inochi Amaoto @ 2026-11-10 7:06 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen Wang,
Inochi Amaoto, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Minas Harutyunyan, Greg Kroah-Hartman,
Longbin Li, Dinh Nguyen, Paul Zimmerman, Felipe Balbi
Cc: devicetree, sophgo, linux-riscv, linux-kernel, linux-usb, Yixun Lan
The connector ID work initializes the peripheral controller and calls
dwc2_hsotg_core_connect() even when no gadget driver has requested a
connection. After a host-to-device role change, this clears SFTDISCON
and lets the host enumerate an unconfigured gadget.
Check enabled in the connection helper. So the device will keep
disconnect until the controller is ready
Fixes: 510ffaa48eac ("usb: dwc2: Initialize the USB core for peripheral mode")
Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
---
drivers/usb/dwc2/gadget.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 6c8a9f05fff0..5c8e85493289 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -3664,7 +3664,8 @@ void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
{
/* remove the soft-disconnect and let's go */
- if (!hsotg->role_sw || (dwc2_readl(hsotg, GOTGCTL) & GOTGCTL_BSESVLD))
+ if (hsotg->enabled &&
+ (!hsotg->role_sw || (dwc2_readl(hsotg, GOTGCTL) & GOTGCTL_BSESVLD)))
dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON);
}
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/4] usb: dwc2: drd: Sync the generic PHY mode with role requests
2026-11-10 7:06 [PATCH 0/4] usb: dwc2: Add USB DRD support on CV1800 Series SoC Inochi Amaoto
2026-11-10 7:06 ` [PATCH 1/4] usb: dwc2: gadget: honor pullup requests in core_connect Inochi Amaoto
@ 2026-11-10 7:06 ` Inochi Amaoto
2026-11-10 7:06 ` [PATCH 3/4] usb: dwc2: Add USB DRD support on CV1800 series SoC Inochi Amaoto
2026-11-10 7:06 ` [PATCH 4/4] riscv: dts: sophgo: Enable USB role switch on Huashan PI Inochi Amaoto
3 siblings, 0 replies; 5+ messages in thread
From: Inochi Amaoto @ 2026-11-10 7:06 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen Wang,
Inochi Amaoto, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Minas Harutyunyan, Greg Kroah-Hartman,
Longbin Li, Dinh Nguyen, Paul Zimmerman, Felipe Balbi
Cc: devicetree, sophgo, linux-riscv, linux-kernel, linux-usb, Yixun Lan
The USB DRD does not reply on a OTG phy, which means the controller
should not reply on the switch behavior from PHY. Instead, it should
sync the phy state to avoid a brolen role.
Set the generic PHY mode after resolving the default role and before
updating the controller. And disable the controller clock if there
is a failure and it is disabled.
Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
---
drivers/usb/dwc2/core.h | 2 +-
drivers/usb/dwc2/drd.c | 48 ++++++++++++++++++++++++++++++-------
drivers/usb/dwc2/platform.c | 4 +++-
3 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 015a5fb7842d..298b418e6859 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1400,7 +1400,7 @@ static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
int dwc2_drd_init(struct dwc2_hsotg *hsotg);
void dwc2_drd_suspend(struct dwc2_hsotg *hsotg);
-void dwc2_drd_resume(struct dwc2_hsotg *hsotg);
+int dwc2_drd_resume(struct dwc2_hsotg *hsotg);
void dwc2_drd_exit(struct dwc2_hsotg *hsotg);
/*
diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c
index 1ad8fa3f862a..1ad3804cbc80 100644
--- a/drivers/usb/dwc2/drd.c
+++ b/drivers/usb/dwc2/drd.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/iopoll.h>
+#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/usb/role.h>
#include "core.h"
@@ -88,6 +89,7 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
unsigned long flags;
int already = 0;
+ int ret = 0;
/* Skip session not in line with dr_mode */
if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
@@ -111,14 +113,11 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
* mode and sessions. It is the case if cable is plugged at boot.
*/
if (!hsotg->ll_hw_enabled && hsotg->clk) {
- int ret = clk_prepare_enable(hsotg->clk);
-
+ ret = clk_prepare_enable(hsotg->clk);
if (ret)
return ret;
}
- spin_lock_irqsave(&hsotg->lock, flags);
-
if (role == USB_ROLE_NONE) {
/* default operation mode when usb role is USB_ROLE_NONE */
if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
@@ -127,6 +126,15 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
role = USB_ROLE_DEVICE;
}
+ if (role == USB_ROLE_HOST || role == USB_ROLE_DEVICE) {
+ ret = phy_set_mode(hsotg->phy, role == USB_ROLE_HOST ?
+ PHY_MODE_USB_HOST : PHY_MODE_USB_DEVICE);
+ if (ret)
+ goto out_clk;
+ }
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+
if ((IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) ||
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)) &&
dwc2_is_device_mode(hsotg) &&
@@ -160,26 +168,40 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
/* This will raise a Connector ID Status Change Interrupt */
dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
- if (!hsotg->ll_hw_enabled && hsotg->clk)
- clk_disable_unprepare(hsotg->clk);
-
dev_dbg(hsotg->dev, "%s-session valid\n",
role == USB_ROLE_NONE ? "No" :
role == USB_ROLE_HOST ? "A" : "B");
- return 0;
+out_clk:
+ if (!hsotg->ll_hw_enabled && hsotg->clk)
+ clk_disable_unprepare(hsotg->clk);
+
+ return ret;
}
int dwc2_drd_init(struct dwc2_hsotg *hsotg)
{
struct usb_role_switch_desc role_sw_desc = {0};
struct usb_role_switch *role_sw;
+ bool is_host;
int ret;
if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
return 0;
hsotg->role_sw_default_mode = usb_get_role_switch_default_mode(hsotg->dev);
+
+ if (hsotg->role_sw_default_mode != USB_DR_MODE_UNKNOWN)
+ is_host = hsotg->dr_mode == USB_DR_MODE_HOST;
+ else
+ is_host = hsotg->role_sw_default_mode == USB_DR_MODE_HOST;
+
+ ret = phy_set_mode(hsotg->phy,
+ is_host ? PHY_MODE_USB_HOST : PHY_MODE_USB_DEVICE);
+ if (ret)
+ return dev_err_probe(hsotg->dev, ret,
+ "failed to set default PHY mode\n");
+
role_sw_desc.driver_data = hsotg;
role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
role_sw_desc.set = dwc2_drd_role_sw_set;
@@ -214,10 +236,11 @@ void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
}
}
-void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
+int dwc2_drd_resume(struct dwc2_hsotg *hsotg)
{
u32 gintsts, gintmsk;
enum usb_role role;
+ int ret;
if (hsotg->role_sw) {
/* get last known role (as the get ops isn't implemented by this driver) */
@@ -230,6 +253,11 @@ void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
role = USB_ROLE_DEVICE;
}
+ ret = phy_set_mode(hsotg->phy, role == USB_ROLE_HOST ?
+ PHY_MODE_USB_HOST : PHY_MODE_USB_DEVICE);
+ if (ret)
+ return ret;
+
/* restore last role that may have been lost */
if (role == USB_ROLE_HOST)
dwc2_ovr_avalid(hsotg, true);
@@ -250,6 +278,8 @@ void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
gintmsk |= GINTSTS_CONIDSTSCHNG;
dwc2_writel(hsotg, gintmsk, GINTMSK);
}
+
+ return 0;
}
void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index ef0d73077034..ab39d6b36b19 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -783,7 +783,9 @@ static int __maybe_unused dwc2_resume(struct device *dev)
/* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
dwc2_force_dr_mode(dwc2);
} else {
- dwc2_drd_resume(dwc2);
+ ret = dwc2_drd_resume(dwc2);
+ if (ret)
+ return ret;
}
if (dwc2_is_device_mode(dwc2))
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 3/4] usb: dwc2: Add USB DRD support on CV1800 series SoC
2026-11-10 7:06 [PATCH 0/4] usb: dwc2: Add USB DRD support on CV1800 Series SoC Inochi Amaoto
2026-11-10 7:06 ` [PATCH 1/4] usb: dwc2: gadget: honor pullup requests in core_connect Inochi Amaoto
2026-11-10 7:06 ` [PATCH 2/4] usb: dwc2: drd: Sync the generic PHY mode with role requests Inochi Amaoto
@ 2026-11-10 7:06 ` Inochi Amaoto
2026-11-10 7:06 ` [PATCH 4/4] riscv: dts: sophgo: Enable USB role switch on Huashan PI Inochi Amaoto
3 siblings, 0 replies; 5+ messages in thread
From: Inochi Amaoto @ 2026-11-10 7:06 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen Wang,
Inochi Amaoto, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Minas Harutyunyan, Greg Kroah-Hartman,
Longbin Li, Dinh Nguyen, Paul Zimmerman, Felipe Balbi
Cc: devicetree, sophgo, linux-riscv, linux-kernel, linux-usb, Yixun Lan
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@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
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 4/4] riscv: dts: sophgo: Enable USB role switch on Huashan PI
2026-11-10 7:06 [PATCH 0/4] usb: dwc2: Add USB DRD support on CV1800 Series SoC Inochi Amaoto
` (2 preceding siblings ...)
2026-11-10 7:06 ` [PATCH 3/4] usb: dwc2: Add USB DRD support on CV1800 series SoC Inochi Amaoto
@ 2026-11-10 7:06 ` Inochi Amaoto
3 siblings, 0 replies; 5+ messages in thread
From: Inochi Amaoto @ 2026-11-10 7:06 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen Wang,
Inochi Amaoto, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Minas Harutyunyan, Greg Kroah-Hartman,
Longbin Li, Dinh Nguyen, Paul Zimmerman, Felipe Balbi
Cc: devicetree, sophgo, linux-riscv, linux-kernel, linux-usb, Yixun Lan
As current the DWC2 support role switch on CV18XX SoCs. Enable it
on Huashan PI.
Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
---
arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts b/arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts
index aedf79f47407..bf3d58c355c3 100644
--- a/arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts
+++ b/arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dts
@@ -88,6 +88,8 @@ &uart0 {
};
&usb {
- dr_mode = "host";
+ dr_mode = "otg";
+ usb-role-switch;
+ role-switch-default-mode = "host";
status = "okay";
};
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread