This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 440d5f7f87d23fbe2261a2f822f55b69f96e9059 Author: Justin Hammond <[email protected]> AuthorDate: Sun Aug 16 16:03:56 2026 +0800 drivers/usbhost: Mask the write-one-to-clear PORTSC bits on reset. Eight PORTSC bits are write-one-to-clear, so writing back a value just read clears PED and every change bit that was set, disabling the port being reset. Mask them out using the new XHCI_PORTSC_RW1C. The wait after reset also decided on its own counter rather than on the port, reporting a timeout for a port that enabled on the last iteration. Test PED, and report PORTSC when it does time out. Assisted-by: Claude:claude-opus-5 Signed-off-by: Justin Hammond <[email protected]> --- drivers/usbhost/usbhost_xhci.h | 9 +++++++++ drivers/usbhost/usbhost_xhci_pci.c | 32 ++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/drivers/usbhost/usbhost_xhci.h b/drivers/usbhost/usbhost_xhci.h index 3888eabacf6..3ece9affe41 100644 --- a/drivers/usbhost/usbhost_xhci.h +++ b/drivers/usbhost/usbhost_xhci.h @@ -268,6 +268,15 @@ #define XHCI_PORTSC_DR (1 << 30) /* Bit 30: Device Removable */ #define XHCI_PORTSC_WPR (1 << 31) /* Bit 31: Warm Port Reset */ +/* The write-one-to-clear bits of PORTSC. Mask these out of any + * read-modify-write of the register, unless clearing them is intended. + */ + +#define XHCI_PORTSC_RW1C (XHCI_PORTSC_PED | XHCI_PORTSC_CSC | \ + XHCI_PORTSC_PEC | XHCI_PORTSC_WRC | \ + XHCI_PORTSC_OCC | XHCI_PORTSC_PRC | \ + XHCI_PORTSC_PLC | XHCI_PORTSC_CEC) + /* Port Power Management Status and Control (USB3) */ #define XHCI_PORTPMSC_U1TO_SHIFT (0) /* Bits 0-7: U1 Timeout */ diff --git a/drivers/usbhost/usbhost_xhci_pci.c b/drivers/usbhost/usbhost_xhci_pci.c index 8f8348a6ad1..3a44dae1d01 100644 --- a/drivers/usbhost/usbhost_xhci_pci.c +++ b/drivers/usbhost/usbhost_xhci_pci.c @@ -74,6 +74,10 @@ */ #define XHCI_HALT_TIMEOUT_MS (100) + +/* Milliseconds allowed for a port to enable after reset. */ + +#define XHCI_PORT_RESET_MS (500) #define XHCI_BUFSIZE (512) /* Port numbers macros */ @@ -1360,9 +1364,12 @@ static int xhci_port_enable(FAR struct usbhost_xhci_s *priv, if (!(regval & XHCI_PORTSC_PED)) { - /* Reset the port */ + /* Reset the port, masking the write-one-to-clear bits out of the + * value first. See XHCI_PORTSC_RW1C. + */ - regval = xhci_oper_getreg(priv, XHCI_PORTSC(rhpndx)); + regval = xhci_oper_getreg(priv, XHCI_PORTSC(rhpndx)); + regval &= ~XHCI_PORTSC_RW1C; regval |= XHCI_PORTSC_PR; xhci_oper_putreg(priv, XHCI_PORTSC(rhpndx), regval); @@ -1370,16 +1377,25 @@ static int xhci_port_enable(FAR struct usbhost_xhci_s *priv, /* Wait for Enabled state for port */ - retries = 10; - while (!(xhci_oper_getreg(priv, XHCI_PORTSC(rhpndx)) - & XHCI_PORTSC_PED) && retries > 0) + for (retries = XHCI_PORT_RESET_MS; retries > 0; retries--) { - retries--; - up_mdelay(100); + regval = xhci_oper_getreg(priv, XHCI_PORTSC(rhpndx)); + if ((regval & XHCI_PORTSC_PED) != 0) + { + break; + } + + up_mdelay(1); } - if (retries == 0) + /* Test the port, not the counter: a port that comes up on the last + * attempt leaves the loop with the count exhausted too. + */ + + if ((regval & XHCI_PORTSC_PED) == 0) { + pcierr("port %d will not enable, PORTSC %08" PRIx32 "\n", rhpndx, + regval); return -ETIMEDOUT; } }
