From 51617b404e6e0d8ffa13f1e8ecc0bc07d6e2c7e6 Mon Sep 17 00:00:00 2001 From: Kaj Schittecat Date: Sat, 1 Aug 2026 11:36:24 +0200 Subject: [PATCH] touch: SD mount ladders renegotiate the clock upward after a slow-rung init (#194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A card whose power-up handshake only completed at 400 kHz kept 400 kHz as its SESSION clock — SD.begin's frequency is the operating clock, and neither the boot ladder (main.cpp) nor the runtime ladder (fmSdTryMount) ever raised it after a slow success. With the SD as the T-Deck's primary store that meant loading contacts + history at ~25 KB/s: the reported ~3-minute boot, f_getfree timeouts making an inserted card show as 0 KB/empty in the file manager, and backups wedging. Launcher installs hit it most (the Launcher's own SD use leaves the card needing the slow wake path more often). Standard SD bring-up is init-slow-then-raise: after any sub-4MHz success both ladders now re-begin at 4 MHz, falling back to the clock that just worked if the fast re-begin fails. A healthy card is unaffected (mounts at 4 MHz on the first rung). T-Deck + V4-R8 + M9 (the shared-SPI SD boards) build. Likely also behind the long-standing Launcher-mode weirdness cluster together with the torn-prefs fix (#195/#196): slow-clock sessions made every write window enormous. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/main.cpp | 25 ++++++++++++++++++++++++- src/ui-touch/UITask.cpp | 15 +++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 97760fc4..d844b5e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -519,11 +519,34 @@ void setup() { { 300, 1000000 }, { 450, 1000000 }, { 650, 400000 }, { 900, 400000 }, }; int tries = want_full_sd ? 7 : 3; + uint32_t mounted_hz = 0; for (int a = 0; a < tries && !sd_mounted; ++a) { SD.end(); delay(kBootMount[a].settle_ms); - if (SD.begin(PIN_SD_CS, *_spi, kBootMount[a].hz, "/sd", 6) && SD.cardType() != CARD_NONE) + if (SD.begin(PIN_SD_CS, *_spi, kBootMount[a].hz, "/sd", 6) && SD.cardType() != CARD_NONE) { sd_mounted = true; + mounted_hz = kBootMount[a].hz; + } + } + // RENEGOTIATE UPWARD after a slow-rung success (GH #194). Standard SD bring-up is + // "initialise at a conservative clock, then raise it" — but SD.begin's clock is the + // operating clock for the whole session, so a card that only WOKE at 400 kHz then ran + // its entire life at 400 kHz (~25 KB/s). With the card as the primary store that was a + // ~3-minute boot (contacts + history at modem speed), runtime f_getfree timeouts (the + // file manager showing an inserted card as 0 KB/empty), and wedged backups. An + // initialised card almost always sustains 4 MHz even when its power-up handshake needed + // 400 kHz; if the fast re-begin fails, fall back to the clock that just worked. + if (sd_mounted && mounted_hz < 4000000) { + SD.end(); + delay(60); + if (SD.begin(PIN_SD_CS, *_spi, 4000000, "/sd", 6) && SD.cardType() != CARD_NONE) { + Serial.printf("[BOOT] SD renegotiated %lu -> 4000000 Hz\n", (unsigned long)mounted_hz); + } else { + SD.end(); + delay(120); + sd_mounted = SD.begin(PIN_SD_CS, *_spi, mounted_hz, "/sd", 6) && SD.cardType() != CARD_NONE; + if (sd_mounted) Serial.printf("[BOOT] SD stays at %lu Hz (4 MHz renegotiation failed)\n", (unsigned long)mounted_hz); + } } } if (sd_mounted) { diff --git a/src/ui-touch/UITask.cpp b/src/ui-touch/UITask.cpp index 2c44a75d..55fd08fb 100644 --- a/src/ui-touch/UITask.cpp +++ b/src/ui-touch/UITask.cpp @@ -18698,15 +18698,30 @@ static bool fmSdTryMount() { }; const int kAttempts = (int)(sizeof(kMountLadder) / sizeof(kMountLadder[0])); bool mounted = false; + uint32_t mounted_hz = 0; disableLoopWDT(); for (int attempt = 0; attempt < kAttempts; ++attempt) { SD.end(); delay(kMountLadder[attempt].settle_ms); if (SD.begin(PIN_SD_CS, *spi, kMountLadder[attempt].hz, "/sd", 6) && SD.cardType() != CARD_NONE) { mounted = true; + mounted_hz = kMountLadder[attempt].hz; break; } } + // Renegotiate upward after a slow-rung success (GH #194): the succeeding rung's clock used + // to become the SESSION clock, so a card whose wake-up needed 400 kHz served contacts, + // history and tiles at ~25 KB/s forever after ("slower is fine" above predates the SD being + // the primary store). Init slow, then raise — fall back to the proven clock if 4 MHz fails. + if (mounted && mounted_hz < 4000000) { + SD.end(); + delay(60); + if (!(SD.begin(PIN_SD_CS, *spi, 4000000, "/sd", 6) && SD.cardType() != CARD_NONE)) { + SD.end(); + delay(120); + mounted = SD.begin(PIN_SD_CS, *spi, mounted_hz, "/sd", 6) && SD.cardType() != CARD_NONE; + } + } enableLoopWDT(); if (mounted) { s_sd_mounted = true;