drivers/usbhost: Attach the xHCI interrupt after the controller starts.

The handler defers to a worker that walks the event ring, and the ring is
not allocated until the controller is started, several steps later.  A
controller left running by a boot loader has an interrupt pending as soon
as the line is enabled, so attaching earlier is a race with nothing able
to answer it.

Attach after the start, and clear USBSTS and the interrupter pending flag
once the handler is in place: a message signalled interrupt is sent on the
flag's clear to set transition, so a flag raised before the handler
existed would never produce another.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
This commit is contained in:
Justin Hammond 2026-08-16 17:09:58 +08:00 • committed by Xiang Xiao
parent 4b86c1dd23
commit 648aa30e37

View file

@ -4545,14 +4545,6 @@ static int xhci_hw_initialize(FAR struct usbhost_xhci_s *priv)
goto errout;
}
/* Configure interrupts */
ret = xhci_irq_initialize(priv);
if (ret < 0)
{
goto errout;
}
/* Halt controller */
ret = xhci_ctrl_halt(priv);
@ -4697,6 +4689,7 @@ xhci_initialize(FAR const char *name, uintptr_t base,
{
FAR struct usbhost_conn_xhci_s *conn = NULL;
FAR struct usbhost_xhci_s *priv = NULL;
uint32_t regval;
int ret;
DEBUGASSERT(name != NULL && base != 0 && ops != NULL &&
@ -4756,6 +4749,34 @@ xhci_initialize(FAR const char *name, uintptr_t base,
goto errout;
}
/* Take the interrupt only now.
*
* The handler defers to a worker that walks the event ring, and the ring
* does not exist until the controller has been started. A controller
* left running by a boot loader can have an interrupt pending the moment
* the line is enabled, so attaching any earlier is a race with nothing
* to answer it.
*/
ret = xhci_irq_initialize(priv);
if (ret < 0)
{
uerr("failed to attach interrupt: %d\n", ret);
goto errout;
}
/* Acknowledge anything the controller raised before the handler was
* attached. A message is sent once, on the transition, so a bit set in
* that window would never produce another. Clear them, so the next
* event is a fresh assertion.
*/
regval = xhci_oper_getreg(priv, XHCI_USBSTS);
xhci_oper_putreg(priv, XHCI_USBSTS, regval);
regval = xhci_runt_getreg(priv, XHCI_IMAN(0));
xhci_runt_putreg(priv, XHCI_IMAN(0), regval | XHCI_IMAN_IP);
#ifdef CONFIG_DEBUG_USB_INFO
xhci_dump_mem(priv, "after init");
#endif