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: implement sdmmc_host-based SDMMC controller driver

Change-Id: I26c47c630ea364de043a224b549d7867fb1e5794

authored by

Aidan MacDonald and committed by
Solomon Peachy
141b4a22 38db211a

+579 -85
+1 -1
firmware/SOURCES
··· 2024 2024 target/arm/stm32/gpio-stm32h7.c 2025 2025 target/arm/stm32/i2c-stm32h7.c 2026 2026 target/arm/stm32/pcm-stm32h7.c 2027 - target/arm/stm32/sd-stm32h7.c 2027 + target/arm/stm32/sdmmc-stm32h7.c 2028 2028 target/arm/stm32/spi-stm32h7.c 2029 2029 target/arm/stm32/system-stm32h7.c 2030 2030 target/arm/stm32/timer-stm32h7.c
+2
firmware/target/arm/stm32/clock-stm32h7.h
··· 34 34 STM_CLOCK_SPI5_KER, 35 35 STM_CLOCK_SPI6_KER, 36 36 STM_CLOCK_LTDC_KER, 37 + STM_CLOCK_SDMMC1_KER, 38 + STM_CLOCK_SDMMC2_KER, 37 39 STM_NUM_CLOCKS, 38 40 }; 39 41
+2 -1
firmware/target/arm/stm32/irqhandlers-stm32h743.c
··· 28 28 void lcdc_irq_handler(void) ATTR_IRQ_HANDLER; 29 29 void otg_hs_irq_handler(void) ATTR_IRQ_HANDLER; 30 30 void sai_irq_handler(void) ATTR_IRQ_HANDLER; 31 - void sdmmc_irq_handler(void) ATTR_IRQ_HANDLER; 31 + void sdmmc1_irq_handler(void) ATTR_IRQ_HANDLER; 32 + void sdmmc2_irq_handler(void) ATTR_IRQ_HANDLER; 32 33 void spi1_irq_handler(void) ATTR_IRQ_HANDLER; 33 34 void spi2_irq_handler(void) ATTR_IRQ_HANDLER; 34 35 void spi3_irq_handler(void) ATTR_IRQ_HANDLER;
-81
firmware/target/arm/stm32/sd-stm32h7.c
··· 1 - /*************************************************************************** 2 - * __________ __ ___. 3 - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 - * \/ \/ \/ \/ \/ 8 - * $Id$ 9 - * 10 - * Copyright (C) 2025 by Aidan MacDonald 11 - * 12 - * This program is free software; you can redistribute it and/or 13 - * modify it under the terms of the GNU General Public License 14 - * as published by the Free Software Foundation; either version 2 15 - * of the License, or (at your option) any later version. 16 - * 17 - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 - * KIND, either express or implied. 19 - * 20 - ****************************************************************************/ 21 - #include "storage.h" 22 - #include "sdmmc.h" 23 - #include "sd.h" 24 - 25 - int sd_init(void) 26 - { 27 - return 0; 28 - } 29 - 30 - void sd_enable(bool on) 31 - { 32 - (void)on; 33 - } 34 - 35 - int sd_event(long id, intptr_t data) 36 - { 37 - return storage_event_default_handler(id, data, 0, STORAGE_SD); 38 - } 39 - 40 - long sd_last_disk_activity(void) 41 - { 42 - return 0; 43 - } 44 - 45 - bool sd_present(IF_MD_NONVOID(int drive)) 46 - { 47 - IF_MD((void)drive); 48 - return true; 49 - } 50 - 51 - bool sd_removable(IF_MD_NONVOID(int card_no)) 52 - { 53 - IF_MD((void)card_no); 54 - return true; 55 - } 56 - 57 - tCardInfo *card_get_info_target(int card_no) 58 - { 59 - (void)card_no; 60 - 61 - static tCardInfo null_info = {0}; 62 - return &null_info; 63 - } 64 - 65 - int sd_read_sectors(IF_MD(int drive,) sector_t start, int count, void *buf) 66 - { 67 - IF_MD((void)drive); 68 - (void)start; 69 - (void)count; 70 - (void)buf; 71 - return -1; 72 - } 73 - 74 - int sd_write_sectors(IF_MD(int drive,) sector_t start, int count, const void *buf) 75 - { 76 - IF_MD((void)drive); 77 - (void)start; 78 - (void)count; 79 - (void)buf; 80 - return -1; 81 - }
+497
firmware/target/arm/stm32/sdmmc-stm32h7.c
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2025 by Aidan MacDonald 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License 14 + * as published by the Free Software Foundation; either version 2 15 + * of the License, or (at your option) any later version. 16 + * 17 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 + * KIND, either express or implied. 19 + * 20 + ****************************************************************************/ 21 + #include "sdmmc-stm32h7.h" 22 + #include "kernel.h" 23 + #include "panic.h" 24 + #include "regs/stm32h743/rcc.h" 25 + #include "regs/stm32h743/sdmmc.h" 26 + #include <string.h> 27 + 28 + /* cmd_wait flags */ 29 + #define WAIT_CMD 0x01 30 + #define WAIT_DATA 0x02 31 + 32 + /* Maximum number of bytes for 1 transfer */ 33 + #define MAX_DATA_LEN \ 34 + (BM_SDMMC_DLENR_DATALENGTH >> BP_SDMMC_DLENR_DATALENGTH) 35 + 36 + /* IRQs for command phase */ 37 + #define CMD_SUCCESS_BITS \ 38 + __reg_orm(SDMMC_STAR, CMDSENT, CMDREND) 39 + #define CMD_ERROR_BITS \ 40 + __reg_orm(SDMMC_STAR, CTIMEOUT, CCRCFAIL) 41 + #define CMD_END_BITS \ 42 + (CMD_SUCCESS_BITS | CMD_ERROR_BITS) 43 + 44 + /* IRQs for data phase */ 45 + #define DATA_SUCCESS_BITS \ 46 + __reg_orm(SDMMC_STAR, DATAEND) 47 + #define DATA_ERROR_BITS \ 48 + __reg_orm(SDMMC_STAR, DTIMEOUT, DABORT, DCRCFAIL, TXUNDERR, RXOVERR) 49 + #define DATA_END_BITS \ 50 + (DATA_SUCCESS_BITS | DATA_ERROR_BITS) 51 + 52 + static size_t get_sdmmc_bus_freq(uint32_t clock) 53 + { 54 + switch (clock) 55 + { 56 + case SDMMC_BUS_CLOCK_400KHZ: return 400000; 57 + case SDMMC_BUS_CLOCK_25MHZ: return 25000000; 58 + case SDMMC_BUS_CLOCK_50MHZ: return 50000000; 59 + default: return 0; 60 + } 61 + } 62 + 63 + static size_t abs_diff(size_t a, size_t b) 64 + { 65 + return a > b ? a - b : b - a; 66 + } 67 + 68 + static bool stm32h7_sdmmc_is_powered_off(struct stm32h7_sdmmc_controller *ctl) 69 + { 70 + uint32_t pwrctrl = reg_readlf(ctl->regs, SDMMC_POWER, PWRCTRL); 71 + 72 + return pwrctrl == BV_SDMMC_POWER_PWRCTRL_POWER_CYCLE; 73 + } 74 + 75 + void stm32h7_reset_sdmmc1(void) 76 + { 77 + reg_writef(RCC_AHB3RSTR, SDMMC1RST(1)); 78 + reg_writef(RCC_AHB3RSTR, SDMMC1RST(0)); 79 + } 80 + 81 + void stm32h7_sdmmc_init(struct stm32h7_sdmmc_controller *ctl, 82 + uint32_t instance, 83 + enum stm_clock clock, 84 + void (*reset_sdmmc)(void), 85 + void (*vcc_enable)(bool)) 86 + { 87 + memset(ctl, 0, sizeof(ctl)); 88 + 89 + ctl->regs = instance; 90 + ctl->clock = clock; 91 + ctl->reset_sdmmc = reset_sdmmc; 92 + ctl->vcc_enable = vcc_enable; 93 + 94 + semaphore_init(&ctl->sem, 1, 0); 95 + } 96 + 97 + void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled) 98 + { 99 + struct stm32h7_sdmmc_controller *ctl = controller; 100 + 101 + if (enabled) 102 + { 103 + /* Enable VCC */ 104 + if (ctl->vcc_enable) 105 + ctl->vcc_enable(true); 106 + 107 + /* Stay in power-off state for >= 1ms */ 108 + reg_writelf(ctl->regs, SDMMC_POWER, PWRCTRL_V(POWER_OFF)); 109 + sleep(1); 110 + 111 + /* Bus clock is now needed, so enable kernel clock */ 112 + stm_clock_enable(ctl->clock); 113 + 114 + /* Configure bus parameters */ 115 + stm32h7_sdmmc_set_bus_width(ctl, SDMMC_BUS_WIDTH_1BIT); 116 + stm32h7_sdmmc_set_bus_clock(ctl, SDMMC_BUS_CLOCK_400KHZ); 117 + reg_writelf(ctl->regs, SDMMC_CLKCR, PWRSAV(0)); 118 + 119 + /* Power on and wait for >= 74 SDMMC clock cycles (= 185us) */ 120 + reg_writelf(ctl->regs, SDMMC_POWER, PWRCTRL_V(POWER_ON)); 121 + udelay(200); 122 + 123 + /* Automatically stop clock when bus is not in use */ 124 + reg_writelf(ctl->regs, SDMMC_CLKCR, PWRSAV(1)); 125 + } 126 + else 127 + { 128 + /* Controller reset */ 129 + ctl->reset_sdmmc(); 130 + 131 + /* 132 + * Disable kernel clock while powered down. The SDMMC block 133 + * diagrams from the reference manual show that sdmmc_ker_ck 134 + * is only used for the clock management subunit. Most likely 135 + * the kernel clock isn't needed if the clock output is static 136 + * and the bus is powered down; some quick testing shows this 137 + * seems to be true. 138 + */ 139 + stm_clock_disable(ctl->clock); 140 + 141 + /* Disable VCC */ 142 + if (ctl->vcc_enable) 143 + ctl->vcc_enable(false); 144 + 145 + /* Stay in power cycle state for >= 1ms */ 146 + reg_writelf(ctl->regs, SDMMC_POWER, PWRCTRL_V(POWER_CYCLE)); 147 + sleep(1); 148 + } 149 + } 150 + 151 + void stm32h7_sdmmc_set_bus_width(void *controller, uint32_t width) 152 + { 153 + struct stm32h7_sdmmc_controller *ctl = controller; 154 + 155 + /* Ignore requests from sdmmc_host while powered off */ 156 + if (stm32h7_sdmmc_is_powered_off(ctl)) 157 + return; 158 + 159 + if (width == SDMMC_BUS_WIDTH_1BIT) 160 + reg_writelf(ctl->regs, SDMMC_CLKCR, WIDBUS_V(1BIT)); 161 + else if (width == SDMMC_BUS_WIDTH_4BIT) 162 + reg_writelf(ctl->regs, SDMMC_CLKCR, WIDBUS_V(4BIT)); 163 + else if (width == SDMMC_BUS_WIDTH_8BIT) 164 + reg_writelf(ctl->regs, SDMMC_CLKCR, WIDBUS_V(8BIT)); 165 + else 166 + panicf("%s", __func__); 167 + } 168 + 169 + void stm32h7_sdmmc_set_bus_clock(void *controller, uint32_t clock) 170 + { 171 + struct stm32h7_sdmmc_controller *ctl = controller; 172 + 173 + /* Ignore requests from sdmmc_host while powered off */ 174 + if (stm32h7_sdmmc_is_powered_off(ctl)) 175 + return; 176 + 177 + size_t ker_freq = stm_clock_get_frequency(ctl->clock); 178 + size_t bus_freq = get_sdmmc_bus_freq(clock); 179 + if (!bus_freq) 180 + panicf("%s", __func__); 181 + 182 + size_t div[2]; 183 + size_t freq[2]; 184 + 185 + /* Divider only supports even values, or can be bypassed (div=1) */ 186 + div[0] = ker_freq / bus_freq; 187 + if (div[0] <= 1) 188 + { 189 + div[0] = 1; 190 + div[1] = 2; 191 + } 192 + else 193 + { 194 + div[0] &= ~1u; 195 + div[1] = div[0] + 2; 196 + } 197 + 198 + for (int i = 0; i < 2; ++i) 199 + freq[i] = ker_freq / div[i]; 200 + 201 + /* Pick divider with lowest error */ 202 + int idx; 203 + if (abs_diff(freq[0], bus_freq) < abs_diff(freq[1], bus_freq)) 204 + idx = 0; 205 + else 206 + idx = 1; 207 + 208 + /* Set divider */ 209 + ctl->bus_freq = freq[idx]; 210 + reg_writelf(ctl->regs, SDMMC_CLKCR, 211 + SELCLKRX_V(SDMMC_IO_IN_CK), 212 + BUSSPEED_V(SLOW), 213 + DDR(0), 214 + NEGEDGE(0), 215 + CLKDIV(div[idx] / 2)); 216 + } 217 + 218 + int stm32h7_sdmmc_submit_command(void *controller, 219 + const struct sdmmc_host_command *cmd, 220 + struct sdmmc_host_response *resp) 221 + { 222 + struct stm32h7_sdmmc_controller *ctl = controller; 223 + 224 + uint32_t maskr = CMD_ERROR_BITS; 225 + uint32_t cmdr = __reg_orf(SDMMC_CMDR, CPSMEN(1), CMDINDEX(cmd->command)); 226 + uint32_t cmd_wait = WAIT_CMD; 227 + 228 + void *buff_addr = cmd->buffer; 229 + size_t buff_size = cmd->nr_blocks * cmd->block_len; 230 + 231 + /* 232 + * Handle response length setting 233 + */ 234 + switch (SDMMC_RESP_LENGTH(cmd->flags)) 235 + { 236 + case SDMMC_RESP_NONE: 237 + reg_vwritef(cmdr, SDMMC_CMDR, WAITRESP_V(NONE)); 238 + reg_vwritef(maskr, SDMMC_MASKR, CMDSENT(1)); 239 + break; 240 + 241 + case SDMMC_RESP_SHORT: 242 + if (cmd->flags & SDMMC_RESP_NOCRC) 243 + { 244 + reg_vwritef(cmdr, SDMMC_CMDR, WAITRESP_V(SHORT_NOCRC)); 245 + reg_vwritef(maskr, SDMMC_MASKR, CCRCFAIL(0)); 246 + } 247 + else 248 + { 249 + reg_vwritef(cmdr, SDMMC_CMDR, WAITRESP_V(SHORT)); 250 + } 251 + 252 + reg_vwritef(maskr, SDMMC_MASKR, CMDREND(1)); 253 + break; 254 + 255 + case SDMMC_RESP_LONG: 256 + reg_vwritef(cmdr, SDMMC_CMDR, WAITRESP_V(LONG)); 257 + reg_vwritef(maskr, SDMMC_MASKR, CMDREND(1)); 258 + break; 259 + 260 + default: 261 + panicf("%s: bad resp mode", __func__); 262 + break; 263 + } 264 + 265 + /* 266 + * Handle commands with data transfers 267 + */ 268 + if (SDMMC_DATA_PRESENT(cmd->flags)) 269 + { 270 + if ((uintptr_t)buff_addr & (CACHEALIGN_SIZE - 1)) 271 + panicf("%s: unaligned buffer", __func__); 272 + 273 + if (buff_size > MAX_DATA_LEN) 274 + panicf("%s: buffer too big", __func__); 275 + 276 + /* 277 + * IDMA on the SDMMC controller can't access the DTCM. 278 + * This is only possible by bounce-buffering in one of 279 + * the other memories accessible to IDMA, then using 280 + * another DMA process to copy the resulting buffer to 281 + * DTCM, which seems unnecessarily convoluted. 282 + */ 283 + if ((uintptr_t)buff_addr >= STM32_DTCM_BASE && 284 + (uintptr_t)buff_addr < STM32_DTCM_BASE + STM32_DTCM_SIZE) 285 + panicf("%s: buffer in DTCM not supported", __func__); 286 + 287 + /* Set block size */ 288 + uint32_t dctrl = 0; 289 + uint32_t dblocksize = find_first_set_bit(cmd->block_len); 290 + if (dblocksize > 14 || (cmd->block_len & (cmd->block_len - 1))) 291 + panicf("%s: incorrect block size", __func__); 292 + 293 + reg_vwritef(dctrl, SDMMC_DCTRL, DBLOCKSIZE(dblocksize)); 294 + 295 + /* Set data direction & perform needed cache operations */ 296 + if (SDMMC_DATA_DIR(cmd->flags) == SDMMC_DATA_WRITE) 297 + { 298 + commit_dcache_range(cmd->buffer, buff_size); 299 + reg_vwritef(dctrl, SDMMC_DCTRL, DTDIR(0)); 300 + } 301 + else 302 + { 303 + discard_dcache_range(cmd->buffer, buff_size); 304 + reg_vwritef(dctrl, SDMMC_DCTRL, DTDIR(1)); 305 + } 306 + 307 + /* Set buffer address in IDMA */ 308 + reg_varl(ctl->regs, SDMMC_IDMABASE0R) = (uintptr_t)buff_addr; 309 + reg_assignlf(ctl->regs, SDMMC_IDMACTRLR, IDMAEN(1)); 310 + 311 + /* Use a 10 second timeout (DTIMER is in units of bus clocks) */ 312 + reg_varl(ctl->regs, SDMMC_DTIMER) = 10 * ctl->bus_freq; 313 + reg_varl(ctl->regs, SDMMC_DLENR) = buff_size; 314 + 315 + /* DCTRL must be written last */ 316 + reg_varl(ctl->regs, SDMMC_DCTRL) = dctrl; 317 + 318 + /* Enable data phase */ 319 + reg_vwritef(cmdr, SDMMC_CMDR, CMDTRANS(1)); 320 + maskr |= DATA_END_BITS; 321 + cmd_wait |= WAIT_DATA; 322 + } 323 + else 324 + { 325 + /* Disable data transfer */ 326 + reg_assignlf(ctl->regs, SDMMC_IDMACTRLR, IDMAEN(0)); 327 + reg_varl(ctl->regs, SDMMC_DTIMER) = 0; 328 + reg_varl(ctl->regs, SDMMC_DLENR) = 0; 329 + reg_varl(ctl->regs, SDMMC_DCTRL) = 0; 330 + } 331 + 332 + /* 333 + * Set CMDSTOP bit for CMD12 (stop transmission) command; 334 + * this is needed to stop the DPSM in case of an error in 335 + * the previous data transfer command. 336 + */ 337 + if (cmd->command == SD_STOP_TRANSMISSION) 338 + reg_vwritef(cmdr, SDMMC_CMDR, CMDSTOP(1)); 339 + 340 + /* Update controller state */ 341 + ctl->cmd_resp = resp; 342 + ctl->cmd_wait = cmd_wait; 343 + ctl->cmd_error = SDMMC_STATUS_OK; 344 + ctl->need_cmd12 = false; 345 + 346 + /* Barrier to ensure state is written before IRQ handler can run */ 347 + membarrier(); 348 + 349 + /* Unmask IRQs and begin command execution */ 350 + reg_varl(ctl->regs, SDMMC_MASKR) = maskr; 351 + reg_varl(ctl->regs, SDMMC_ARGR) = cmd->argument; 352 + reg_varl(ctl->regs, SDMMC_CMDR) = cmdr; 353 + 354 + /* Wait for command completion */ 355 + semaphore_wait(&ctl->sem, TIMEOUT_BLOCK); 356 + 357 + /* 358 + * Discard data from speculative reads that may have 359 + * accessed the buffer during the DMA transfer. 360 + */ 361 + int cmd_error = ctl->cmd_error; 362 + if (cmd_error == SDMMC_STATUS_OK) 363 + { 364 + if (SDMMC_DATA_DIR(cmd->flags) == SDMMC_DATA_READ) 365 + discard_dcache_range(buff_addr, buff_size); 366 + } 367 + 368 + /* 369 + * If a data transfer command fails we need to issue CMD12 370 + * to stop DMA before returning. There is no other way to 371 + * abort the DMA transfer. 372 + */ 373 + if (ctl->need_cmd12) 374 + { 375 + static const struct sdmmc_host_command cmd12 = { 376 + .command = SD_STOP_TRANSMISSION, 377 + .flags = SDMMC_RESP_SHORT | SDMMC_RESP_BUSY, 378 + }; 379 + 380 + /* 381 + * Recursion depth is limited to 1 because this can 382 + * only happen due to a failed data transfer command, 383 + * and CMD12 is not a data transfer command. 384 + */ 385 + stm32h7_sdmmc_submit_command(ctl, &cmd12, NULL); 386 + } 387 + 388 + /* Return error from original command */ 389 + return cmd_error; 390 + } 391 + 392 + void stm32h7_sdmmc_abort_command(void *controller) 393 + { 394 + struct stm32h7_sdmmc_controller *ctl = controller; 395 + 396 + /* Prevent the IRQ handler from preempting us */ 397 + reg_varl(ctl->regs, SDMMC_MASKR) = 0; 398 + arm_dsb(); 399 + 400 + /* 401 + * Cancel any waiting command, including asking for 402 + * CMD12 if we need to abort DMA. 403 + */ 404 + if (ctl->cmd_wait) 405 + { 406 + if (ctl->cmd_wait & WAIT_DATA) 407 + ctl->need_cmd12 = true; 408 + 409 + ctl->cmd_wait = 0; 410 + semaphore_release(&ctl->sem); 411 + } 412 + } 413 + 414 + void stm32h7_sdmmc_irq_handler(struct stm32h7_sdmmc_controller *ctl) 415 + { 416 + uint32_t star = reg_readl(ctl->regs, SDMMC_STAR); 417 + uint32_t maskr = reg_readl(ctl->regs, SDMMC_MASKR); 418 + uint32_t icr = 0; 419 + 420 + if (!ctl->cmd_wait) 421 + panicf("sdmmc_irq: not waiting: %08lx", star); 422 + 423 + /* 424 + * Ignore interrupts which we haven't enabled; this is needed 425 + * to ensure the correct interrupt is used to detect command 426 + * completion (CMDSENT or CMDREND). 427 + */ 428 + star &= maskr; 429 + 430 + if (ctl->cmd_wait & WAIT_CMD) 431 + { 432 + if (star & CMD_END_BITS) 433 + { 434 + if (reg_vreadf(star, SDMMC_STAR, CTIMEOUT)) 435 + ctl->cmd_error = SDMMC_STATUS_TIMEOUT; 436 + else if (reg_vreadf(star, SDMMC_STAR, CCRCFAIL)) 437 + ctl->cmd_error = SDMMC_STATUS_INVALID_CRC; 438 + 439 + /* Copy the response, if present */ 440 + if (ctl->cmd_resp && reg_vreadf(star, SDMMC_STAR, CMDREND)) 441 + { 442 + ctl->cmd_resp->data[0] = reg_readl(ctl->regs, SDMMC_RESPR(0)); 443 + ctl->cmd_resp->data[1] = reg_readl(ctl->regs, SDMMC_RESPR(1)); 444 + ctl->cmd_resp->data[2] = reg_readl(ctl->regs, SDMMC_RESPR(2)); 445 + ctl->cmd_resp->data[3] = reg_readl(ctl->regs, SDMMC_RESPR(3)); 446 + ctl->cmd_resp = NULL; 447 + } 448 + 449 + ctl->cmd_wait &= ~WAIT_CMD; 450 + icr |= CMD_END_BITS; 451 + } 452 + } 453 + 454 + if (ctl->cmd_wait & WAIT_DATA) 455 + { 456 + if ((star & DATA_END_BITS) || ctl->cmd_error) 457 + { 458 + if (ctl->cmd_error) 459 + { 460 + /* 461 + * An error in the command phase of a data transfer 462 + * command may leave the DPSM active & DMA ongoing. 463 + * Set a flag so submit_command() can handle error 464 + * recovery. 465 + */ 466 + ctl->need_cmd12 = true; 467 + } 468 + else 469 + { 470 + if (reg_vreadf(star, SDMMC_STAR, DTIMEOUT)) 471 + ctl->cmd_error = SDMMC_STATUS_TIMEOUT; 472 + else if (reg_vreadf(star, SDMMC_STAR, DCRCFAIL)) 473 + ctl->cmd_error = SDMMC_STATUS_INVALID_CRC; 474 + else if (star & DATA_ERROR_BITS) 475 + ctl->cmd_error = SDMMC_STATUS_ERROR; 476 + } 477 + 478 + ctl->cmd_wait &= ~WAIT_DATA; 479 + icr |= DATA_END_BITS; 480 + } 481 + } 482 + 483 + /* 484 + * Each interrupt should complete at least one phase 485 + * but in error cases we can get spurious interrupts. 486 + */ 487 + if (icr) 488 + { 489 + /* Clear handled interrupts */ 490 + reg_varl(ctl->regs, SDMMC_MASKR) &= ~icr; 491 + reg_varl(ctl->regs, SDMMC_ICR) = icr; 492 + } 493 + 494 + /* Signal command complete if there are no more phases to wait for */ 495 + if (ctl->cmd_wait == 0) 496 + semaphore_release(&ctl->sem); 497 + }
+75
firmware/target/arm/stm32/sdmmc-stm32h7.h
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2025 by Aidan MacDonald 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License 14 + * as published by the Free Software Foundation; either version 2 15 + * of the License, or (at your option) any later version. 16 + * 17 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 + * KIND, either express or implied. 19 + * 20 + ****************************************************************************/ 21 + #ifndef __SDMMC_STM32H7_H__ 22 + #define __SDMMC_STM32H7_H__ 23 + 24 + #include "sdmmc_host.h" 25 + #include "semaphore.h" 26 + #include "clock-stm32h7.h" 27 + 28 + struct stm32h7_sdmmc_controller 29 + { 30 + /* SDMMC instance address */ 31 + uint32_t regs; 32 + 33 + /* SDMMC kernel clock */ 34 + enum stm_clock clock; 35 + 36 + /* Callback to reset SDMMC instance in RCC */ 37 + void (*reset_sdmmc)(void); 38 + 39 + /* Callback to enable/disable VCC supply to card */ 40 + void (*vcc_enable)(bool); 41 + 42 + /* Semaphore to wait for command completion */ 43 + struct semaphore sem; 44 + 45 + /* Current SDMMC_CK frequency in Hz (used for timeout calculation) */ 46 + uint32_t bus_freq; 47 + 48 + /* 49 + * Command handling state, may be read & written 50 + * by both thread & IRQ handler. 51 + */ 52 + struct sdmmc_host_response *cmd_resp; 53 + uint32_t cmd_wait; 54 + int cmd_error; 55 + int need_cmd12; 56 + }; 57 + 58 + void stm32h7_reset_sdmmc1(void); 59 + 60 + void stm32h7_sdmmc_init(struct stm32h7_sdmmc_controller *controller, 61 + uint32_t instance, 62 + enum stm_clock clock, 63 + void (*reset_sdmmc)(void), 64 + void (*vcc_enable)(bool)); 65 + 66 + void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled); 67 + void stm32h7_sdmmc_set_bus_width(void *controller, uint32_t width); 68 + void stm32h7_sdmmc_set_bus_clock(void *controller, uint32_t clock); 69 + int stm32h7_sdmmc_submit_command(void *controller, 70 + const struct sdmmc_host_command *cmd, 71 + struct sdmmc_host_response *resp); 72 + void stm32h7_sdmmc_abort_command(void *controller); 73 + void stm32h7_sdmmc_irq_handler(struct stm32h7_sdmmc_controller *controller); 74 + 75 + #endif /* __SDMMC_STM32H7_H__ */
+2 -2
firmware/target/arm/stm32/vectors-stm32h7.S
··· 76 76 .word UIE /* [ 46] */ 77 77 .word dma_irq_handler /* [ 47] DMA1 Stream7 */ 78 78 .word UIE /* [ 48] */ 79 - .word sdmmc_irq_handler /* [ 49] SDMMC1 */ 79 + .word sdmmc1_irq_handler /* [ 49] SDMMC1 */ 80 80 .word UIE /* [ 50] */ 81 81 .word spi3_irq_handler /* [ 51] SPI3 */ 82 82 .word UIE /* [ 52] */ ··· 151 151 .word UIE /* [121] */ 152 152 .word dma_irq_handler /* [122] MDMA */ 153 153 .word UIE /* [123] */ 154 - .word sdmmc_irq_handler /* [124] SDMMC2 */ 154 + .word sdmmc2_irq_handler /* [124] SDMMC2 */ 155 155 .word UIE /* [125] */ 156 156 .word UIE /* [126] */ 157 157 .word UIE /* [127] */