#include "boot.h" /* Card type flags (card_type) */ #define CT_MMC 0x01 /* MMC ver 3 */ #define CT_SD1 0x02 /* SD ver 1 */ #define CT_SD2 0x04 /* SD ver 2 */ #define CT_SDC (CT_SD1|CT_SD2) /* SD */ #define CT_BLOCK 0x08 /* Block addressing */ #define CMD0 (0) /* GO_IDLE_STATE */ #define CMD1 (1) /* SEND_OP_COND */ #define CMD2 (2) /* SEND_CID */ #define CMD3 (3) /* RELATIVE_ADDR */ #define CMD4 (4) #define CMD5 (5) /* SLEEP_WAKE (SDC) */ #define CMD6 (6) /* SWITCH_FUNC */ #define CMD7 (7) /* SELECT */ #define CMD8 (8) /* SEND_IF_COND */ #define CMD9 (9) /* SEND_CSD */ #define CMD10 (10) /* SEND_CID */ #define CMD11 (11) #define CMD12 (12) /* STOP_TRANSMISSION */ #define CMD13 (13) #define CMD15 (15) #define CMD16 (16) /* SET_BLOCKLEN */ #define CMD17 (17) /* READ_SINGLE_BLOCK */ #define CMD18 (18) /* READ_MULTIPLE_BLOCK */ #define CMD19 (19) #define CMD20 (20) #define CMD23 (23) #define CMD24 (24) #define CMD25 (25) #define CMD27 (27) #define CMD28 (28) #define CMD29 (29) #define CMD30 (30) #define CMD32 (32) #define CMD33 (33) #define CMD38 (38) #define CMD42 (42) #define CMD55 (55) /* APP_CMD */ #define CMD56 (56) #define ACMD6 (0x80+6) /* define the data bus width */ #define ACMD41 (0x80+41) /* SEND_OP_COND (ACMD) */ // Capability bits #define SDC_CAPABILITY_SD_4BIT 0x0001 #define SDC_CAPABILITY_SD_RESET 0x0002 #define SDC_CAPABILITY_ADDR 0xff00 // Control bits #define SDC_CONTROL_SD_4BIT 0x0001 #define SDC_CONTROL_SD_RESET 0x0002 // Card detect bits #define SDC_CARD_INSERT_INT_EN 0x0001 #define SDC_CARD_INSERT_INT_REQ 0x0002 #define SDC_CARD_REMOVE_INT_EN 0x0004 #define SDC_CARD_REMOVE_INT_REQ 0x0008 // Command status bits #define SDC_CMD_INT_STATUS_CC 0x0001 // Command complete #define SDC_CMD_INT_STATUS_EI 0x0002 // Any error #define SDC_CMD_INT_STATUS_CTE 0x0004 // Timeout #define SDC_CMD_INT_STATUS_CCRC 0x0008 // CRC error #define SDC_CMD_INT_STATUS_CIE 0x0010 // Command code check error // Data status bits #define SDC_DAT_INT_STATUS_TRS 0x0001 // Transfer complete #define SDC_DAT_INT_STATUS_ERR 0x0002 // Any error #define SDC_DAT_INT_STATUS_CTE 0x0004 // Timeout #define SDC_DAT_INT_STATUS_CRC 0x0008 // CRC error #define SDC_DAT_INT_STATUS_CFE 0x0010 // Data FIFO underrun or overrun #define ERR_EOF 30 #define ERR_NOT_ELF 31 #define ERR_ELF_BITS 32 #define ERR_ELF_ENDIANNESS 33 #define ERR_CMD_CRC 34 #define ERR_CMD_CHECK 35 #define ERR_DATA_CRC 36 #define ERR_DATA_FIFO 37 #define ERR_BUF_ALIGNMENT 38 struct sdc_regs { volatile uint32_t argument; volatile uint32_t command; volatile uint32_t response1; volatile uint32_t response2; volatile uint32_t response3; volatile uint32_t response4; volatile uint32_t data_timeout; volatile uint32_t control; volatile uint32_t cmd_timeout; volatile uint32_t clock_divider; volatile uint32_t software_reset; volatile uint32_t power_control; volatile uint32_t capability; volatile uint32_t cmd_int_status; volatile uint32_t cmd_int_enable; volatile uint32_t dat_int_status; volatile uint32_t dat_int_enable; volatile uint32_t block_size; volatile uint32_t block_count; volatile uint32_t card_detect; volatile uint32_t res_50; volatile uint32_t res_54; volatile uint32_t res_58; volatile uint32_t res_5c; volatile uint64_t dma_addres; }; static struct sdc_regs * const regs __attribute__((section(".rodata"))) = (struct sdc_regs *)0x00013100; static int errno __attribute__((section(".bss"))); static DSTATUS drv_status __attribute__((section(".bss"))); static BYTE card_type __attribute__((section(".bss"))); static uint32_t response[4] __attribute__((section(".bss"))); static int alt_mem __attribute__((section(".bss"))); static const char * errno_to_str(void) { switch (errno) { case FR_OK: return "No error"; case FR_DISK_ERR: return "Disk I/O error"; case FR_INT_ERR: return "Assertion failed"; case FR_NOT_READY: return "Disk not ready"; case FR_NO_FILE: return "File not found"; case FR_NO_PATH: return "Path not found"; case FR_INVALID_NAME: return "Invalid path"; case FR_DENIED: return "Access denied"; case FR_EXIST: return "Already exist"; case FR_INVALID_OBJECT: return "The FS object is invalid"; case FR_WRITE_PROTECTED: return "The drive is write protected"; case FR_INVALID_DRIVE: return "The drive number is invalid"; case FR_NOT_ENABLED: return "The volume has no work area"; case FR_NO_FILESYSTEM: return "Not a valid FAT volume"; case FR_MKFS_ABORTED: return "The f_mkfs() aborted"; case FR_TIMEOUT: return "Timeout"; case FR_LOCKED: return "Locked"; case FR_NOT_ENOUGH_CORE: return "Not enough memory"; case FR_TOO_MANY_OPEN_FILES: return "Too many open files"; case ERR_EOF: return "Unexpected EOF"; case ERR_NOT_ELF: return "Not an ELF file"; case ERR_ELF_BITS: return "Wrong ELF word size"; case ERR_ELF_ENDIANNESS: return "Wrong ELF endianness"; case ERR_CMD_CRC: return "Command CRC error"; case ERR_CMD_CHECK: return "Command code check error"; case ERR_DATA_CRC: return "Data CRC error"; case ERR_DATA_FIFO: return "Data FIFO error"; case ERR_BUF_ALIGNMENT: return "Bad buffer alignment"; } return "Unknown error code"; } static void usleep(unsigned us) { uintptr_t cycles0; uintptr_t cycles1; asm volatile ("csrr %0, 0xB00" : "=r" (cycles0)); for (;;) { asm volatile ("csrr %0, 0xB00" : "=r" (cycles1)); if (cycles1 - cycles0 >= us * 100) break; } } static int sdc_cmd_finish(unsigned cmd) { while (1) { unsigned status = regs->cmd_int_status; if (status) { // clear interrupts regs->cmd_int_status = 0; while (regs->software_reset != 0) {} if (status == SDC_CMD_INT_STATUS_CC) { // get response response[0] = regs->response1; response[1] = regs->response2; response[2] = regs->response3; response[3] = regs->response4; return 0; } errno = FR_DISK_ERR; if (status & SDC_CMD_INT_STATUS_CTE) errno = FR_TIMEOUT; if (status & SDC_CMD_INT_STATUS_CCRC) errno = ERR_CMD_CRC; if (status & SDC_CMD_INT_STATUS_CIE) errno = ERR_CMD_CHECK; break; } } return -1; } static int sdc_data_finish(void) { int status; while ((status = regs->dat_int_status) == 0) {} regs->dat_int_status = 0; while (regs->software_reset != 0) {} if (status == SDC_DAT_INT_STATUS_TRS) return 0; errno = FR_DISK_ERR; if (status & SDC_DAT_INT_STATUS_CTE) errno = FR_TIMEOUT; if (status & SDC_DAT_INT_STATUS_CRC) errno = ERR_DATA_CRC; if (status & SDC_DAT_INT_STATUS_CFE) errno = ERR_DATA_FIFO; return -1; } static int send_data_cmd(unsigned cmd, unsigned arg, void * buf, unsigned blocks) { unsigned command = (cmd & 0x3f) << 8; switch (cmd) { case CMD0: case CMD4: case CMD15: // No responce break; case CMD11: case CMD13: case CMD16: case CMD17: case CMD18: case CMD19: case CMD23: case CMD24: case CMD25: case CMD27: case CMD30: case CMD32: case CMD33: case CMD42: case CMD55: case CMD56: case ACMD6: // R1 command |= 1; // 48 bits command |= 1 << 3; // resp CRC command |= 1 << 4; // resp OPCODE break; case CMD7: case CMD12: case CMD20: case CMD28: case CMD29: case CMD38: // R1b command |= 1; // 48 bits command |= 1 << 2; // busy command |= 1 << 3; // resp CRC command |= 1 << 4; // resp OPCODE break; case CMD2: case CMD9: case CMD10: // R2 command |= 2; // 136 bits command |= 1 << 3; // resp CRC break; case ACMD41: // R3 command |= 1; // 48 bits break; case CMD3: // R6 command |= 1; // 48 bits command |= 1 << 2; // busy command |= 1 << 3; // resp CRC command |= 1 << 4; // resp OPCODE break; case CMD8: // R7 command |= 1; // 48 bits command |= 1 << 3; // resp CRC command |= 1 << 4; // resp OPCODE break; } if (blocks) { command |= 1 << 5; if ((intptr_t)buf & 3) { errno = ERR_BUF_ALIGNMENT; return -1; } regs->dma_addres = (uint64_t)(intptr_t)buf; regs->block_size = 511; regs->block_count = blocks - 1; regs->data_timeout = 0xFFFFFF; } regs->command = command; regs->cmd_timeout = 0xFFFFF; regs->argument = arg; if (sdc_cmd_finish(cmd) < 0) return -1; if (blocks) return sdc_data_finish(); return 0; } #define send_cmd(cmd, arg) send_data_cmd(cmd, arg, NULL, 0) static int ini_sd(void) { unsigned rca; /* Reset controller */ regs->software_reset = 1; while ((regs->software_reset & 1) == 0) {} // This clock divider is meant to initialize the card at // 400kHz regs->clock_divider = 0x7c; regs->software_reset = 0; while (regs->software_reset) {} usleep(5000); card_type = 0; drv_status = STA_NOINIT; if (regs->capability & SDC_CAPABILITY_SD_RESET) { /* Power cycle SD card */ regs->control |= SDC_CONTROL_SD_RESET; usleep(1000000); regs->control &= ~SDC_CONTROL_SD_RESET; usleep(100000); } /* Enter Idle state */ send_cmd(CMD0, 0); card_type = CT_SD1; if (send_cmd(CMD8, 0x1AA) == 0) { if ((response[0] & 0xfff) != 0x1AA) { errno = ERR_CMD_CHECK; return -1; } card_type = CT_SD2; } /* Wait for leaving idle state (ACMD41 with HCS bit) */ while (1) { /* ACMD41, Set Operating Conditions: Host High Capacity & 3.3V */ if (send_cmd(CMD55, 0) < 0 || send_cmd(ACMD41, 0x40300000) < 0) return -1; if (response[0] & (1 << 31)) { if (response[0] & (1 << 30)) card_type |= CT_BLOCK; break; } } /* Enter Identification state */ if (send_cmd(CMD2, 0) < 0) return -1; /* Get RCA (Relative Card Address) */ rca = 0x1234; if (send_cmd(CMD3, rca << 16) < 0) return -1; rca = response[0] >> 16; /* Select card */ if (send_cmd(CMD7, rca << 16) < 0) return -1; /* Clock 25MHz */ regs->clock_divider = 3; usleep(10000); /* Bus width 1-bit */ regs->control = 0; if (send_cmd(CMD55, rca << 16) < 0 || send_cmd(ACMD6, 0) < 0) return -1; /* Set R/W block length to 512 */ if (send_cmd(CMD16, 512) < 0) return -1; drv_status &= ~STA_NOINIT; return 0; } int disk_read(BYTE * buf, LBA_t sector, UINT count) { /* This is not needed. This has everything to do with the FAT filesystem stuff that I'm not including. All I need to do is initialize the SD card and read from it. Anything in here that is checking for potential errors, I'm going to have to temporarily do without. */ // if (!count) return RES_PARERR; /* if (drv_status & STA_NOINIT) return RES_NOTRDY; */ /* Convert LBA to byte address if needed */ if (!(card_type & CT_BLOCK)) sector *= 512; while (count > 0) { UINT bcnt = count > MAX_BLOCK_CNT ? MAX_BLOCK_CNT : count; unsigned bytes = bcnt * 512; if (send_data_cmd(bcnt == 1 ? CMD17 : CMD18, sector, buf, bcnt) < 0) return RES_ERROR; if (bcnt > 1 && send_cmd(CMD12, 0) < 0) return RES_ERROR; sector += (card_type & CT_BLOCK) ? bcnt : bytes; count -= bcnt; buf += bytes; } return RES_OK; } void copyFlash(QWORD address, QWORD * Dst, DWORD numBlocks) { ini_sd(); BYTE * buf = (BYTE *)Dst; if (disk_read(buf, (LBA_t)address, (UINT)numBlocks) < 0) /* UART Print function?*/; } /* int main() { ini_sd(); return 0; } */