#include "io.h" #include "sc64.h" typedef struct { io32_t SR_CMD; io32_t DATA[2]; io32_t IDENTIFIER; io32_t KEY; } sc64_regs_t; #define SC64_REGS_BASE (0x1FFF0000UL) #define SC64_REGS ((sc64_regs_t *) SC64_REGS_BASE) #define SC64_SR_IRQ_PENDING (1 << 29) #define SC64_SR_CMD_ERROR (1 << 30) #define SC64_SR_CPU_BUSY (1 << 31) #define SC64_V2_IDENTIFIER (0x53437632) #define SC64_KEY_RESET (0x00000000UL) #define SC64_KEY_UNLOCK_1 (0x5F554E4CUL) #define SC64_KEY_UNLOCK_2 (0x4F434B5FUL) #define SC64_KEY_LOCK (0xFFFFFFFFUL) typedef enum { SC64_CMD_IDENTIFIER_GET = 'v', SC64_CMD_VERSION_GET = 'V', SC64_CMD_CONFIG_GET = 'c', SC64_CMD_CONFIG_SET = 'C', SC64_CMD_SETTING_GET = 'a', SC64_CMD_SETTING_SET = 'A', SC64_CMD_TIME_GET = 't', SC64_CMD_TIME_SET = 'T', SC64_CMD_USB_READ = 'm', SC64_CMD_USB_WRITE = 'M', SC64_CMD_USB_READ_STATUS = 'u', SC64_CMD_USB_WRITE_STATUS = 'U', SC64_CMD_SD_CARD_OP = 'i', SC64_CMD_SD_SECTOR_SET = 'I', SC64_CMD_SD_READ = 's', SC64_CMD_SD_WRITE = 'S', SC64_CMD_DD_SD_INFO = 'D', SC64_CMD_WRITEBACK_SD_INFO = 'W', SC64_CMD_FLASH_PROGRAM = 'K', SC64_CMD_FLASH_WAIT_BUSY = 'p', SC64_CMD_FLASH_ERASE_BLOCK = 'P', } cmd_id_t; typedef enum { SD_CARD_OP_DEINIT = 0, SD_CARD_OP_INIT = 1, SD_CARD_OP_GET_STATUS = 2, SD_CARD_OP_GET_INFO = 3, SD_CARD_OP_BYTE_SWAP_ON = 4, SD_CARD_OP_BYTE_SWAP_OFF = 5, } sd_card_op_t; static bool sc64_wait_cpu_busy (void) { uint32_t sr; do { sr = pi_io_read(&SC64_REGS->SR_CMD); } while (sr & SC64_SR_CPU_BUSY); return (sr & SC64_SR_CMD_ERROR); } static bool sc64_execute_cmd (uint8_t cmd, uint32_t *args, uint32_t *result) { if (args != NULL) { pi_io_write(&SC64_REGS->DATA[0], args[0]); pi_io_write(&SC64_REGS->DATA[1], args[1]); } pi_io_write(&SC64_REGS->SR_CMD, ((uint32_t) (cmd)) & 0xFF); bool error = sc64_wait_cpu_busy(); if (result != NULL) { result[0] = pi_io_read(&SC64_REGS->DATA[0]); result[1] = pi_io_read(&SC64_REGS->DATA[1]); } return error; } sc64_error_t sc64_get_error (void) { if (pi_io_read(&SC64_REGS->SR_CMD) & SC64_SR_CMD_ERROR) { return (sc64_error_t) (pi_io_read(&SC64_REGS->DATA[0])); } return SC64_OK; } void sc64_unlock (void) { pi_io_write(&SC64_REGS->KEY, SC64_KEY_RESET); pi_io_write(&SC64_REGS->KEY, SC64_KEY_UNLOCK_1); pi_io_write(&SC64_REGS->KEY, SC64_KEY_UNLOCK_2); } void sc64_lock (void) { pi_io_write(&SC64_REGS->KEY, SC64_KEY_RESET); pi_io_write(&SC64_REGS->KEY, SC64_KEY_LOCK); } bool sc64_check_presence (void) { uint32_t identifier = pi_io_read(&SC64_REGS->IDENTIFIER); if (identifier == SC64_V2_IDENTIFIER) { sc64_wait_cpu_busy(); return true; } return false; } bool sc64_irq_pending (void) { if (pi_io_read(&SC64_REGS->SR_CMD) & SC64_SR_IRQ_PENDING) { return true; } return false; } void sc64_irq_clear (void) { pi_io_write(&SC64_REGS->IDENTIFIER, 0); } uint32_t sc64_get_config (sc64_cfg_id_t id) { uint32_t args[2] = { id, 0 }; uint32_t result[2]; sc64_execute_cmd(SC64_CMD_CONFIG_GET, args, result); return result[1]; } void sc64_set_config (sc64_cfg_id_t id, uint32_t value) { uint32_t args[2] = { id, value }; sc64_execute_cmd(SC64_CMD_CONFIG_SET, args, NULL); } uint32_t sc64_get_setting (sc64_setting_id_t id) { uint32_t args[2] = { id, 0 }; uint32_t result[2]; sc64_execute_cmd(SC64_CMD_SETTING_GET, args, result); return result[1]; } void sc64_set_setting (sc64_setting_id_t id, uint32_t value) { uint32_t args[2] = { id, value }; sc64_execute_cmd(SC64_CMD_SETTING_SET, args, NULL); } void sc64_get_boot_info (sc64_boot_info_t *info) { info->boot_mode = (sc64_boot_mode_t) sc64_get_config(CFG_ID_BOOT_MODE); info->cic_seed = (sc64_cic_seed_t) sc64_get_config(CFG_ID_CIC_SEED); info->tv_type = (sc64_tv_type_t) sc64_get_config(CFG_ID_TV_TYPE); } void sc64_get_time (sc64_rtc_time_t *t) { uint32_t result[2]; sc64_execute_cmd(SC64_CMD_TIME_GET, NULL, result); t->second = (result[0] & 0xFF); t->minute = ((result[0] >> 8) & 0xFF); t->hour = ((result[0] >> 16) & 0xFF); t->weekday = ((result[1] >> 24) & 0xFF); t->day = (result[1] & 0xFF); t->month = ((result[1] >> 8) & 0xFF); t->year = ((result[1] >> 16) & 0xFF); } void sc64_set_time (sc64_rtc_time_t *t) { uint32_t args[2] = { ((t->hour << 16) | (t->minute << 8) | t->second), ((t->weekday << 24) | (t->year << 16) | (t->month << 8) | t->day), }; sc64_execute_cmd(SC64_CMD_TIME_SET, args, NULL); } bool sc64_usb_read_ready (uint8_t *type, uint32_t *length) { uint32_t result[2]; sc64_execute_cmd(SC64_CMD_USB_READ_STATUS, NULL, result); if (type != NULL) { *type = result[0] & 0xFF; } if (length != NULL) { *length = result[1]; } return (result[1] > 0); } bool sc64_usb_read (void *address, uint32_t length) { uint32_t args[2] = { (uint32_t) (address), length }; uint32_t result[2]; if (sc64_execute_cmd(SC64_CMD_USB_READ, args, NULL)) { return true; } do { sc64_execute_cmd(SC64_CMD_USB_READ_STATUS, NULL, result); } while(result[0] & (1 << 31)); return false; } bool sc64_usb_write_ready (void) { uint32_t result[2]; sc64_execute_cmd(SC64_CMD_USB_WRITE_STATUS, NULL, result); return (!(result[0] & (1 << 31))); } bool sc64_usb_write (void *address, uint8_t type, uint32_t length) { while (!sc64_usb_write_ready()); uint32_t args[2] = { (uint32_t) (address), ((type << 24) | (length & 0xFFFFFF)) }; return sc64_execute_cmd(SC64_CMD_USB_WRITE, args, NULL); } bool sc64_sd_card_init (void) { uint32_t args[2] = { (uint32_t) (NULL), SD_CARD_OP_INIT }; if (sc64_execute_cmd(SC64_CMD_SD_CARD_OP, args, NULL)) { return true; } return false; } bool sc64_sd_card_deinit (void) { uint32_t args[2] = { (uint32_t) (NULL), SD_CARD_OP_DEINIT }; if (sc64_execute_cmd(SC64_CMD_SD_CARD_OP, args, NULL)) { return true; } return false; } sc64_sd_card_status_t sc64_sd_card_get_status (void) { uint32_t args[2] = { (uint32_t) (NULL), SD_CARD_OP_GET_STATUS }; uint32_t result[2]; if (sc64_execute_cmd(SC64_CMD_SD_CARD_OP, args, result)) { return false; } return (sc64_sd_card_status_t) (result[1]); } bool sc64_sd_card_get_info (void *address) { uint32_t args[2] = { (uint32_t) (address), SD_CARD_OP_GET_INFO }; if (sc64_execute_cmd(SC64_CMD_SD_CARD_OP, args, NULL)) { return true; } return false; } bool sc64_sd_set_byte_swap (bool enabled) { uint32_t args[2] = { (uint32_t) (NULL), enabled ? SD_CARD_OP_BYTE_SWAP_ON : SD_CARD_OP_BYTE_SWAP_OFF }; if (sc64_execute_cmd(SC64_CMD_SD_CARD_OP, args, NULL)) { return true; } return false; } bool sc64_sd_read_sectors (void *address, uint32_t sector, uint32_t count) { uint32_t sector_set_args[2] = { sector, 0 }; uint32_t read_args[2] = { (uint32_t) (address), count }; if (sc64_execute_cmd(SC64_CMD_SD_SECTOR_SET, sector_set_args, NULL)) { return true; } return sc64_execute_cmd(SC64_CMD_SD_READ, read_args, NULL); } bool sc64_sd_write_sectors (void *address, uint32_t sector, uint32_t count) { uint32_t sector_set_args[2] = { sector, 0 }; uint32_t write_args[2] = { (uint32_t) (address), count }; if (sc64_execute_cmd(SC64_CMD_SD_SECTOR_SET, sector_set_args, NULL)) { return true; } return sc64_execute_cmd(SC64_CMD_SD_WRITE, write_args, NULL); } bool sc64_dd_set_sd_info (void *address, uint32_t length) { uint32_t args[2] = { (uint32_t) (address), length }; if (sc64_execute_cmd(SC64_CMD_DD_SD_INFO, args, NULL)) { return true; } return false; } bool sc64_writeback_enable (void *address) { uint32_t args[2] = { (uint32_t) (address), 0 }; if (sc64_execute_cmd(SC64_CMD_WRITEBACK_SD_INFO, args, NULL)) { return true; } return false; } bool sc64_flash_program (void *address, uint32_t length) { uint32_t args[2] = { (uint32_t) (address), length }; return sc64_execute_cmd(SC64_CMD_FLASH_PROGRAM, args, NULL); } void sc64_flash_wait_busy (void) { uint32_t args[2] = { true, 0 }; sc64_execute_cmd(SC64_CMD_FLASH_WAIT_BUSY, args, NULL); } uint32_t sc64_flash_get_erase_block_size (void) { uint32_t args[2] = { false, 0 }; uint32_t result[2]; sc64_execute_cmd(SC64_CMD_FLASH_WAIT_BUSY, args, result); return result[0]; } bool sc64_flash_erase_block (void *address) { uint32_t args[2] = { (uint32_t) (address), 0 }; return sc64_execute_cmd(SC64_CMD_FLASH_ERASE_BLOCK, args, NULL); }