Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

stm32h7: sdmmc: ensure minimum time between CLKCR/CMDR writes

According to RM0433 consecutive writes to the CLKCR and CMDR
registers must be separated by at least 7 AHB clock cycles.

The initialization code didn't respect this and it seemed to
be causing a nasty bug, where the SDMMC bus clock got stuck
at 400 KHz in hardware. Despite the CLKCR register reading
back the correct value, it could not be written with a new
value even in the debugger; resetting the peripheral was the
only way out of this state.

Adding some dummy register reads after any access to CLKCR
should insert the necessary number of wait states. Without
the fix, the SDMMC clock gets stuck about 12% of the time.
With this fix, the clock always initializes correctly.

Change-Id: Iba85b8e1e3c60992ddc42fb4c1e66c37941ed617

authored by

Aidan MacDonald and committed by
Solomon Peachy
65bf8d41 7fdba685

+28
+28
firmware/target/arm/stm32/sdmmc-stm32h7.c
··· 74 74 return a > b ? a - b : b - a; 75 75 } 76 76 77 + /* 78 + * The CLKCR and CMDR registers must not be written twice 79 + * within 7 AHB clock cycles. If this is not respected the 80 + * hardware seems to misbehave, for example updating the 81 + * clock divider in CLKCR can fail (the register field is 82 + * updated, but the actual clock output is not). 83 + * 84 + * Reading a peripheral register should require at least 85 + * two cycles on the AHB bus (1 address + 1 data cycle), 86 + * assuming they are single transfers and no pipelining 87 + * can take place. Doing four reads of an SDMMC register 88 + * should therefore be enough to add the correct number 89 + * of wait states. 90 + */ 91 + static void stm32h7_sdmmc_regwrite_delay(struct stm32h7_sdmmc_controller *ctl) 92 + { 93 + reg_readl(ctl->regs, SDMMC_STAR); 94 + reg_readl(ctl->regs, SDMMC_STAR); 95 + reg_readl(ctl->regs, SDMMC_STAR); 96 + reg_readl(ctl->regs, SDMMC_STAR); 97 + } 98 + 77 99 static bool stm32h7_sdmmc_is_powered_off(struct stm32h7_sdmmc_controller *ctl) 78 100 { 79 101 uint32_t pwrctrl = reg_readlf(ctl->regs, SDMMC_POWER, PWRCTRL); ··· 131 153 132 154 /* Automatically stop clock when bus is not in use */ 133 155 reg_writelf(ctl->regs, SDMMC_CLKCR, PWRSAV(1), HWFC_EN(1)); 156 + stm32h7_sdmmc_regwrite_delay(ctl); 134 157 } 135 158 else 136 159 { ··· 173 196 reg_writelf(ctl->regs, SDMMC_CLKCR, WIDBUS_V(8BIT)); 174 197 else 175 198 panicf("%s", __func__); 199 + 200 + stm32h7_sdmmc_regwrite_delay(ctl); 176 201 } 177 202 178 203 void stm32h7_sdmmc_set_bus_clock(void *controller, uint32_t clock) ··· 222 247 DDR(0), 223 248 NEGEDGE(0), 224 249 CLKDIV(div[idx] / 2)); 250 + 251 + stm32h7_sdmmc_regwrite_delay(ctl); 225 252 } 226 253 227 254 int stm32h7_sdmmc_submit_command(void *controller, ··· 360 387 reg_varl(ctl->regs, SDMMC_MASKR) = maskr; 361 388 reg_varl(ctl->regs, SDMMC_ARGR) = cmd->argument; 362 389 reg_varl(ctl->regs, SDMMC_CMDR) = cmdr; 390 + stm32h7_sdmmc_regwrite_delay(ctl); 363 391 364 392 /* Wait for command completion */ 365 393 semaphore_wait(&ctl->sem, TIMEOUT_BLOCK);