cvw/tests/custom/boot/boot.c

413 lines
12 KiB
C
Raw Normal View History

#include "boot.h"
2023-01-10 17:19:28 +00:00
/* 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
2023-01-10 17:19:28 +00:00
#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
2023-01-10 17:19:28 +00:00
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
2023-01-10 17:19:28 +00:00
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) {
2023-01-27 20:35:34 +00:00
/* 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; */
2023-01-27 20:35:34 +00:00
/* 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();
2023-01-27 20:35:34 +00:00
BYTE * buf = (BYTE *)Dst;
if (disk_read(buf, (LBA_t)address, (UINT)numBlocks) < 0) /* UART Print function?*/;
2023-01-27 20:35:34 +00:00
}
/*
int main() {
ini_sd();
return 0;
}
*/