sylveos

Toy Operating System
Log | Files | Refs

commit 93f89d83bb17f5f5dbdb2f184f4c60d0861c88fd
parent 861ced844d7e2be03fc021af5376ac168563f583
Author: Sylvia Ivory <git@sivory.net>
Date:   Sun,  8 Mar 2026 20:51:28 -0700

Add pi-sd module

Diffstat:
Mbuild.zig | 4+++-
Ainclude/emmc.c | 793+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/emmc.h | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/pi-sd.c | 37+++++++++++++++++++++++++++++++++++++
Ainclude/pi-sd.h | 14++++++++++++++
Ainclude/pi.h | 11+++++++++++
6 files changed, 1050 insertions(+), 1 deletion(-)

diff --git a/build.zig b/build.zig @@ -26,7 +26,6 @@ fn pi_module_opts(target: std.Build.ResolvedTarget, optimize: std.builtin.Optimi .omit_frame_pointer = true, .single_threaded = true, .no_builtin = true, - .code_model = .small, }; } @@ -50,6 +49,9 @@ fn build_pi( const pi = b.createModule(pi_module_opts(target, optimize, b.path("pi/root.zig"))); pi.addImport("shared", shared); + pi.addIncludePath(b.path("include/")); + pi.addCSourceFile(.{ .file = b.path("include/emmc.c") }); + pi.addCSourceFile(.{ .file = b.path("include/pi-sd.c") }); const program = b.createModule(pi_module_opts(target, optimize, program_path)); program.addImport("shared", shared); diff --git a/include/emmc.c b/include/emmc.c @@ -0,0 +1,793 @@ +#include "emmc.h" +#include "pi.h" + +// Raspberry Pi EMMC driver adapted from Low Level Devel: +// https://github.com/rockytriton/LLD + +static bool wait_reg_mask(reg32 *reg, u32 mask, bool set, u32 timeout) { + for (int cycles = 0; cycles <= timeout * 10; cycles++) { + if ((*reg & mask) ? set : !set) { + return true; + } + + delay_us(100); + } + + return false; +} + +static u32 get_clock_divider(u32 base_clock) { +#define TARGET_RATE SD_CLOCK_HIGH + u32 target_div = 1; + + if (TARGET_RATE <= base_clock) { + target_div = base_clock / TARGET_RATE; + + if (base_clock % TARGET_RATE) { + target_div = 0; + } + } + + int div = -1; + for (int fb = 31; fb >= 0; fb--) { + u32 bt = (1 << fb); + + if (target_div & bt) { + div = fb; + target_div &= ~(bt); + + if (target_div) { + div++; + } + + break; + } + } + + if (div == -1) { + div = 31; + } + + if (div >= 32) { + div = 31; + } + + if (div != 0) { + div = (1 << (div - 1)); + } + + if (div >= 0x400) { + div = 0x3FF; + } + + u32 freqSel = div & 0xff; + u32 upper = (div >> 8) & 0x3; + u32 ret = (freqSel << 8) | (upper << 6) | (0 << 5); + + return ret; +} + +/* static bool switch_clock_rate(u32 base_clock, u32 target_rate) { */ +/* u32 divider = get_clock_divider(base_clock, target_rate); */ + +/* while((EMMC->status & (EMMC_STATUS_CMD_INHIBIT | EMMC_STATUS_DAT_INHIBIT))) { */ +/* delay_ms(1); */ +/* } */ + +/* u32 c1 = EMMC->control[1] & ~EMMC_CTRL1_CLK_ENABLE; */ + +/* EMMC->control[1] = c1; */ + +/* delay_ms(3); */ + +/* EMMC->control[1] = (c1 | divider) & ~0xFFE0; */ + +/* delay_ms(3); */ + +/* EMMC->control[1] = c1 | EMMC_CTRL1_CLK_ENABLE; */ + +/* delay_ms(3); */ + +/* return true; */ +/* } */ + +bool emmc_setup_clock() { + EMMC->control2 = 0; + + /* u32 rate = mailbox_clock_rate(CT_EMMC); */ + /* u32 rate = rpi_clock_curhz_get(CLOCK_EMMC); */ + u32 rate = 250000000; + + u32 n = EMMC->control[1]; + n |= EMMC_CTRL1_CLK_INT_EN; + n |= get_clock_divider(rate); + n &= ~(0xf << 16); + n |= (11 << 16); + + EMMC->control[1] = n; + + if (!wait_reg_mask(&EMMC->control[1], EMMC_CTRL1_CLK_STABLE, true, 2000)) { + /* printk("EMMC_ERR: SD CLOCK NOT STABLE\n"); */ + return false; + } + + delay_ms(30); + + //enabling the clock + EMMC->control[1] |= 4; + + delay_ms(30); + + return true; +} + +static emmc_device device = {0}; + +static const emmc_cmd INVALID_CMD = RES_CMD; + +static const emmc_cmd commands[] = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + RES_CMD, + {0, 0, 0, 0, 0, 0, RT136, 0, 1, 0, 0, 0, 2, 0}, + {0, 0, 0, 0, 0, 0, RT48, 0, 1, 0, 0, 0, 3, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0}, + {0, 0, 0, 0, 0, 0, RT136, 0, 0, 0, 0, 0, 5, 0}, + {0, 0, 0, 0, 0, 0, RT48, 0, 1, 0, 0, 0, 6, 0}, + {0, 0, 0, 0, 0, 0, RT48Busy, 0, 1, 0, 0, 0, 7, 0}, + {0, 0, 0, 0, 0, 0, RT48, 0, 1, 0, 0, 0, 8, 0}, + {0, 0, 0, 0, 0, 0, RT136, 0, 1, 0, 0, 0, 9, 0}, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + {0, 0, 0, 0, 0, 0, RT48, 0, 1, 0, 0, 0, 16, 0}, + {0, 0, 0, 1, 0, 0, RT48, 0, 1, 0, 1, 0, 17, 0}, + {0, 1, 1, 1, 1, 0, RT48, 0, 1, 0, 1, 0, 18, 0}, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + {0, 0, 0, 0, 0, 0, RT48, 0, 1, 0, 1, 0, 24, 0}, + {0, 1, 1, 0, 1, 0, RT48, 0, 1, 0, 1, 0, 25, 0}, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + {0, 0, 0, 0, 0, 0, RT48, 0, 0, 0, 0, 0, 41, 0}, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + RES_CMD, + {0, 0, 0, 1, 0, 0, RT48, 0, 1, 0, 1, 0, 51, 0}, + RES_CMD, + RES_CMD, + RES_CMD, + {0, 0, 0, 0, 0, 0, RT48, 0, 1, 0, 0, 0, 55, 0}, +}; + +static u32 sd_error_mask(sd_error err) { + return 1 << (16 + (u32)err); +} + +static void set_last_error(u32 intr_val) { + device.last_error = intr_val & 0xFFFF0000; + device.last_interrupt = intr_val; +} + +static bool do_data_transfer(emmc_cmd cmd) { + u32 wrIrpt = 0; + bool write = false; + + if (cmd.direction) { + wrIrpt = 1 << 5; + } else { + wrIrpt = 1 << 4; + write = true; + } + + u32 *data = (u32 *)device.buffer; + + for (int block = 0; block < device.transfer_blocks; block++) { + wait_reg_mask(&EMMC->int_flags, wrIrpt | 0x8000, true, 2000); + u32 intr_val = EMMC->int_flags; + EMMC->int_flags = wrIrpt | 0x8000; + + if ((intr_val & (0xffff0000 | wrIrpt)) != wrIrpt) { + set_last_error(intr_val); + return false; + } + + + u32 length = device.block_size; + + if (write) { + for (; length > 0; length -= 4) { + EMMC->data = *data++; + } + } else { + for (; length > 0; length -= 4) { + *data++ = EMMC->data; + } + } + } + + return true; +} + +static bool emmc_issue_command(emmc_cmd cmd, u32 arg, u32 timeout) { + device.last_command_value = TO_REG(&cmd); + reg32 command_reg = device.last_command_value; + + if (device.transfer_blocks > 0xFFFF) { + /* printk("EMMC_ERR: transferBlocks too large: %d\n", device.transfer_blocks); */ + return false; + } + + EMMC->block_size_count = device.block_size | (device.transfer_blocks << 16); + EMMC->arg1 = arg; + EMMC->cmd_xfer_mode = command_reg; + + int times = 0; + + while(times < timeout) { + u32 reg = EMMC->int_flags; + + if (reg & 0x8001) { + break; + } + + delay_ms(1); + times++; + } + + if (times >= timeout) { + /* printk("EMMC_WARN: emmc_issue_command timed out\n"); */ + device.last_success = false; + return false; + } + + u32 intr_val = EMMC->int_flags; + + EMMC->int_flags = 0xFFFF0001; + + if ((intr_val & 0xFFFF0001) != 1) { + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: Error waiting for command interrupt complete: %d\n", cmd.index); */ + + set_last_error(intr_val); + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: IRQFLAGS: %x - %x - %x\n", EMMC->int_flags, EMMC->status, intr_val); */ + + device.last_success = false; + return false; + } + + switch(cmd.response_type) { + case RT48: + case RT48Busy: + device.last_response[0] = EMMC->response[0]; + break; + + case RT136: + device.last_response[0] = EMMC->response[0]; + device.last_response[1] = EMMC->response[1]; + device.last_response[2] = EMMC->response[2]; + device.last_response[3] = EMMC->response[3]; + break; + } + + if (cmd.is_data) { + do_data_transfer(cmd); + } + + if (cmd.response_type == RT48Busy || cmd.is_data) { + wait_reg_mask(&EMMC->int_flags, 0x8002, true, 2000); + intr_val = EMMC->int_flags; + + EMMC->int_flags = 0xFFFF0002; + + if ((intr_val & 0xFFFF0002) != 2 && (intr_val & 0xFFFF0002) != 0x100002) { + set_last_error(intr_val); + return false; + } + + EMMC->int_flags = 0xFFFF0002; + } + + device.last_success = true; + + return true; +} + +static bool emmc_command(u32 command, u32 arg, u32 timeout) { + if (command & 0x80000000) { + /* printk("EMMC_ERR: COMMAND ERROR NOT APP\n"); */ + return false; + } + + device.last_command = commands[command]; + + if (TO_REG(&device.last_command) == TO_REG(&INVALID_CMD)) { + /* printk("EMMC_ERR: INVALID COMMAND!\n"); */ + return false; + } + + return emmc_issue_command( device.last_command, arg, timeout); +} + +static bool reset_command() { + EMMC->control[1] |= EMMC_CTRL1_RESET_CMD; + + for (int i=0; i<10000; i++) { + if (!( EMMC->control[1] & EMMC_CTRL1_RESET_CMD)) { + return true; + } + + delay_ms(1); + } + + /* printk("EMMC_ERR: Command line failed to reset properly: %x\n", EMMC->control[1]); */ + + return false; +} + +bool emmc_app_command(u32 command, u32 arg, u32 timeout) { + + if (commands[command].index >= 60) { + /* printk("EMMC_ERR: INVALID APP COMMAND\n"); */ + return false; + } + + device.last_command = commands[CTApp]; + + u32 rca = 0; + + if (device.rca) { + rca = device.rca << 16; + } + + if (emmc_issue_command( device.last_command, rca, 2000)) { + device.last_command = commands[command]; + + return emmc_issue_command( device.last_command, arg, 2000); + } + + return false; +} + +static bool check_v2_card() { + bool v2Card = false; + + if (!emmc_command( CTSendIfCond, 0x1AA, 200)) { + if (device.last_error == 0) { + /* printk("EMMC_ERR: SEND_IF_COND Timeout\n"); */ + } else if (device.last_error & (1 << 16)) { + if (!reset_command()) { + return false; + } + + EMMC->int_flags = sd_error_mask(SDECommandTimeout); + /* printk("EMMC_ERR: SEND_IF_COND CMD TIMEOUT\n"); */ + } else { + /* printk("EMMC_ERR: Failure sending SEND_IF_COND\n"); */ + return false; + } + } else { + if ((device.last_response[0] & 0xFFF) != 0x1AA) { + /* printk("EMMC_ERR: Unusable SD Card: %x\n", device.last_response[0]); */ + return false; + } + + v2Card = true; + } + + return v2Card; +} + +static bool check_usable_card() { + if (!emmc_command( CTIOSetOpCond, 0, 1000)) { + if (device.last_error == 0) { + /* printk("EMMC_ERR: CTIOSetOpCond Timeout\n"); */ + } else if (device.last_error & (1 << 16)) { + if (!reset_command()) { + return false; + } + + EMMC->int_flags = sd_error_mask(SDECommandTimeout); + } else { + /* printk("EMMC_ERR: SDIO Card not supported\n"); */ + return false; + } + } + + return true; +} + +static bool check_sdhc_support(bool v2_card) { + bool card_busy = true; + + while(card_busy) { + u32 v2_flags = 0; + + if (v2_card) { + v2_flags |= (1 << 30); //SDHC Support + } + + if (!emmc_app_command( CTOcrCheck, 0x00FF8000 | v2_flags, 2000)) { + /* printk("EMMC_ERR: APP CMD 41 FAILED 2nd\n"); */ + return false; + } + + if (device.last_response[0] >> 31 & 1) { + device.ocr = (device.last_response[0] >> 8 & 0xFFFF); + device.sdhc = ((device.last_response[0] >> 30) & 1) != 0; + card_busy = false; + } else { + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: SLEEPING: %x\n", device.last_response[0]); */ + delay_ms(500); + } + } + + return true; +} + +static bool check_ocr() { + bool passed = false; + + for (int i=0; i<5; i++) { + if (!emmc_app_command(CTOcrCheck, 0, 2000)) { + /* printk("EMMC_WARN: APP CMD OCR CHECK TRY %d FAILED\n", i + 1); */ + passed = false; + } else { + passed = true; + } + + if (passed) { + break; + } + + return false; + } + + if (!passed) { + /* printk("EMMC_ERR: APP CMD 41 FAILED\n"); */ + return false; + } + + device.ocr = (device.last_response[0] >> 8 & 0xFFFF); + + /* if (EMMC_DEBUG) printk("MEMORY OCR: %x\n", device.ocr); */ + + return true; +} + +static bool check_rca() { + if (!emmc_command( CTSendCide, 0, 2000)) { + /* printk("EMMC_ERR: Failed to send CID\n"); */ + + return false; + } + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: CARD ID: %x.%x.%x.%x\n", device.last_response[0], device.last_response[1], device.last_response[2], device.last_response[3]); */ + + if (!emmc_command( CTSendRelativeAddr, 0, 2000)) { + /* printk("EMMC_ERR: Failed to send Relative Addr\n"); */ + + return false; + } + + device.rca = (device.last_response[0] >> 16) & 0xFFFF; + + /* + if (EMMC_DEBUG) { + printk("EMMC_DEBUG: RCA: %x\n", device.rca); + + printk("EMMC_DEBUG: CRC_ERR: %d\n", (device.last_response[0] >> 15) & 1); + printk("EMMC_DEBUG: CMD_ERR: %d\n", (device.last_response[0] >> 14) & 1); + printk("EMMC_DEBUG: GEN_ERR: %d\n", (device.last_response[0] >> 13) & 1); + printk("EMMC_DEBUG: STS_ERR: %d\n", (device.last_response[0] >> 9) & 1); + printk("EMMC_DEBUG: READY : %d\n", (device.last_response[0] >> 8) & 1); + } + */ + + if (!((device.last_response[0] >> 8) & 1)) { + /* printk("EMMC_ERR: Failed to read RCA\n"); */ + return false; + } + + return true; +} + +static bool select_card() { + if (!emmc_command( CTSelectCard, device.rca << 16, 2000)) { + /* printk("EMMC_ERR: Failed to select card\n"); */ + return false; + } + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: Selected Card\n"); */ + + u32 status = (device.last_response[0] >> 9) & 0xF; + + if (status != 3 && status != 4) { + /* printk("EMMC_ERR: Invalid Status: %d\n", status); */ + return false; + } + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: Status: %d\n", status); */ + + return true; +} + +static bool set_scr() { + if (!device.sdhc) { + if (!emmc_command( CTSetBlockLen, 512, 2000)) { + /* printk("EMMC_ERR: Failed to set block len\n"); */ + return false; + } + } + + u32 bsc = EMMC->block_size_count; + bsc &= ~0xFFF; //mask off bottom bits + bsc |= 0x200; //set bottom bits to 512 + EMMC->block_size_count = bsc; + + device.buffer = &device.scr.scr[0]; + device.block_size = 8; + device.transfer_blocks = 1; + + if (!emmc_app_command( CTSendSCR, 0, 30000)) { + /* printk("EMMC_ERR: Failed to send SCR\n"); */ + return false; + } + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: GOT SRC: SCR0: %x SCR1: %x BWID: %x\n", device.scr.scr[0], device.scr.scr[1], device.scr.bus_widths); */ + + device.block_size = 512; + + u32 scr0 = BSWAP32(device.scr.scr[0]); + device.scr.version = 0xFFFFFFFF; + u32 spec = (scr0 >> (56 - 32)) & 0xf; + u32 spec3 = (scr0 >> (47 - 32)) & 0x1; + u32 spec4 = (scr0 >> (42 - 32)) & 0x1; + + if (spec == 0) { + device.scr.version = 1; + } else if (spec == 1) { + device.scr.version = 11; + } else if (spec == 2) { + + if (spec3 == 0) { + device.scr.version = 2; + } else if (spec3 == 1) { + if (spec4 == 0) { + device.scr.version = 3; + } + if (spec4 == 1) { + device.scr.version = 4; + } + } + } + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: SCR Version: %d\n", device.scr.version); */ + + return true; +} + +static bool emmc_card_reset() { + EMMC->control[1] = EMMC_CTRL1_RESET_HOST; + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: Card resetting...\n"); */ + + if (!wait_reg_mask(&EMMC->control[1], EMMC_CTRL1_RESET_ALL, false, 2000)) { + /* printk("EMMC_ERR: Card reset timeout!\n"); */ + return false; + } + + /* #if (RPI_VERSION == 4) */ + /* //This enabled VDD1 bus power for SD card, needed for RPI 4. */ + /* u32 c0 = EMMC->control[0]; */ + /* c0 |= 0x0F << 8; */ + /* EMMC->control[0] = c0; */ + /* delay_ms(3); */ + /* #endif */ + + if (!emmc_setup_clock()) { + return false; + } + + //All interrupts go to interrupt register. + EMMC->int_enable = 0; + EMMC->int_flags = 0xFFFFFFFF; + EMMC->int_mask = 0xFFFFFFFF; + + delay_ms(203); + + device.transfer_blocks = 0; + device.last_command_value = 0; + device.last_success = false; + device.block_size = 0; + + if (!emmc_command(CTGoIdle, 0, 2000)) { + /* printk("EMMC_ERR: NO GO_IDLE RESPONSE\n"); */ + return false; + } + + bool v2_card = check_v2_card(); + + if (!check_usable_card()) { + return false; + } + + if (!check_ocr()) { + return false; + } + + if (!check_sdhc_support(v2_card)) { + return false; + } + + /* switch_clock_rate(device.base_clock, SD_CLOCK_NORMAL); */ + + delay_ms(10); + + if (!check_rca()) { + return false; + } + + if (!select_card()) { + return false; + } + + if (!set_scr()) { + return false; + } + + EMMC->int_flags = 0xFFFFFFFF; + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: Card reset!\n"); */ + + return true; +} + +static bool do_data_command(bool write, u8 *b, u32 bsize, u32 block_no) { + if (!device.sdhc) { + block_no *= 512; + } + + if (bsize < device.block_size) { + /* printk("EMMC_ERR: INVALID BLOCK SIZE: \n", bsize, device.block_size); */ + return false; + } + + /* assert(device.block_size == 512); */ + /* device.transfer_blocks = bsize / device.block_size; */ + device.transfer_blocks = bsize / 512; + + /* if (bsize % device.block_size) { */ + if (bsize & 0x1ff) { + /* printk("EMMC_ERR: BAD BLOCK SIZE\n"); */ + return false; + } + + device.buffer = b; + + cmd_type command = CTReadBlock; + + if (write && device.transfer_blocks > 1) { + command = CTWriteMultiple; + } else if (write) { + command = CTWriteBlock; + } else if (!write && device.transfer_blocks > 1) { + command = CTReadMultiple; + } + + int retry_count = 0; + int max_retries = 3; + + /* if (EMMC_DEBUG) printk("EMMC_DEBUG: Sending command: %d\n", command); */ + + while(retry_count < max_retries) { + if (emmc_command( command, block_no, 5000)) { + break; + } + + if (++retry_count < max_retries) { + /* printk("EMMC_WARN: Retrying data command\n"); */ + } else { + /* printk("EMMC_ERR: Giving up data command\n"); */ + return false; + } + } + + return true; +} + +int emmc_read(u32 sector, u8 *buffer, u32 size) { + /* assert(size % 512 == 0); */ + + /* int r = do_data_command(false, buffer, size, sector); */ + /* if (r != size) { */ + /* printk("EMMC_ERR: READ FAILED: %d\n", r); */ + /* return -1; */ + /* } */ + + bool success = do_data_command(false, buffer, size, sector); + if (!success) { + /* printk("EMMC_ERR: READ FAILED: sector=%d, size=%d\n", sector, size); */ + return -1; + } + + return size; +} + +int emmc_write(u32 sector, u8 *buffer, u32 size) { + /* assert(size % 512 == 0); */ + + int r = do_data_command(true, buffer, size, sector); + if (!r) { + /* printk("EMMC_ERR: WRITE FAILED: %d\n", r); */ + return -1; + } + return size; +} + +bool emmc_init() { + gpio_set_function(34, GPIO_FUNC_INPUT); + gpio_set_function(35, GPIO_FUNC_INPUT); + gpio_set_function(36, GPIO_FUNC_INPUT); + gpio_set_function(37, GPIO_FUNC_INPUT); + gpio_set_function(38, GPIO_FUNC_INPUT); + gpio_set_function(39, GPIO_FUNC_INPUT); + + gpio_set_function(48, GPIO_FUNC_ALT3); + gpio_set_function(49, GPIO_FUNC_ALT3); + gpio_set_function(50, GPIO_FUNC_ALT3); + gpio_set_function(51, GPIO_FUNC_ALT3); + gpio_set_function(52, GPIO_FUNC_ALT3); + + device.transfer_blocks = 0; + device.last_command_value = 0; + device.last_success = false; + device.block_size = 0; + device.sdhc = false; + device.ocr = 0; + device.rca = 0; + device.base_clock = 0; + + bool success = false; + for (int i=0; i<10; i++) { + success = emmc_card_reset(); + + if (success) { + break; + } + + delay_ms(100); + /* printk("EMMC_WARN: Failed to reset card, trying again...\n"); */ + } + + if (!success) { + return false; + } + + return true; +} diff --git a/include/emmc.h b/include/emmc.h @@ -0,0 +1,192 @@ +#pragma once + +// Raspberry Pi EMMC driver adapted from Low Level Devel: +// https://github.com/rockytriton/LLD + +#include <stdint.h> +#include <stdbool.h> + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef volatile u32 reg32; + +#define EMMC_DEBUG 0 + +#define BSWAP32(x) (((x << 24) & 0xff000000 ) | \ + ((x << 8) & 0x00ff0000 ) | \ + ((x >> 8) & 0x0000ff00 ) | \ + ((x >> 24) & 0x000000ff )) + +typedef struct { + u8 resp_a : 1; + u8 block_count : 1; + u8 auto_command : 2; + u8 direction : 1; + u8 multiblock : 1; + u16 resp_b : 10; + u8 response_type : 2; + u8 res0 : 1; + u8 crc_enable : 1; + u8 idx_enable : 1; + u8 is_data : 1; + u8 type : 2; + u8 index : 6; + u8 res1 : 2; +} emmc_cmd; + +#define RES_CMD {1, 1, 3, 1, 1, 0xF, 3, 1, 1, 1, 1, 3, 0xF, 3} + +typedef enum { + RTNone, + RT136, + RT48, + RT48Busy +} cmd_resp_type; + +typedef enum { + CTGoIdle = 0, + CTSendCide = 2, + CTSendRelativeAddr = 3, + CTIOSetOpCond = 5, + CTSelectCard = 7, + CTSendIfCond = 8, + CTSetBlockLen = 16, + CTReadBlock = 17, + CTReadMultiple = 18, + CTWriteBlock = 24, + CTWriteMultiple = 25, + CTOcrCheck = 41, + CTSendSCR = 51, + CTApp = 55 +} cmd_type; + +typedef struct { + u32 scr[2]; + u32 bus_widths; + u32 version; +} scr_register; + +typedef enum { + SDECommandTimeout, + SDECommandCrc, + SDECommandEndBit, + SDECommandIndex, + SDEDataTimeout, + SDEDataCrc, + SDEDataEndBit, + SDECurrentLimit, + SDEAutoCmd12, + SDEADma, + SDETuning, + SDERsvd +} sd_error; + +typedef struct { + bool last_success; + u32 transfer_blocks; + emmc_cmd last_command; + reg32 last_command_value; + u32 block_size; + u32 last_response[4]; + bool sdhc; + u16 ocr; + u32 rca; + u64 offset; + void *buffer; + u32 base_clock; + u32 last_error; + u32 last_interrupt; + scr_register scr; +} emmc_device; + +typedef struct { + reg32 arg2; + reg32 block_size_count; + reg32 arg1; + reg32 cmd_xfer_mode; + reg32 response[4]; + reg32 data; + reg32 status; + reg32 control[2]; + reg32 int_flags; + reg32 int_mask; + reg32 int_enable; + reg32 control2; + reg32 cap1; + reg32 cap2; + reg32 res0[2]; + reg32 force_int; + reg32 res1[7]; + reg32 boot_timeout; + reg32 debug_config; + reg32 res2[2]; + reg32 ext_fifo_config; + reg32 ext_fifo_enable; + reg32 tune_step; + reg32 tune_SDR; + reg32 tune_DDR; + reg32 res3[23]; + reg32 spi_int_support; + reg32 res4[2]; + reg32 slot_int_status; +} emmc_regs; + +#define TO_REG(p) *((reg32 *)p) + +// SD Clock Frequencies (in Hz) +#define SD_CLOCK_ID 400000 +#define SD_CLOCK_NORMAL 25000000 +#define SD_CLOCK_HIGH 50000000 +#define SD_CLOCK_100 100000000 +#define SD_CLOCK_208 208000000 +#define SD_COMMAND_COMPLETE 1 +#define SD_TRANSFER_COMPLETE (1 << 1) +#define SD_BLOCK_GAP_EVENT (1 << 2) +#define SD_DMA_INTERRUPT (1 << 3) +#define SD_BUFFER_WRITE_READY (1 << 4) +#define SD_BUFFER_READ_READY (1 << 5) +#define SD_CARD_INSERTION (1 << 6) +#define SD_CARD_REMOVAL (1 << 7) +#define SD_CARD_INTERRUPT (1 << 8) + +#define EMMC_BASE 0x20300000 + +#define EMMC ((emmc_regs *)EMMC_BASE) + +#define EMMC_CTRL1_RESET_DATA (1 << 26) +#define EMMC_CTRL1_RESET_CMD (1 << 25) +#define EMMC_CTRL1_RESET_HOST (1 << 24) +#define EMMC_CTRL1_RESET_ALL (EMMC_CTRL1_RESET_DATA | EMMC_CTRL1_RESET_CMD | EMMC_CTRL1_RESET_HOST) + +#define EMMC_CTRL1_CLK_GENSEL (1 << 5) +#define EMMC_CTRL1_CLK_ENABLE (1 << 2) +#define EMMC_CTRL1_CLK_STABLE (1 << 1) +#define EMMC_CTRL1_CLK_INT_EN (1 << 0) + +#define EMMC_CTRL0_ALT_BOOT_EN (1 << 22) +#define EMMC_CTRL0_BOOT_EN (1 << 21) +#define EMMC_CTRL0_SPI_MODE (1 << 20) + +#define EMMC_STATUS_DAT_INHIBIT (1 << 1) +#define EMMC_STATUS_CMD_INHIBIT (1 << 0) + +bool emmc_init(); +int emmc_read(u32 sector, u8* buffer, u32 size); +int emmc_write(u32 sector, u8* buffer, u32 size); + +// bzt compat layer. +#define SD_OK 1 +static inline int sd_init(void) { + return emmc_init(); +} +static inline int +sd_readblock(uint32_t sec, void *data, unsigned nsec) { + return emmc_read(sec, data, nsec*512); +} +static inline int +sd_writeblock(const void *data, uint32_t sec, unsigned nsec) { + return emmc_write(sec, (void*)data, nsec*512); +} diff --git a/include/pi-sd.c b/include/pi-sd.c @@ -0,0 +1,37 @@ +#include <stdint.h> + +#include "pi-sd.h" +#include "emmc.h" + +static int init_p = 0; + +int pi_sd_init(void) { + if(sd_init() != SD_OK) return -1; + + init_p = 1; + + return 1; +} + +int pi_sd_read(void *data, uint32_t lba, uint32_t nsec) { + if (!init_p) return -1; + + int res; + + if((res = sd_readblock(lba, data, nsec)) != 512 * nsec) { + return -1; + } + + return 1; +} + +int pi_sd_write(void *data, uint32_t lba, uint32_t nsec) { + if (!init_p) return -1; + + int res; + if((res = sd_writeblock(data, lba, nsec)) != 512 * nsec) { + return -1; + } + + return 1; +} diff --git a/include/pi-sd.h b/include/pi-sd.h @@ -0,0 +1,14 @@ +#pragma once + +#include <stdint.h> + +#define NBYTES_PER_SECTOR 512 + +// initialize the PI SD driver +int pi_sd_init(void); + +// read `nsec` sectors of the SD card starting at `lba` into a buffer +int pi_sd_read(void *data, uint32_t lba, uint32_t nsec); + +// write `data` to `nsec` sectors of the SD card starting at `lba` +int pi_sd_write(void *data, uint32_t lba, uint32_t nsec); diff --git a/include/pi.h b/include/pi.h @@ -0,0 +1,11 @@ +#pragma once + +#include <stdint.h> + +#define GPIO_FUNC_INPUT 0b000 +#define GPIO_FUNC_ALT3 0b111 + +void delay_us(uint32_t us); +void delay_ms(uint32_t ms); +void delay_s(uint32_t s); +void gpio_set_function(uint8_t pin, uint8_t mode);