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;
         }
     }

Reply via email to