summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHa Thach <[email protected]>2026-08-19 18:33:12 +0700
committerGitHub <[email protected]>2026-08-19 18:33:12 +0700
commit7800876bf151a239046521232dc4603b156061be (patch)
tree31fd2349adcae0e7f8bdf4f10aae20765a3483f5
parent34e8d99ade2c18c4ab629f307544962e0af5347c (diff)
parent75a01f561438c16677b48f3a59fda80a2b096ad8 (diff)
Merge pull request #3833 from hathach/claude/ci-hs-set-address-order
dcd(ci_hs): stage the device address before priming the status stage
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_type.h8
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c17
2 files changed, 19 insertions, 6 deletions
diff --git a/src/portable/chipidea/ci_hs/ci_hs_type.h b/src/portable/chipidea/ci_hs/ci_hs_type.h
index 5baa14821..b3ef3b6af 100644
--- a/src/portable/chipidea/ci_hs/ci_hs_type.h
+++ b/src/portable/chipidea/ci_hs/ci_hs_type.h
@@ -29,6 +29,14 @@ enum {
USBCMD_INTR_THRESHOLD_MASK = 0x00FF0000u, // Interrupt Threshold bit 23:16
};
+// DEVICEADDR
+#define DEVICEADDR_USBADR_POS 25
+
+enum {
+ DEVICEADDR_USBADRA = TU_BIT(24), ///< Device Address Advance: stage USBADR until the next EP0 IN is ACKed
+ DEVICEADDR_USBADR_MASK = 0xFE000000u, ///< Device Address bit 31:25
+};
+
// PORTSC1
#define PORTSC1_PORT_SPEED_POS 26
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index 6ab28e0be..f1c333280 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -361,12 +361,17 @@ void dcd_int_disable(uint8_t rhport) {
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
- // Response with status first before changing device address. A refused prime means a new
- // setup superseded this transfer; staging an address whose ACK will never arrive would
- // leave the device answering on it, so only arm the address when the status went out.
- if (dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0, false)) {
- ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
- dcd_reg->DEVICEADDR = (dev_addr << 25) | TU_BIT(24);
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
+ const uint32_t prev = dcd_reg->DEVICEADDR & DEVICEADDR_USBADR_MASK;
+
+ // IMXRT1060RM 42.7.23 / UM10503 Table 478: stage the address before priming the status stage so
+ // hardware loads USBADR at the status ACK. Priming first races that ACK against this write.
+ dcd_reg->DEVICEADDR = ((uint32_t)dev_addr << DEVICEADDR_USBADR_POS) | DEVICEADDR_USBADRA;
+
+ if (!dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0, false)) {
+ // USB 2.0 9.4.6: the address changes only after the status stage completes successfully. The
+ // status never went out, so drop the stage - USBADRA=0 takes effect instantly.
+ dcd_reg->DEVICEADDR = prev;
}
}