Re: i.MX kernel hangup caused by chipidea USB gadget driver

Xu Yang
2025-06-09T11:58:04Z

Hi Shawn,

Thanks for your reports!

On Mon, Jun 09, 2025 at 01:31:06PM +0800, Shawn Guo wrote:
> Hi Xu, Peter,
>
> I'm seeing a kernel hangup on imx8mm-evk board. It happens when:
>
> - USB gadget is enabled as Ethernet
> - There is data transfer over USB Ethernet
> - Device is going in/out suspend
>
> A simple way to reproduce the issue could be:
>
> 1. Copy a big file (like 500MB) from host PC to device with scp
>
> 2. While the file copy is ongoing, suspend & resume the device like:
>
> $ echo +3 > /sys/class/rtc/rtc0/wakealarm; echo mem > /sys/power/state
>
> 3. The device will hang up there
>
> I reproduced on the following kernels:
>
> - Mainline kernel
> - NXP kernel lf-6.6.y
> - NXP kernel lf-6.12.y
>
> But NXP kernel lf-6.1.y doesn't have this problem. I tracked it down to
> Peter's commit [1] on lf-6.1.y, and found that the gadget disconnect &
> connect calls got lost from suspend & resume hooks, when the commit were
> split and pushed upstream. I confirm that adding the calls back fixes
> the hangup.
>
> ---8<--------------------
>
> diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
> index 8a9b31fd5c89..72329a7eac4d 100644
> --- a/drivers/usb/chipidea/udc.c
> +++ b/drivers/usb/chipidea/udc.c
> @@ -2374,6 +2374,9 @@ static void udc_suspend(struct ci_hdrc *ci)
> */
> if (hw_read(ci, OP_ENDPTLISTADDR, ~0) == 0)
> hw_write(ci, OP_ENDPTLISTADDR, ~0, ~0);
> +
> + if (ci->driver && ci->vbus_active && (ci->gadget.state != USB_STATE_SUSPENDED))
> + usb_gadget_disconnect(&ci->gadget);
> }
>
> static void udc_resume(struct ci_hdrc *ci, bool power_lost)
> @@ -2384,6 +2387,9 @@ static void udc_resume(struct ci_hdrc *ci, bool power_lost)
> OTGSC_BSVIS | OTGSC_BSVIE);
> if (ci->vbus_active)
> usb_gadget_vbus_disconnect(&ci->gadget);
> + } else {
> + if (ci->driver && ci->vbus_active)
> + usb_gadget_connect(&ci->gadget);
> }
>
> /* Restore value 0 if it was set for power lost check */
>
> ---->8------------------

During the scp process, the usb host won't put usb device to suspend state.
In current design, then the ether driver doesn't know the system has
suspended after echo mem. The root cause is that ether driver is still tring
to queue usb request after usb controller has suspended where usb clock is off,
then the system hang.

With the above changes, I think the ether driver will fail to eth_start_xmit()
at an ealier stage, so the issue can't be triggered.

I think the ether driver needs call gether_suspend() accordingly, to do this,
the controller driver need explicitly call suspend() function when it's going
to be suspended. Could you check whether below patch fix the issue?

---8<--------------------

diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 8a9b31fd5c89..27a7674ed62c 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -2367,6 +2367,8 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
#ifdef CONFIG_PM_SLEEP
static void udc_suspend(struct ci_hdrc *ci)
{
+ ci->driver->suspend(&ci->gadget);
+
/*
* Set OP_ENDPTLISTADDR to be non-zero for
* checking if controller resume from power lost
@@ -2389,6 +2391,8 @@ static void udc_resume(struct ci_hdrc *ci, bool power_lost)
/* Restore value 0 if it was set for power lost check */
if (hw_read(ci, OP_ENDPTLISTADDR, ~0) == 0xFFFFFFFF)
hw_write(ci, OP_ENDPTLISTADDR, ~0, 0);
+
+ ci->driver->resume(&ci->gadget);
}
#endif

---->8------------------

Thanks,
Xu Yang

>
> But it's unclear to me why the hangup happens and how the change above
> fix the problem. Do you guys have any insight here?o
>
> Shawn
>
> [1] https://github.com/reMarkable/linux-imx/commit/0791d25578cb0e46fd93ae7a3c36ff7a424f3547
>