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.

firmware: add sdmmc_host storage driver

sdmmc_host is a portable driver for targets with SD/MMC
storage. It handles all the logic needed to initialize
and access SD/MMC devices which is common to all targets.

Targets only need to implement functions to issue SD/MMC
commands, manage clocks & power, and managing the insert
state of the card (if it is hotswappable). This vastly
reduces the work needed to get a new SD/MMC based target
up & running.

At present it's only written for and tested with SD cards,
as I don't have access to an MMC-based target to test on.

Change-Id: I6a0d7747113c11a3697ae20cbb551bef8bfd1292

authored by

Aidan MacDonald and committed by
Solomon Peachy
87bf6b4e 385483d6

+1112
+3
firmware/SOURCES
··· 2155 2155 #ifdef HAVE_CW2015 2156 2156 drivers/cw2015.c 2157 2157 #endif 2158 + #ifdef HAVE_SDMMC_HOST 2159 + drivers/sdmmc_host.c 2160 + #endif 2158 2161 #endif 2159 2162 2160 2163 /* firmware/kernel section */
+783
firmware/drivers/sdmmc_host.c
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2025 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_host.h" 22 + #include "system.h" 23 + #include "storage.h" 24 + #include "disk_cache.h" 25 + #include "debug.h" 26 + #include "panic.h" 27 + #include "logf.h" 28 + #include <string.h> 29 + 30 + /* 31 + * SD/MMC host interface 32 + */ 33 + 34 + #ifdef CONFIG_STORAGE_MULTI 35 + # define DECLARE_FIRST_DRIVE(decl) static decl 36 + #else 37 + # define DECLARE_FIRST_DRIVE(decl) static const decl = 0 38 + #endif 39 + 40 + #if CONFIG_STORAGE & STORAGE_SD 41 + static struct sdmmc_host *sdmmc_sd_hosts[SDMMC_HOST_NUM_SD_CONTROLLERS]; 42 + DECLARE_FIRST_DRIVE(int sdmmc_sd_first_drive); 43 + static volatile long sdmmc_sd_last_activity; 44 + #endif 45 + 46 + #if CONFIG_STORAGE & STORAGE_MMC 47 + static struct sdmmc_host *sdmmc_mmc_hosts[SDMMC_HOST_NUM_MMC_CONTROLLERS]; 48 + DECLARE_FIRST_DRIVE(int sdmmc_mmc_first_drive); 49 + static volatile long sdmmc_mmc_last_activity; 50 + #endif 51 + 52 + static int sdmmc_host_get_logical_drive(struct sdmmc_host *host) 53 + { 54 + switch (host->config.type) 55 + { 56 + #if CONFIG_STORAGE & STORAGE_SD 57 + case STORAGE_SD: 58 + return sdmmc_sd_first_drive + host->drive; 59 + #endif 60 + 61 + #if CONFIG_STORAGE & STORAGE_MMC 62 + case STORAGE_MMC: 63 + return sdmmc_mmc_first_drive + host->drive; 64 + #endif 65 + 66 + default: 67 + return 0; 68 + } 69 + } 70 + 71 + void sdmmc_host_init(struct sdmmc_host *host, 72 + const struct sdmmc_host_config *config, 73 + const struct sdmmc_controller_ops *ops, 74 + void *controller) 75 + { 76 + struct sdmmc_host **array = NULL; 77 + size_t array_size = 0; 78 + 79 + switch (config->type) 80 + { 81 + #if CONFIG_STORAGE & STORAGE_SD 82 + case STORAGE_SD: 83 + array = sdmmc_sd_hosts; 84 + array_size = ARRAYLEN(sdmmc_sd_hosts); 85 + break; 86 + #endif 87 + 88 + #if CONFIG_STORAGE & STORAGE_MMC 89 + case STORAGE_MMC: 90 + array = sdmmc_mmc_hosts; 91 + array_size = ARRAYLEN(sdmmc_mmc_hosts); 92 + break; 93 + #endif 94 + 95 + default: 96 + panicf("%s: bad type", __func__); 97 + return; 98 + } 99 + 100 + if (!ops->submit_command || !ops->set_bus_clock) 101 + { 102 + panicf("%s: missing ops", __func__); 103 + return; 104 + } 105 + 106 + for (size_t i = 0; i < array_size; ++i) 107 + { 108 + if (array[i] == NULL) 109 + { 110 + memset(host, 0, sizeof(*host)); 111 + 112 + host->config = *config; 113 + host->drive = i; 114 + host->ops = ops; 115 + host->controller = controller; 116 + host->present = !config->is_removable; 117 + mutex_init(&host->lock); 118 + 119 + array[i] = host; 120 + return; 121 + } 122 + } 123 + 124 + panicf("%s: too many controllers", __func__); 125 + } 126 + 127 + #ifdef HAVE_HOTSWAP 128 + void sdmmc_host_init_medium_present(struct sdmmc_host *host, bool present) 129 + { 130 + if (!host->config.is_removable) 131 + panicf("%s called for non-removable drive", __func__); 132 + 133 + host->present = present; 134 + } 135 + 136 + void sdmmc_host_set_medium_present(struct sdmmc_host *host, bool present) 137 + { 138 + long event = present ? Q_STORAGE_MEDIUM_INSERTED : Q_STORAGE_MEDIUM_REMOVED; 139 + 140 + if (!host->config.is_removable) 141 + panicf("%s called for non-removable drive", __func__); 142 + 143 + storage_post_event(event, sdmmc_host_get_logical_drive(host)); 144 + } 145 + #endif 146 + 147 + #ifdef CONFIG_STORAGE_MULTI 148 + static int sdmmc_host_count_drives(struct sdmmc_host **array, 149 + size_t array_size) 150 + { 151 + size_t num_drives = 0; 152 + for (; num_drives < array_size; ++num_drives) 153 + { 154 + if (array[num_drives] == NULL) 155 + break; 156 + } 157 + 158 + return num_drives; 159 + } 160 + #endif 161 + 162 + /* 163 + * Changes the bus power state if the target supports it. 164 + */ 165 + static void sdmmc_host_set_power_enabled(struct sdmmc_host *host, bool enabled) 166 + { 167 + if (host->powered != enabled) 168 + { 169 + if (host->ops->set_power_enabled) 170 + host->ops->set_power_enabled(host->controller, enabled); 171 + 172 + host->powered = enabled; 173 + } 174 + } 175 + 176 + /* 177 + * Resets the bus by powering it down and clearing all device-related 178 + * state. The bus must be powered back on manually before trying to 179 + * reinitialize the SD/MMC device. 180 + */ 181 + static void sdmmc_host_bus_reset(struct sdmmc_host *host) 182 + { 183 + sdmmc_host_set_power_enabled(host, false); 184 + 185 + host->need_reset = false; 186 + host->initialized = false; 187 + host->is_hcs_card = false; 188 + memset(&host->cardinfo, 0, sizeof(host->cardinfo)); 189 + } 190 + 191 + #ifdef HAVE_HOTSWAP 192 + static void sdmmc_host_hotswap_event(struct sdmmc_host *host, bool is_present) 193 + { 194 + /* Ignore spurious events */ 195 + if (host->present == is_present) 196 + return; 197 + 198 + /* Update medium presence flag so I/O threads see removals */ 199 + host->present = is_present; 200 + 201 + /* Handle sudden medium removal */ 202 + if (!is_present) 203 + { 204 + if (host->ops->abort_command) 205 + host->ops->abort_command(host->controller); 206 + 207 + mutex_lock(&host->lock); 208 + sdmmc_host_bus_reset(host); 209 + mutex_unlock(&host->lock); 210 + } 211 + 212 + /* Broadcast hotswap event to the system */ 213 + queue_broadcast(is_present ? SYS_HOTSWAP_INSERTED : SYS_HOTSWAP_EXTRACTED, 214 + sdmmc_host_get_logical_drive(host)); 215 + } 216 + #endif 217 + 218 + static bool sdmmc_host_medium_present(struct sdmmc_host *host) 219 + { 220 + #ifdef HAVE_HOTSWAP 221 + return host->present; 222 + #else 223 + return true; 224 + #endif 225 + } 226 + 227 + /* 228 + * Submit one command to the host controller. 229 + */ 230 + static int sdmmc_host_submit_cmd(struct sdmmc_host *host, 231 + const struct sdmmc_host_command *cmd, 232 + struct sdmmc_host_response *resp) 233 + { 234 + /* 235 + * TODO: generic SD/MMC error detection & recovery, examples: 236 + * 237 + * - timeout to abort the command if controller is hung 238 + * - automatic retrying of commands if CRC error occurs 239 + */ 240 + 241 + return host->ops->submit_command(host->controller, cmd, resp); 242 + } 243 + 244 + /* 245 + * Execute an SD APP_CMD 246 + */ 247 + static int sdmmc_host_submit_app_cmd(struct sdmmc_host *host, 248 + const struct sdmmc_host_command *cmd, 249 + struct sdmmc_host_response *resp) 250 + { 251 + struct sdmmc_host_response aresp; 252 + struct sdmmc_host_command acmd = { 253 + .command = SD_APP_CMD, 254 + .argument = host->cardinfo.rca, 255 + .flags = SDMMC_RESP_SHORT, 256 + }; 257 + 258 + int rc = sdmmc_host_submit_cmd(host, &acmd, &aresp); 259 + if (rc) 260 + { 261 + logf("%s: cmd55 err %d", __func__, rc); 262 + return rc; 263 + } 264 + 265 + /* Check that card accepted the APP_CMD */ 266 + if ((aresp.data[0] & SD_R1_APP_CMD) == 0) 267 + { 268 + logf("%s: cmd55 not acked", __func__); 269 + return SDMMC_STATUS_ERROR; 270 + } 271 + 272 + return sdmmc_host_submit_cmd(host, cmd, resp); 273 + } 274 + 275 + static int sdmmc_host_cmd_go_idle_state(struct sdmmc_host *host) 276 + { 277 + struct sdmmc_host_command cmd = { 278 + .command = SD_GO_IDLE_STATE, 279 + .flags = SDMMC_RESP_NONE, 280 + }; 281 + 282 + return sdmmc_host_submit_cmd(host, &cmd, NULL); 283 + } 284 + 285 + static int sdmmc_host_cmd_send_if_cond(struct sdmmc_host *host) 286 + { 287 + struct sdmmc_host_response resp; 288 + struct sdmmc_host_command cmd = { 289 + .command = SD_SEND_IF_COND, 290 + .argument = 0x1aa, 291 + .flags = SDMMC_RESP_SHORT, 292 + }; 293 + 294 + int rc = sdmmc_host_submit_cmd(host, &cmd, &resp); 295 + switch (rc) 296 + { 297 + case SDMMC_STATUS_OK: 298 + /* Valid response means it's an HCS card */ 299 + if ((resp.data[0] & 0xff) == 0xaa) 300 + { 301 + host->is_hcs_card = true; 302 + break; 303 + } 304 + // fallthrough 305 + 306 + case SDMMC_STATUS_TIMEOUT: 307 + host->is_hcs_card = false; 308 + return SDMMC_STATUS_OK; 309 + 310 + default: 311 + return rc; 312 + } 313 + return 0; 314 + } 315 + 316 + static int sdmmc_host_cmd_send_app_op_cond(struct sdmmc_host *host) 317 + { 318 + struct sdmmc_host_response resp; 319 + struct sdmmc_host_command cmd = { 320 + .command = SD_APP_OP_COND, 321 + .flags = SDMMC_RESP_SHORT | SDMMC_RESP_NOCRC, 322 + }; 323 + 324 + /* Add operating voltages */ 325 + cmd.argument |= host->config.bus_voltages; 326 + cmd.argument &= (0x1FFu << 15); 327 + 328 + /* Set HCS bit for SDHC/SDXC */ 329 + if (host->is_hcs_card) 330 + cmd.argument |= SD_OCR_CARD_CAPACITY_STATUS; 331 + 332 + /* 333 + * Maximum wait time to initialize is 1 second according 334 + * to the SD specs. 335 + */ 336 + int timeout = HZ; 337 + for (; timeout > 0; timeout--) 338 + { 339 + int rc = sdmmc_host_submit_app_cmd(host, &cmd, &resp); 340 + if (rc) 341 + return rc; 342 + 343 + /* Exit when card says it's initialized */ 344 + if (resp.data[0] & (1u << 31)) 345 + break; 346 + 347 + /* Card not initialized yet, wait & try again */ 348 + sleep(1); 349 + } 350 + 351 + if (timeout == 0) 352 + return SDMMC_STATUS_TIMEOUT; 353 + 354 + return SDMMC_STATUS_OK; 355 + } 356 + 357 + static int sdmmc_host_cmd_all_send_cid(struct sdmmc_host *host) 358 + { 359 + struct sdmmc_host_response resp; 360 + struct sdmmc_host_command cmd = { 361 + .command = SD_ALL_SEND_CID, 362 + .flags = SDMMC_RESP_LONG, 363 + }; 364 + 365 + int rc = sdmmc_host_submit_cmd(host, &cmd, &resp); 366 + if (rc) 367 + return rc; 368 + 369 + for (int i = 0; i < 4; ++i) 370 + host->cardinfo.cid[i] = resp.data[i]; 371 + 372 + return rc; 373 + } 374 + 375 + static int sdmmc_host_cmd_send_rca(struct sdmmc_host *host) 376 + { 377 + struct sdmmc_host_response resp; 378 + struct sdmmc_host_command cmd = { 379 + .command = SD_SEND_RELATIVE_ADDR, 380 + .flags = SDMMC_RESP_SHORT, 381 + }; 382 + 383 + int rc = sdmmc_host_submit_cmd(host, &cmd, &resp); 384 + if (rc) 385 + return rc; 386 + 387 + host->cardinfo.rca = resp.data[0] & (0xFFFFu << 16); 388 + return rc; 389 + } 390 + 391 + static int sdmmc_host_cmd_send_csd(struct sdmmc_host *host) 392 + { 393 + struct sdmmc_host_response resp; 394 + struct sdmmc_host_command cmd = { 395 + .command = SD_SEND_CSD, 396 + .argument = host->cardinfo.rca, 397 + .flags = SDMMC_RESP_LONG, 398 + }; 399 + 400 + int rc = sdmmc_host_submit_cmd(host, &cmd, &resp); 401 + if (rc) 402 + return rc; 403 + 404 + for (int i = 0; i < 4; ++i) 405 + host->cardinfo.csd[i] = resp.data[i]; 406 + 407 + sd_parse_csd(&host->cardinfo); 408 + return rc; 409 + } 410 + 411 + static int sdmmc_host_cmd_select_card(struct sdmmc_host *host) 412 + { 413 + struct sdmmc_host_command cmd = { 414 + .command = SD_SELECT_CARD, 415 + .argument = host->cardinfo.rca, 416 + .flags = SDMMC_RESP_SHORT | SDMMC_RESP_BUSY, 417 + }; 418 + 419 + return sdmmc_host_submit_cmd(host, &cmd, NULL); 420 + } 421 + 422 + static int sdmmc_host_cmd_clr_card_detect(struct sdmmc_host *host) 423 + { 424 + struct sdmmc_host_command cmd = { 425 + .command = SD_SET_CLR_CARD_DETECT, 426 + .argument = 0, 427 + .flags = SDMMC_RESP_SHORT, 428 + }; 429 + 430 + return sdmmc_host_submit_app_cmd(host, &cmd, NULL); 431 + } 432 + 433 + static int sdmmc_host_cmd_set_bus_width(struct sdmmc_host *host, 434 + uint32_t bus_width) 435 + { 436 + struct sdmmc_host_command cmd = { 437 + .command = SD_SET_BUS_WIDTH, 438 + .flags = SDMMC_RESP_SHORT, 439 + }; 440 + 441 + if (bus_width == SDMMC_BUS_WIDTH_1BIT) 442 + cmd.argument = 0; 443 + else if (bus_width == SDMMC_BUS_WIDTH_4BIT) 444 + cmd.argument = 2; 445 + else 446 + return SDMMC_STATUS_ERROR; 447 + 448 + return sdmmc_host_submit_app_cmd(host, &cmd, NULL); 449 + } 450 + 451 + static int sdmmc_host_cmd_switch_freq(struct sdmmc_host *host, 452 + uint32_t clock) 453 + { 454 + struct sdmmc_host_command cmd = { 455 + .command = SD_SWITCH_FUNC, 456 + .flags = SDMMC_RESP_SHORT | SDMMC_DATA_READ, 457 + .nr_blocks = 1, 458 + .block_len = 64, 459 + }; 460 + 461 + if (clock == SDMMC_BUS_CLOCK_25MHZ) 462 + cmd.argument = 0x80fffff0; 463 + else if (clock == SDMMC_BUS_CLOCK_50MHZ) 464 + cmd.argument = 0x80fffff1; 465 + else 466 + return SDMMC_STATUS_ERROR; 467 + 468 + /* 469 + * We'll just assume the disk cache will have a free buffer. 470 + * Since the disk isn't mounted, we should have at least one 471 + * free that would otherwise be used by the FAT filesystem. 472 + */ 473 + cmd.buffer = dc_get_buffer(); 474 + if (!cmd.buffer) 475 + panicf("%s: OOM", __func__); 476 + 477 + int rc = sdmmc_host_submit_cmd(host, &cmd, NULL); 478 + 479 + dc_release_buffer(cmd.buffer); 480 + return rc; 481 + } 482 + 483 + static int sdmmc_host_cmd_set_block_len(struct sdmmc_host *host, int len) 484 + { 485 + struct sdmmc_host_command cmd = { 486 + .command = SD_SET_BLOCKLEN, 487 + .argument = len, 488 + .flags = SDMMC_RESP_SHORT, 489 + }; 490 + 491 + return sdmmc_host_submit_cmd(host, &cmd, NULL); 492 + } 493 + 494 + static void sdmmc_host_set_controller_bus_width(struct sdmmc_host *host, uint32_t width) 495 + { 496 + if (host->ops->set_bus_width) 497 + host->ops->set_bus_width(host->controller, width); 498 + } 499 + 500 + static void sdmmc_host_set_controller_bus_clock(struct sdmmc_host *host, uint32_t clock) 501 + { 502 + host->ops->set_bus_clock(host->controller, clock); 503 + } 504 + 505 + /* 506 + * Initialize the SD/MMC device and make it ready to access. 507 + * On success, sets host->initialized to true and returns 0. 508 + * Returns nonzero on error. If an error occurs the bus must 509 + * be reset before retrying. 510 + * 511 + * Preconditions: 512 + * - host->initialized is false 513 + * - device related state is reset to default 514 + */ 515 + static int sdmmc_host_device_init(struct sdmmc_host *host) 516 + { 517 + /* TODO: MMC initialization */ 518 + if (host->config.type != STORAGE_SD) 519 + panicf("%s: TODO mmc init", __func__); 520 + 521 + /* Initialize bus */ 522 + sdmmc_host_set_controller_bus_width(host, SDMMC_BUS_WIDTH_1BIT); 523 + sdmmc_host_set_controller_bus_clock(host, SDMMC_BUS_CLOCK_400KHZ); 524 + sdmmc_host_set_power_enabled(host, true); 525 + 526 + /* Handle SD initialization */ 527 + int rc = sdmmc_host_cmd_go_idle_state(host); 528 + if (rc) 529 + { 530 + logf("sdmmc_host_cmd_go_idle_state: %d", rc); 531 + return rc; 532 + } 533 + 534 + rc = sdmmc_host_cmd_send_if_cond(host); 535 + if (rc) 536 + { 537 + logf("sdmmc_host_cmd_send_if_cond: %d", rc); 538 + return rc; 539 + } 540 + 541 + rc = sdmmc_host_cmd_send_app_op_cond(host); 542 + if (rc) 543 + { 544 + logf("sdmmc_host_cmd_send_app_op_cond: %d", rc); 545 + return rc; 546 + } 547 + 548 + rc = sdmmc_host_cmd_all_send_cid(host); 549 + if (rc) 550 + { 551 + logf("sdmmc_host_cmd_all_send_cid: %d", rc); 552 + return rc; 553 + } 554 + 555 + rc = sdmmc_host_cmd_send_rca(host); 556 + if (rc) 557 + { 558 + logf("sdmmc_host_cmd_send_rca: %d", rc); 559 + return rc; 560 + } 561 + 562 + rc = sdmmc_host_cmd_send_csd(host); 563 + if (rc) 564 + { 565 + logf("sdmmc_host_cmd_send_csd: %d", rc); 566 + return rc; 567 + } 568 + 569 + rc = sdmmc_host_cmd_select_card(host); 570 + if (rc) 571 + { 572 + logf("sdmmc_host_cmd_select_card: %d", rc); 573 + return rc; 574 + } 575 + 576 + rc = sdmmc_host_cmd_clr_card_detect(host); 577 + if (rc) 578 + { 579 + logf("sdmmc_host_cmd_select_card: %d", rc); 580 + return rc; 581 + } 582 + 583 + /* 584 + * All SD cards must support 1-bit and 4-bit bus widths, 585 + * so we only need to check the controller capabilities. 586 + */ 587 + if (host->config.bus_widths & SDMMC_BUS_WIDTH_4BIT) 588 + { 589 + rc = sdmmc_host_cmd_set_bus_width(host, SDMMC_BUS_WIDTH_4BIT); 590 + if (rc) 591 + { 592 + logf("sdmmc_host_cmd_set_bus_width: %d", rc); 593 + return rc; 594 + } 595 + 596 + sdmmc_host_set_controller_bus_width(host, SDMMC_BUS_WIDTH_4BIT); 597 + } 598 + 599 + /* 600 + * Switch to highest clock frequency supported by both 601 + * card and controller. 25MHz must always be supported. 602 + */ 603 + if (host->cardinfo.sd2plus && 604 + (host->config.bus_clocks & SDMMC_BUS_CLOCK_50MHZ)) 605 + { 606 + rc = sdmmc_host_cmd_switch_freq(host, SDMMC_BUS_CLOCK_50MHZ); 607 + if (rc) 608 + { 609 + logf("sdmmc_host_cmd_switch_freq: %d", rc); 610 + return rc; 611 + } 612 + 613 + sdmmc_host_set_controller_bus_clock(host, SDMMC_BUS_CLOCK_50MHZ); 614 + } 615 + else 616 + { 617 + sdmmc_host_set_controller_bus_clock(host, SDMMC_BUS_CLOCK_25MHZ); 618 + } 619 + 620 + host->initialized = true; 621 + return 0; 622 + } 623 + 624 + static int sdmmc_host_transfer(struct sdmmc_host *host, 625 + sector_t start, int count, void *buf, 626 + uint32_t data_dir) 627 + { 628 + int rc = -1; 629 + 630 + mutex_lock(&host->lock); 631 + 632 + if (!sdmmc_host_medium_present(host)) 633 + goto out; 634 + 635 + if (host->need_reset) 636 + { 637 + /* Automatically clears need_reset flag */ 638 + sdmmc_host_bus_reset(host); 639 + } 640 + 641 + if (!host->initialized) 642 + { 643 + rc = sdmmc_host_device_init(host); 644 + if (rc) 645 + { 646 + host->need_reset = true; 647 + goto out; 648 + } 649 + } 650 + 651 + if (count < 1) 652 + goto out; 653 + if (start + count > host->cardinfo.numblocks) 654 + goto out; 655 + 656 + while (count > 0) 657 + { 658 + /* TODO: multiple block transfers */ 659 + int xfer_count = 1; 660 + 661 + /* Set block length for non-HCS cards */ 662 + if (!host->is_hcs_card) 663 + { 664 + rc = sdmmc_host_cmd_set_block_len(host, SD_BLOCK_SIZE); 665 + if (rc) 666 + goto out; 667 + } 668 + 669 + struct sdmmc_host_command cmd = { 670 + .buffer = buf, 671 + .nr_blocks = 1, 672 + .block_len = SD_BLOCK_SIZE, 673 + .flags = SDMMC_RESP_SHORT | data_dir, 674 + }; 675 + 676 + if (data_dir == SDMMC_DATA_WRITE) 677 + cmd.command = SD_WRITE_BLOCK; 678 + else 679 + cmd.command = SD_READ_SINGLE_BLOCK; 680 + 681 + if (host->cardinfo.sd2plus) 682 + cmd.argument = start; 683 + else 684 + cmd.argument = start * SD_BLOCK_SIZE; 685 + 686 + rc = sdmmc_host_submit_cmd(host, &cmd, NULL); 687 + if (rc) 688 + goto out; 689 + 690 + buf += xfer_count * SD_BLOCK_SIZE; 691 + start += xfer_count; 692 + count -= xfer_count; 693 + } 694 + 695 + out: 696 + mutex_unlock(&host->lock); 697 + return rc; 698 + } 699 + 700 + /* 701 + * SD storage API 702 + */ 703 + 704 + #if CONFIG_STORAGE & STORAGE_SD 705 + int sd_init(void) 706 + { 707 + return 0; 708 + } 709 + 710 + #ifdef CONFIG_STORAGE_MULTI 711 + int sd_num_drives(int first_drive) 712 + { 713 + sdmmc_sd_first_drive = first_drive; 714 + return sdmmc_host_count_drives(sdmmc_sd_hosts, ARRAYLEN(sdmmc_sd_hosts)); 715 + } 716 + #endif 717 + 718 + tCardInfo *card_get_info_target(int drive) 719 + { 720 + /* FIXME: this API is racy, no way to fix without refactoring */ 721 + return &sdmmc_sd_hosts[drive]->cardinfo; 722 + } 723 + 724 + long sd_last_disk_activity(void) 725 + { 726 + return sdmmc_sd_last_activity; 727 + } 728 + 729 + int sd_event(long id, intptr_t data) 730 + { 731 + #ifdef HAVE_HOTSWAP 732 + if (id == Q_STORAGE_MEDIUM_INSERTED || 733 + id == Q_STORAGE_MEDIUM_REMOVED) 734 + { 735 + bool present = (id == Q_STORAGE_MEDIUM_INSERTED); 736 + 737 + sdmmc_host_hotswap_event(sdmmc_sd_hosts[IF_MD_DRV(drive)], present); 738 + return 0; 739 + } 740 + #endif 741 + 742 + return storage_event_default_handler(id, data, 743 + sdmmc_sd_last_activity, STORAGE_SD); 744 + } 745 + 746 + int sd_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf) 747 + { 748 + struct sdmmc_host *host = sdmmc_sd_hosts[IF_MD_DRV(drive)]; 749 + 750 + sdmmc_sd_last_activity = current_tick; 751 + 752 + return sdmmc_host_transfer(host, start, count, buf, SDMMC_DATA_READ); 753 + } 754 + 755 + int sd_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf) 756 + { 757 + struct sdmmc_host *host = sdmmc_sd_hosts[IF_MD_DRV(drive)]; 758 + 759 + sdmmc_sd_last_activity = current_tick; 760 + 761 + return sdmmc_host_transfer(host, start, count, (void *)buf, SDMMC_DATA_WRITE); 762 + } 763 + 764 + #ifdef HAVE_HOTSWAP 765 + bool sd_removable(IF_MD_NONVOID(int drive)) 766 + { 767 + struct sdmmc_host *host = sdmmc_sd_hosts[IF_MD_DRV(drive)]; 768 + 769 + return host->config.is_removable; 770 + } 771 + 772 + bool sd_present(IF_MD_NONVOID(int drive)) 773 + { 774 + struct sdmmc_host *host = sdmmc_sd_hosts[IF_MD_DRV(drive)]; 775 + 776 + return sdmmc_host_medium_present(host); 777 + } 778 + #endif /* HAVE_HOTSWAP */ 779 + #endif /* CONFIG_STORAGE & STORAGE_SD */ 780 + 781 + /* 782 + * TODO: add MMC storage API; right now nothing needs it 783 + */
+9
firmware/export/config.h
··· 1448 1448 # define HAVE_PERCEPTUAL_VOLUME 1449 1449 #endif 1450 1450 1451 + #if defined(SDMMC_HOST_NUM_SD_CONTROLLERS) || \ 1452 + defined(SDMMC_HOST_NUM_MMC_CONTROLLERS) 1453 + # define HAVE_SDMMC_HOST 1454 + #endif 1455 + 1456 + #if defined(HAVE_SDMMC_HOST) && defined(HAVE_HOTSWAP) 1457 + # define HAVE_HOTSWAP_IN_THREAD 1458 + #endif 1459 + 1451 1460 /* 1452 1461 * Turn off legacy codepage handling in the filesystem code for bootloaders, 1453 1462 * and support ISO-8859-1 (Latin-1) only. This only affects DOS 8.3 filename
+291
firmware/export/sdmmc_host.h
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2025 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_HOST_H__ 22 + #define __SDMMC_HOST_H__ 23 + 24 + /* 25 + * Generic SD/MMC host driver which implements the storage 26 + * API for targets with SD/MMC based storage. Targets only 27 + * have to implement a simplified controller interface to 28 + * process SD/MMC commands, and manage clocks/power. 29 + * 30 + * sdmmc_host supports hotswappable storage, but the target 31 + * is responsible for detecting insertion and removals, and 32 + * informing sdmmc_host. 33 + * 34 + * To enable this driver add the following defines in your 35 + * target config.h to set the number of SD/MMC controllers 36 + * on the system: 37 + * 38 + * - SDMMC_HOST_NUM_SD_CONTROLLERS 39 + * - SDMMC_HOST_NUM_MMC_CONTROLLERS 40 + * 41 + * You also need to implement `struct sdmmc_controller_ops` 42 + * for your hardware, and initialize the host drivers for 43 + * each storage device from the target-specific function 44 + * `sdmmc_host_target_init()`. 45 + */ 46 + 47 + #include "storage.h" 48 + #include "sdmmc.h" 49 + #include "mutex.h" 50 + #include <stdbool.h> 51 + #include <stdint.h> 52 + #include <stddef.h> 53 + 54 + /* Response length flags */ 55 + #define SDMMC_RESP_NONE 0x00 56 + #define SDMMC_RESP_SHORT 0x01 57 + #define SDMMC_RESP_LONG 0x02 58 + #define SDMMC_RESP_LENGTH(flags) ((flags) & 0x03) 59 + 60 + /* Response requires busy polling */ 61 + #define SDMMC_RESP_BUSY 0x04 62 + 63 + /* Response has no CRC */ 64 + #define SDMMC_RESP_NOCRC 0x08 65 + 66 + /* Data present & direction of data transfer */ 67 + #define SDMMC_DATA_READ 0x10 68 + #define SDMMC_DATA_WRITE 0x30 69 + #define SDMMC_DATA_PRESENT(flags) ((flags) & 0x10) 70 + #define SDMMC_DATA_DIR(flags) ((flags) & 0x30) 71 + 72 + /* Status codes returned by submit_command() callback */ 73 + #define SDMMC_STATUS_OK 0 74 + #define SDMMC_STATUS_ERROR (-1) 75 + #define SDMMC_STATUS_TIMEOUT (-2) 76 + #define SDMMC_STATUS_INVALID_CRC (-3) 77 + 78 + struct sdmmc_host_command 79 + { 80 + uint16_t flags; 81 + uint16_t command; 82 + uint32_t argument; 83 + uint16_t nr_blocks; 84 + uint16_t block_len; 85 + void *buffer; 86 + }; 87 + 88 + struct sdmmc_host_response 89 + { 90 + uint32_t data[4]; 91 + }; 92 + 93 + /* Possible bus voltages */ 94 + #define SDMMC_BUS_VOLTAGE_2V7_2V8 (1 << 15) 95 + #define SDMMC_BUS_VOLTAGE_2V8_2V9 (1 << 16) 96 + #define SDMMC_BUS_VOLTAGE_2V9_3V0 (1 << 17) 97 + #define SDMMC_BUS_VOLTAGE_3V0_3V1 (1 << 18) 98 + #define SDMMC_BUS_VOLTAGE_3V1_3V2 (1 << 19) 99 + #define SDMMC_BUS_VOLTAGE_3V2_3V3 (1 << 20) 100 + #define SDMMC_BUS_VOLTAGE_3V3_3V4 (1 << 21) 101 + #define SDMMC_BUS_VOLTAGE_3V4_3V5 (1 << 22) 102 + #define SDMMC_BUS_VOLTAGE_3V5_3V6 (1 << 23) 103 + 104 + /* Possible bus widths */ 105 + #define SDMMC_BUS_WIDTH_1BIT (1 << 0) 106 + #define SDMMC_BUS_WIDTH_4BIT (1 << 1) 107 + #define SDMMC_BUS_WIDTH_8BIT (1 << 2) 108 + 109 + /* Possible bus clock speeds */ 110 + #define SDMMC_BUS_CLOCK_400KHZ (1 << 0) 111 + #define SDMMC_BUS_CLOCK_25MHZ (1 << 1) 112 + #define SDMMC_BUS_CLOCK_50MHZ (1 << 2) 113 + 114 + /** 115 + * Callbacks for implementing an SD/MMC host controller. 116 + * 117 + * Most functions here are called with the sdmmc_host lock 118 + * held which prevents multiple threads from accessing the 119 + * same controller. 120 + */ 121 + struct sdmmc_controller_ops 122 + { 123 + /** 124 + * \brief Enable/disable power supply of the SD/MMC device 125 + * \param controller Controller pointer 126 + * \param enable Whether power should be enabled or not 127 + * 128 + * sdmmc_host will try to power cycle the device after 129 + * non-recoverable errors. Removable devices will also 130 + * have bus power enabled/disabled when the device is 131 + * inserted or removed. 132 + * 133 + * Even if the target can't physically power cycle the 134 + * card, a reset of the SDMMC controller is often good 135 + * enough. 136 + * 137 + * Optional. Not implementing this function does not 138 + * change any behavior of sdmmc_host -- it will still 139 + * try to recover from errors by reinitializing the 140 + * SDMMC device. 141 + */ 142 + void (*set_power_enabled) (void *controller, bool enable); 143 + 144 + /** 145 + * \brief Set the bus width (number of active data lines) 146 + * \param controller Controller pointer 147 + * \param width One of the SDMMC_BUS_WIDTH constants 148 + * 149 + * Optional. If not implemented then the bus width is 150 + * limited to 1 bit wide. 151 + */ 152 + void (*set_bus_width) (void *controller, uint32_t width); 153 + 154 + /** 155 + * \brief Set the frequency of the clock provided to the SD/MMC device 156 + * \param controller Controller pointer 157 + * \param clock One of the SDMMC_BUS_CLOCK constants 158 + * 159 + * All controllers must implement this funtion and support 160 + * at least 400 KHz and 25 MHz rates. 161 + */ 162 + void (*set_bus_clock) (void *controller, uint32_t clock); 163 + 164 + /** 165 + * \brief Submit a command to the controller and wait for the response 166 + * \param cmd Command descriptor 167 + * \param resp Response data buffer; may be NULL 168 + * 169 + * \retval SDMMC_STATUS_OK if command completed successfully 170 + * \retval SDMMC_STATUS_TIMEOUT if failed due to command timeout 171 + * \retval SDMMC_STATUS_INVALID_CRC if failed due to an invalid CRC 172 + * \retval SDMMC_STATUS_ERROR if failed in any other way 173 + * 174 + * Note that if `resp` is NULL, the controller must still use 175 + * the response format specified in `cmd->flags`; the response 176 + * data, if any, should be discarded. 177 + */ 178 + int (*submit_command) (void *controller, 179 + const struct sdmmc_host_command *cmd, 180 + struct sdmmc_host_response *resp); 181 + 182 + /** 183 + * Abort command processing. Any in-progress call to 184 + * submit_command() must be made to return as soon as 185 + * possible with a nonzero status code. 186 + * 187 + * If no command is in progress then this function 188 + * must do nothing. 189 + * 190 + * Optional. If not implemented then the controller is 191 + * responsible for ensuring that submit_command() will 192 + * eventually return even in case of device removal or 193 + * a command unexpectedly hanging. 194 + * 195 + * May be called without the sdmmc_host lock held. 196 + */ 197 + void (*abort_command) (void *controller); 198 + }; 199 + 200 + struct sdmmc_host_config 201 + { 202 + /* Storage type: either STORAGE_SD or STORAGE_MMC */ 203 + int type; 204 + 205 + /* Supported voltages, bus widths, and clock speeds */ 206 + uint32_t bus_voltages; 207 + uint32_t bus_widths; 208 + uint32_t bus_clocks; 209 + 210 + /* Set to true if the device is removable at runtime */ 211 + bool is_removable; 212 + }; 213 + 214 + struct sdmmc_host 215 + { 216 + /* Static configuration */ 217 + struct sdmmc_host_config config; 218 + 219 + /* Physical drive number */ 220 + int drive; 221 + 222 + #ifdef HAVE_HOTSWAP 223 + /* Medium presence flag */ 224 + volatile int present; 225 + #endif 226 + 227 + /* 228 + * Lock which protects most controller operations 229 + * and mutable sdmmc_host state. 230 + */ 231 + struct mutex lock; 232 + 233 + /* Bus & device state flags; must only be accessed with lock held */ 234 + bool need_reset : 1; 235 + bool powered : 1; 236 + bool initialized : 1; 237 + bool is_hcs_card : 1; 238 + 239 + /* Controller implemented by the target */ 240 + const struct sdmmc_controller_ops *ops; 241 + void *controller; 242 + 243 + /* Card info probed from card */ 244 + tCardInfo cardinfo; 245 + }; 246 + 247 + /** 248 + * Called during storage_init() to add SD/MMC host controllers. 249 + * The target should call sdmmc_host_init() for each controller 250 + * in the system. 251 + */ 252 + void sdmmc_host_target_init(void) INIT_ATTR; 253 + 254 + /** 255 + * \brief Initialize an SD/MMC host controller 256 + * \param host Host driver state, allocated by the target 257 + * \param config Static configuration for host controller 258 + * \param ops Controller callbacks 259 + * \param controller Opaque pointer passed to ops callbacks 260 + * 261 + * Physical drive numbers will be assigned in the order of 262 + * initialization, eg. the first host will be drive 0, the 263 + * second will be drive 1, and so forth. 264 + */ 265 + void sdmmc_host_init(struct sdmmc_host *host, 266 + const struct sdmmc_host_config *config, 267 + const struct sdmmc_controller_ops *ops, 268 + void *controller) INIT_ATTR; 269 + 270 + /** 271 + * \brief Set initial SD/MMC medium presence 272 + * \param host Host controller 273 + * \param present True if medium is present 274 + * \note Only use this function from `sdmmc_host_target_init()` to set 275 + * the initial medium presence state. Only needs to be called for 276 + * drives that support removable media; non-removable drives will 277 + * always be considered inserted. 278 + */ 279 + void sdmmc_host_init_medium_present(struct sdmmc_host *host, bool present) INIT_ATTR; 280 + 281 + /** 282 + * \brief Set SD/MMC medium presence state 283 + * \param host Host controller 284 + * \param present True if medium is present 285 + * \note Do not use this function from `sdmmc_host_target_init()` since 286 + * it relies on the storage queue being available. You must instead 287 + * call `sdmmc_host_init_medium_present()` to set the initial state. 288 + */ 289 + void sdmmc_host_set_medium_present(struct sdmmc_host *host, bool present); 290 + 291 + #endif /* __SDMMC_HOST_H__ */
+5
firmware/export/storage.h
··· 58 58 #ifdef STORAGE_CLOSE 59 59 Q_STORAGE_CLOSE, 60 60 #endif 61 + #ifdef HAVE_HOTSWAP_IN_THREAD 62 + Q_STORAGE_MEDIUM_INSERTED, 63 + Q_STORAGE_MEDIUM_REMOVED, 64 + #endif 61 65 }; 62 66 63 67 #define STG_EVENT_ASSERT_ACTIVE(type) \ ··· 118 122 119 123 int storage_init(void) STORAGE_INIT_ATTR; 120 124 void storage_close(void); 125 + void storage_post_event(long event, intptr_t data); 121 126 122 127 #ifdef HAVE_HOSTFS 123 128 #include "hostfs.h"
+21
firmware/storage.c
··· 25 25 #include "disk.h" 26 26 #include "pathfuncs.h" 27 27 28 + #ifdef HAVE_SDMMC_HOST 29 + # include "sdmmc_host.h" 30 + #endif 31 + 28 32 #ifdef CONFIG_STORAGE_MULTI 29 33 30 34 #define DRIVER_MASK 0xff000000 ··· 181 185 } 182 186 break; 183 187 188 + #ifdef HAVE_HOTSWAP_IN_THREAD 189 + case Q_STORAGE_MEDIUM_INSERTED: 190 + case Q_STORAGE_MEDIUM_REMOVED: 191 + storage_event_send(DRIVE_EVT, ev.id, ev.data); 192 + break; 193 + #endif 194 + 184 195 #if (CONFIG_STORAGE & STORAGE_ATA) 185 196 case Q_STORAGE_SLEEP: 186 197 storage_event_send(bdcast, ev.id, 0); ··· 255 266 } 256 267 #endif /* STORAGE_CLOSE */ 257 268 269 + void storage_post_event(long event, intptr_t data) 270 + { 271 + if (storage_thread_id) 272 + queue_post(&storage_queue, event, data); 273 + } 274 + 258 275 static inline void storage_thread_init(void) 259 276 { 260 277 if (storage_thread_id) { ··· 272 289 int storage_init(void) 273 290 { 274 291 int rc=0; 292 + 293 + #ifdef HAVE_SDMMC_HOST 294 + sdmmc_host_target_init(); 295 + #endif 275 296 276 297 #ifdef CONFIG_STORAGE_MULTI 277 298 int i;