diff options
Diffstat (limited to 'fw/fe310/eos/sdcard.c')
-rw-r--r-- | fw/fe310/eos/sdcard.c | 322 |
1 files changed, 272 insertions, 50 deletions
diff --git a/fw/fe310/eos/sdcard.c b/fw/fe310/eos/sdcard.c index 4239707..970ab17 100644 --- a/fw/fe310/eos/sdcard.c +++ b/fw/fe310/eos/sdcard.c @@ -1,5 +1,6 @@ #include <stdlib.h> #include <stdint.h> +#include <stdio.h> #include "eos.h" #include "timer.h" @@ -7,14 +8,20 @@ #include "spi.h" #include "spi_dev.h" +#include "sdc_crypto.h" #include "sdcard.h" +#define SDC_TIMEOUT_CMD 500 +#define SDC_TIMEOUT_READ 200 +#define SDC_TIMEOUT_WRITE 500 + #define SDC_POLY_CRC7 0x09 #define SDC_POLY_CRC16 0x1021 #define SDC_CMD_FLAG_CRC 0x01 #define SDC_CMD_FLAG_NOCS 0x02 -#define SDC_CMD_FLAG_SKIP 0x08 +#define SDC_CMD_FLAG_NOWAIT 0x04 +#define SDC_CMD_FLAG_RSTUFF 0x08 #define SDC_TOKEN_START_BLK 0xfe #define SDC_TOKEN_START_BLKM 0xfc @@ -37,23 +44,32 @@ #define SDC_NCR 10 -#define SDC_TYPE_MMC 1 -#define SDC_TYPE_SDC1 2 -#define SDC_TYPE_SDC2 3 - #define SDC_ERR(rv) ((rv < 0) ? rv : EOS_ERR) /* CMD */ #define GO_IDLE_STATE 0 #define SEND_OP_COND 1 #define SEND_IF_COND 8 +#define SEND_CSD 9 +#define STOP_TRANSMISSION 12 #define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define READ_MULTIPLE_BLOCK 18 +#define WRITE_BLOCK 24 +#define WRITE_MULTIPLE_BLOCK 25 +#define ERASE_WR_BLK_START 32 +#define ERASE_WR_BLK_END 33 +#define ERASE 38 #define APP_CMD 55 #define READ_OCR 58 /* ACMD */ +#define SD_STATUS 13 +#define SET_WR_BLK_ERASE_COUNT 23 #define SD_APP_OP_COND 41 +static uint8_t sdc_type = 0; + static uint8_t sdc_crc7(uint8_t crc, uint8_t b) { int i; @@ -83,9 +99,6 @@ static uint32_t sdc_nto(uint64_t start, uint32_t timeout) { return (d > timeout) ? 0 : timeout - d; } -static void sdc_xchg(unsigned char *buffer, uint16_t len) { -} - static uint8_t sdc_xchg8(uint8_t data) { return eos_spi_xchg8(data, 0); } @@ -98,6 +111,22 @@ static uint32_t sdc_xchg32(uint32_t data) { return eos_spi_xchg32(data, 0); } +static void sdc_buf_send(unsigned char *buffer, uint16_t len) { + int i; + + for (i=0; i<len; i++) { + sdc_xchg8(buffer[i]); + } +} + +static void sdc_buf_recv(unsigned char *buffer, uint16_t len) { + int i; + + for (i=0; i<len; i++) { + buffer[i] = sdc_xchg8(0xff); + } +} + static void sdc_select(void) { eos_spi_cs_set(); eos_spi_xchg8(0xff, 0); @@ -125,7 +154,7 @@ static int sdc_xchg_cmd(uint8_t cmd, uint32_t arg, uint8_t flags) { sdc_xchg8(cmd); sdc_xchg32(arg); sdc_xchg8(crc); - if (flags & SDC_CMD_FLAG_SKIP) sdc_xchg8(0xff); + if (flags & SDC_CMD_FLAG_RSTUFF) sdc_xchg8(0xff); i = SDC_NCR; do { @@ -142,11 +171,11 @@ static int sdc_ready(uint32_t timeout) { if (timeout == 0) return EOS_ERR_BUSY; start = eos_time_get_tick(); - do { + do { if (eos_time_since(start) > timeout) break; - d = sdc_xchg8(0xff); - } while (d != 0xff); - if (d != 0xff) return EOS_ERR_BUSY; + d = sdc_xchg8(0xff); + } while (d != 0xff); + if (d != 0xff) return EOS_ERR_BUSY; return EOS_OK; } @@ -157,15 +186,15 @@ static int sdc_block_read(uint8_t *buffer, uint16_t len, uint32_t timeout) { if (timeout == 0) return EOS_ERR_BUSY; start = eos_time_get_tick(); - do { + do { if (eos_time_since(start) > timeout) break; - token = sdc_xchg8(0xff); - } while (token == 0xff); + token = sdc_xchg8(0xff); + } while (token == 0xff); if (token == 0xff) return EOS_ERR_BUSY; - if (token != SDC_TOKEN_START_BLK) return EOS_ERR; + if (token != SDC_TOKEN_START_BLK) return EOS_ERR; - sdc_xchg(buffer, len); - sdc_xchg16(0xff); /* dummy CRC */ + sdc_buf_recv(buffer, len); + sdc_xchg16(0xffff); /* dummy CRC */ return EOS_OK; } @@ -179,8 +208,8 @@ static int sdc_block_write(uint8_t token, uint8_t *buffer, uint16_t len, uint32_ sdc_xchg8(token); if (buffer && len) { - sdc_xchg(buffer, len); - sdc_xchg16(0xff); /* dummy CRC */ + sdc_buf_send(buffer, len); + sdc_xchg16(0xffff); /* dummy CRC */ d = sdc_xchg8(0xff); /* Response */ if ((d & SDC_DRESP_MASK) != SDC_DRESP_ACCEPT) return EOS_ERR; @@ -191,10 +220,11 @@ static int sdc_block_write(uint8_t token, uint8_t *buffer, uint16_t len, uint32_ static int sdc_cmd(uint8_t cmd, uint32_t arg, uint8_t flags, uint32_t timeout) { int do_cs = !(flags & SDC_CMD_FLAG_NOCS); - int rv; + int do_wait = !(flags & SDC_CMD_FLAG_NOWAIT); + int rv = EOS_OK; if (do_cs) sdc_select(); - rv = sdc_ready(timeout); + if (do_wait) rv = sdc_ready(timeout); if (rv) { if (do_cs) sdc_deselect(); return rv; @@ -208,40 +238,39 @@ static int sdc_acmd(uint8_t cmd, uint32_t arg, uint8_t flags, uint32_t timeout) int rv; uint64_t start; - if (flags & SDC_CMD_FLAG_NOCS) return EOS_ERR; - start = eos_time_get_tick(); - rv = sdc_cmd(APP_CMD, 0, flags & SDC_CMD_FLAG_CRC, timeout); + rv = sdc_cmd(APP_CMD, 0, flags, timeout); if (rv & ~SDC_R1_IDLE_STATE) return rv; + if (flags & SDC_CMD_FLAG_NOCS) { + sdc_deselect(); + sdc_select(); + } return sdc_cmd(cmd, arg, flags, sdc_nto(start, timeout)); } -static int sdc_init(uint32_t timeout, uint8_t *type, uint8_t *lba) { - uint8_t _type, _lba; +static int sdc_init(uint32_t timeout) { int rv, i; + uint8_t _type; uint8_t ocr[4]; uint64_t start; - - _type = 0; - _lba = 0; - - eos_time_sleep(10); start = eos_time_get_tick(); + + eos_time_sleep(100); for (i=10; i--;) sdc_xchg8(0xff); /* 80 dummy cycles */ - rv = sdc_cmd(GO_IDLE_STATE, 0, SDC_CMD_FLAG_CRC, timeout); + rv = sdc_cmd(GO_IDLE_STATE, 0, SDC_CMD_FLAG_CRC, SDC_TIMEOUT_CMD); if (rv != SDC_R1_IDLE_STATE) return SDC_ERR(rv); sdc_select(); rv = sdc_cmd(SEND_IF_COND, 0x1aa, SDC_CMD_FLAG_CRC | SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout)); switch (rv) { case SDC_R1_IDLE_STATE: - for (i=0; i<4; i++) { - ocr[i] = sdc_xchg8(0xff); /* get R7 resp */ + for (i=0; i<4; i++) { /* R7 response */ + ocr[i] = sdc_xchg8(0xff); } sdc_deselect(); - if (ocr[2] == 0x01 && ocr[3] == 0xaa) { /* Check voltage range: 2.7-3.6V */ + if (ocr[2] == 0x01 && ocr[3] == 0xaa) { /* Check voltage range: 2.7-3.6V */ do { rv = sdc_acmd(SD_APP_OP_COND, 0x40000000, 0, sdc_nto(start, timeout)); } while (rv == SDC_R1_IDLE_STATE); @@ -250,7 +279,7 @@ static int sdc_init(uint32_t timeout, uint8_t *type, uint8_t *lba) { sdc_select(); rv = sdc_cmd(READ_OCR, 0, SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout)); if (rv == SDC_R1_READY) { - for (i=0; i<4; i++) { + for (i=0; i<4; i++) { /* R7 response */ ocr[i] = sdc_xchg8(0xff); } sdc_deselect(); @@ -259,8 +288,8 @@ static int sdc_init(uint32_t timeout, uint8_t *type, uint8_t *lba) { return SDC_ERR(rv); } - _type = SDC_TYPE_SDC2; - if (ocr[0] & 0xc0) _lba = 1; + _type = EOS_SDC_TYPE_SDC2; + if (ocr[0] & 0xc0) _type |= EOS_SDC_CAP_BLK; } break; @@ -274,7 +303,7 @@ static int sdc_init(uint32_t timeout, uint8_t *type, uint8_t *lba) { } while (rv == SDC_R1_IDLE_STATE); if (rv != SDC_R1_READY) return SDC_ERR(rv); case SDC_R1_READY: - _type = SDC_TYPE_SDC1; + _type = EOS_SDC_TYPE_SDC1; break; default: @@ -282,7 +311,7 @@ static int sdc_init(uint32_t timeout, uint8_t *type, uint8_t *lba) { rv = sdc_cmd(SEND_OP_COND, 0, 0, sdc_nto(start, timeout)); } while (rv == SDC_R1_IDLE_STATE); if (rv != SDC_R1_READY) return SDC_ERR(rv); - _type = SDC_TYPE_MMC; + _type = EOS_SDC_TYPE_MMC; break; } @@ -293,27 +322,220 @@ static int sdc_init(uint32_t timeout, uint8_t *type, uint8_t *lba) { return SDC_ERR(rv); } - if (_type && !_lba) { + if (!(_type & EOS_SDC_CAP_BLK)) { rv = sdc_cmd(SET_BLOCKLEN, 512, 0, sdc_nto(start, timeout)); if (rv != SDC_R1_READY) return SDC_ERR(rv); } - *type = _type; - *lba = _lba; + + if (_type & EOS_SDC_TYPE_SDC) { + uint8_t csd[16]; + + sdc_select(); + rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout)); + if (rv == SDC_R1_READY) { + rv = sdc_block_read(csd, 16, sdc_nto(start, timeout)); + } else { + rv = SDC_ERR(rv); + } + sdc_deselect(); + if (rv) return rv; + + for (i=0; i<16; i++) { + printf("%.2x ", csd[i]); + } + printf("\n"); + if (csd[10] & 0x40) _type |= EOS_SDC_CAP_ERASE_EN; + } + + eos_spi_set_div(EOS_SPI_DEV_SDC, 5); + sdc_type = _type; return EOS_OK; } -#include <stdio.h> - void eos_sdc_init(void) { - uint8_t type, lba; int rv; eos_spi_select(EOS_SPI_DEV_SDC); - rv = sdc_init(5000, &type, &lba); + rv = sdc_init(5000); if (rv) { printf("SDC ERROR\n"); } else { - printf("SDC OK:%d %d\n", type, lba); + printf("SDC OK:%x\n", sdc_type); } eos_spi_deselect(); } + +uint8_t eos_sdc_type(void) { + return sdc_type & EOS_SDC_TYPE_MASK; +} + +uint8_t eos_sdc_cap(void) { + return sdc_type & EOS_SDC_CAP_MASK; +} + +int eos_sdc_get_sect_count(uint32_t timeout, uint32_t *sectors) { + int rv; + uint8_t csd[16]; + uint64_t start = eos_time_get_tick(); + + sdc_select(); + rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, timeout); + if (rv == SDC_R1_READY) { + rv = sdc_block_read(csd, 16, sdc_nto(start, timeout)); + } else { + rv = SDC_ERR(rv); + } + sdc_deselect(); + if (rv) return rv; + + if ((csd[0] >> 6) == 1) { /* SDCv2 */ + *sectors = (csd[9] + (csd[8] << 8) + ((csd[7] & 0x3f) << 16) + 1) << 10; + } else { /* SDCv1 or MMC*/ + uint8_t n = (csd[5] & 0x0f) + (csd[10] >> 7) + ((csd[9] & 0x03) << 1) + 2; + *sectors = (csd[8] >> 6) + (csd[7] << 2) + ((csd[6] & 0x03) << 10) + 1; + *sectors = *sectors << (n - 9); + } + + return EOS_OK; +} + +int eos_sdc_get_blk_size(uint32_t timeout, uint32_t *size) { + int rv; + uint8_t rbl[64]; /* SD Status or CSD register */ + uint64_t start = eos_time_get_tick(); + + sdc_select(); + if (sdc_type & EOS_SDC_TYPE_SDC2) { + rv = sdc_acmd(SD_STATUS, 0, SDC_CMD_FLAG_NOCS, timeout); + sdc_xchg8(0xff); /* R2 response */ + } else { + rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, timeout); + } + if (rv == SDC_R1_READY) { + rv = sdc_block_read(rbl, (sdc_type & EOS_SDC_TYPE_SDC2) ? 64 : 16, sdc_nto(start, timeout)); + } else { + rv = SDC_ERR(rv); + } + sdc_deselect(); + if (rv) return rv; + + if (sdc_type & EOS_SDC_TYPE_SDC2) { + *size = 16UL << (rbl[10] >> 4); + } else if (sdc_type & EOS_SDC_TYPE_SDC1) { + *size = (((rbl[10] & 0x3f) << 1) + (rbl[11] >> 7) + 1) << ((rbl[13] >> 6) - 1); + } else { + *size = (((rbl[10] & 0x7c) >> 2) + 1) * (((rbl[11] & 0x03) << 3) + (rbl[11] >> 5) + 1); + } + + return EOS_OK; +} + +int eos_sdc_sync(uint32_t timeout) { + int rv; + + sdc_select(); + rv = sdc_ready(timeout); + sdc_deselect(); + + return rv; +} + +int eos_sdc_erase(uint32_t blk_start, uint32_t blk_end, uint32_t timeout) { + int rv; + uint64_t start; + + if (!(sdc_type & EOS_SDC_TYPE_SDC)) return EOS_ERR; + if (!(sdc_type & EOS_SDC_CAP_ERASE_EN)) return EOS_ERR; + if (!(sdc_type & EOS_SDC_CAP_BLK)) { + blk_start *= 512; + blk_end *= 512; + } + + start = eos_time_get_tick(); + rv = sdc_cmd(ERASE_WR_BLK_START, blk_start, 0, timeout); + if (rv != SDC_R1_READY) return SDC_ERR(rv); + + rv = sdc_cmd(ERASE_WR_BLK_END, blk_end, 0, sdc_nto(start, timeout)); + if (rv != SDC_R1_READY) return SDC_ERR(rv); + + rv = sdc_cmd(ERASE, 0, 0, sdc_nto(start, timeout)); + if (rv != SDC_R1_READY) return SDC_ERR(rv); + + return eos_sdc_sync(sdc_nto(start, timeout)); +} + +int eos_sdc_sect_read(uint32_t sect, unsigned int count, uint8_t *buffer) { + int rv; + uint64_t start; + uint8_t cmd = ((count == 1) ? READ_SINGLE_BLOCK : READ_MULTIPLE_BLOCK); + + if (!(sdc_type & EOS_SDC_CAP_BLK)) sect *= 512; + start = eos_time_get_tick(); + + sdc_select(); + rv = sdc_cmd(cmd, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD); + if (rv == SDC_R1_READY) { + int _rv = SDC_R1_READY; + + while (count) { + rv = sdc_block_read(buffer, 512, SDC_TIMEOUT_READ); + if (rv) break; + eos_sdcc_decrypt(sect, buffer); + buffer += 512; + count--; + } + if (cmd == READ_MULTIPLE_BLOCK) _rv = sdc_cmd(STOP_TRANSMISSION, 0, SDC_CMD_FLAG_NOCS | SDC_CMD_FLAG_NOWAIT | SDC_CMD_FLAG_RSTUFF, 0); + if (!rv && (_rv != SDC_R1_READY)) rv = SDC_ERR(_rv); + } else { + rv = SDC_ERR(rv); + } + sdc_deselect(); + + return rv; +} + +int eos_sdc_sect_write(uint32_t sect, unsigned int count, uint8_t *buffer) { + int rv; + uint64_t start; + + if (!(sdc_type & EOS_SDC_CAP_BLK)) sect *= 512; + start = eos_time_get_tick(); + + if (count == 1) { + sdc_select(); + rv = sdc_cmd(WRITE_BLOCK, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD); + if (rv == SDC_R1_READY) { + eos_sdcc_encrypt(sect, buffer); + rv = sdc_block_write(SDC_TOKEN_START_BLK, buffer, 512, SDC_TIMEOUT_WRITE); + } else { + rv = SDC_ERR(rv); + } + sdc_deselect(); + } else { + if (sdc_type & EOS_SDC_TYPE_SDC) { + rv = sdc_acmd(SET_WR_BLK_ERASE_COUNT, count, 0, SDC_TIMEOUT_CMD); + if (rv != SDC_R1_READY) return SDC_ERR(rv); + } + + sdc_select(); + rv = sdc_cmd(WRITE_MULTIPLE_BLOCK, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD); + if (rv == SDC_R1_READY) { + int _rv; + + while (count) { + eos_sdcc_encrypt(sect, buffer); + rv = sdc_block_write(SDC_TOKEN_START_BLKM, buffer, 512, SDC_TIMEOUT_WRITE); + if (rv) break; + buffer += 512; + count--; + } + _rv = sdc_block_write(SDC_TOKEN_STOP_TRAN, NULL, 0, SDC_TIMEOUT_WRITE); + if (!rv && (_rv != SDC_R1_READY)) rv = SDC_ERR(_rv); + } else { + rv = SDC_ERR(rv); + } + sdc_deselect(); + } + + return rv; +} |