diff options
| author | hathach <[email protected]> | 2026-07-01 16:36:04 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2026-07-01 17:49:14 +0700 |
| commit | 4202e1e2d0949b1de16ebb0de289ac0ba577579b (patch) | |
| tree | 08531c8632f4239c33450235498ca652b44205a3 | |
| parent | f96757a19af640b14c272945b2272540c4af1cc4 (diff) | |
hcd(max3421): improve CHIPRES reset logic and refine oscillator stabilization timing
| -rw-r--r-- | src/portable/analog/max3421/hcd_max3421.c | 31 |
1 files changed, 18 insertions, 13 deletions
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c index 621d0d86c..971b42c5f 100644 --- a/src/portable/analog/max3421/hcd_max3421.c +++ b/src/portable/analog/max3421/hcd_max3421.c @@ -82,6 +82,11 @@ enum { }; enum { + TIME_TO_EXIT_SUSPEND_MS = 4u, // datasheet: PWRDOWN = 1 to 0 to OSCOKIRQ = 3 ms + 1 ms margin + TIME_CHIPRES_DELAY_MS = 2u // CHIPRES hold: 2 guarantees >= 1 ms actual despite ms-tick quantization +}; + +enum { CPUCTL_IE = 1u << 0, CPUCTL_PULSEWID0 = 1u << 6, CPUCTL_PULSEWID1 = 1u << 7, @@ -490,8 +495,6 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { _hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef); #endif - // NOTE: driver does not seem to work without nRST pin signal - // full duplex, interrupt negative edge reg_write(rhport, PINCTL_ADDR, _tuh_cfg.pinctl | PINCTL_FDUPSPI, false); @@ -501,19 +504,21 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { TU_LOG2_HEX(revision); TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false); - // reset + // Software reset via CHIPRES. Per the Programming Guide, CHIPRES stops the internal oscillator; + // clearing it restarts the oscillator, and OSCOKIRQ latches on the resulting OSCOK 0->1 edge. + // Back-to-back writes hold reset for only ~us, too short for the high-Q crystal to actually stop, + // so no edge is generated and OSCOK never re-latches - this is the startup hang. Hold reset >= 1 ms + // (datasheet "PWRDOWN = 1 to oscillator stop = 5 us") so the oscillator fully stops and the edge fires. reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false); + const uint32_t reset_start_ms = tusb_time_millis_api(); + while (tusb_time_millis_api() - reset_start_ms < TIME_CHIPRES_DELAY_MS) {} // hold reset so the oscillator stops reg_write(rhport, USBCTL_ADDR, 0, false); - // Bound the oscillator-OK wait. On boards where the MAX3421E is only - // soft-reset (CHIPRES) rather than hardware-reset via nRST, OSCOK may never - // latch, which would otherwise hang the host forever (adafruit/circuitpython#10053). - // The oscillator is generally running regardless, so proceed after a timeout. - uint32_t oscok_retries = 200000; - while( !(reg_read(rhport, USBIRQ_ADDR, false) & USBIRQ_OSCOK_IRQ) ) { - if (--oscok_retries == 0) { - break; - } - } + + // Wait for OSCOK (12 MHz oscillator + 48 MHz PLL relock). Bounded as a safety net, so the host never + // hangs if the edge is still missed on some board - the clock is running regardless. + const uint32_t oscok_start_ms = tusb_time_millis_api(); + while (!(reg_read(rhport, USBIRQ_ADDR, false) & USBIRQ_OSCOK_IRQ) && + (tusb_time_millis_api() - oscok_start_ms < TIME_TO_EXIT_SUSPEND_MS)) {} // Mode: Host and DP/DM pull down mode_write(rhport, MODE_DPPULLDN | MODE_DMPULLDN | MODE_HOST, false); |
