diff --git a/cpu/src/kernel/device/dev_sdcard.c b/cpu/src/kernel/device/dev_sdcard.c new file mode 100644 index 00000000..8c97f2fb --- /dev/null +++ b/cpu/src/kernel/device/dev_sdcard.c @@ -0,0 +1,693 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file per_mmcsd.c. + * + * @brief Configuration and handling of MMC/SD controller peripheral. + */ + +/*----- Includes -----------------------------------------------------*/ + +#include "macros.h" + +#include "sd_protocol.h" +#include "dev_sdcard.h" +#include "per_mmcsd.h" + +/*----- Macros -------------------------------------------------------*/ + +#ifdef DEBUG_SDDRIVER +# define DEBUG_LOG_SD(fmt, ...) DEBUG_LOG(fmt, ##__VA_ARGS__) +#else +# define DEBUG_LOG_SD(...) +#endif + +#define MMCSDCON MMCSD0 + +#define MMCSD_CLK 10,0,2 +#define MMCSD_CMD 10,4,2 +#define MMCSD_DAT0 10,8,2 +#define MMCSD_DAT1 10,12,2 +#define MMCSD_DAT2 10,16,2 +#define MMCSD_DAT3 10,20,2 +#define MMCSD_WP 10,24,2 +#define MMCSD_INS 10,28,2 + +#define BUS_POWER_VOLTAGE 3300 //mV + +#define LOW_CLK 400000 //Hz + +#define MMC_RCA 0x1234 + +#define SDMMC_REG_RETRY 100000 +#define SDMMC_CMD_RETRY 20 +#define SDMMC_ARG_NULL 0 + +/*----- Typedefs -----------------------------------------------------*/ + +typedef struct { + uint16_t rca; + uint8_t is_mmc:1; + uint8_t is_hc:1; + uint8_t is_bus4bit:1; + sdp_cur_stat_t ci_stat; + sdp_r1_stat_t r1_stat; + CID_t cid; + CSD_t csd; +} sd_sm_t; + +typedef struct { + int rt; + const char* estr; +} sdmmc_estr_t; + +/*----- Static variable definitions ----------------------------------*/ + +sd_sm_t sd_sm[1]; + +#define RT_ESTR_PAIR(X) { X, #X } + +sdmmc_estr_t sdmmc_estr[] = { + RT_ESTR_PAIR(SDCARD_OK), + RT_ESTR_PAIR(SDCARD_NO_RESPONSE), + RT_ESTR_PAIR(SDCARD_CRC_ERROR), + RT_ESTR_PAIR(SDCARD_UNSUPPORTED), + RT_ESTR_PAIR(SDCARD_ERROR), +}; + +/*----- Extern variable definitions ----------------------------------*/ + +/*----- Static function prototypes -----------------------------------*/ + +static int _sdmmc_inf_init(void); +static inline uint32_t _sdmmc_resp(void); +static t_sdcard_status _sdmmc_print_r1(void); +static t_sdcard_status _sdmmc_get_cid(void); +static uint32_t _sdmmc_cmd_stat(int nr); +static t_sdcard_status _sdmmc_cmd(int nr, uint32_t arg); +static t_sdcard_status _sdmmc_acmd(int nr, uint32_t arg); +static inline t_sdcard_status _sdmmc_cmd_noarg(int nr); +static t_sdcard_status _ACMD41(void); +static t_sdcard_status _CMD1(void); + +// Protocol +static t_sdcard_status _sdmmc_card_init(void); +static t_sdcard_status _sdmmc_get_csd(void); +static t_sdcard_status _sdmmc_speed_up(void); +static t_sdcard_status _sdmmc_get_classes(void); +static t_sdcard_status _sdmmc_sel_card(bool sel); +static t_sdcard_status _sdmmc_max_buswidth(void); + +/*----- Extern function implementations ------------------------------*/ + +const char* dev_sdcard_err_string(int rt) { + return sdmmc_estr[-rt].estr; +} + +t_sdcard_status dev_sdcard_read(uint32_t blk_nr, uint32_t blk_cnt, uint32_t* buf) { + + t_mmcsd_misc misc; + misc.blkcnt = blk_cnt; + misc.mflags = MMCSD_MISC_F_READ | MMCSD_MISC_F_FIFO_RST | MMCSD_MISC_F_FIFO_32B; + mmcsd_cntl_misc(MMCSDCON, &misc); + + int cmd_nr = CMD17R1_READ_SINGLE_BLOCK; + if (blk_cnt > 1) cmd_nr = CMD18R1_READ_MULTIPLE_BLOCK; + + uint32_t arg = (sd_sm->is_hc) ? (blk_nr) : (blk_nr << MASK_OFFSET(MMCSD_BLOCK_SIZE)); + t_sdcard_status r = _sdmmc_cmd(cmd_nr, arg); + if (r != SDCARD_OK) return r; + + t_mmcsd_dat_state ds; + int i = 0; + while (i < blk_cnt * MMCSD_BLOCK_SIZE / sizeof(uint32_t)) { + if ((ds = mmcsd_rd_state(MMCSDCON)) == MMCSD_SD_OK) { + break; + } + if (ds == MMCSD_SD_TOUT) { + return SDCARD_NO_RESPONSE; + } + if (ds == MMCSD_SD_CRC_ERR) { + return SDCARD_CRC_ERROR; + } + if (i == 0 || ds == MMCSD_SD_RECVED) { + int ii = 0; + + for (ii = 0; ii < 32 / sizeof(uint32_t); ii++) { + buf[i++] = mmcsd_read(MMCSDCON); + } + } + // DEBUG_LOG_SD(" *** ST1=0x%.8X ***\n", MMCSDCON->MMCST1); + } + + while ((ds = mmcsd_rd_state(MMCSDCON)) != MMCSD_SD_OK); + + DEBUG_LOG_SD("SDMMC READ %d bytes\n", i * sizeof(uint32_t)); + + if (blk_cnt > 1) { + r = _sdmmc_cmd_noarg(CMD12R1b_STOP_TRANSMISSION); + if (r != SDCARD_OK) return r; + } else { + sd_sm->ci_stat = SDP_TRAN; + } + + return SDCARD_OK; +} + +t_sdcard_status dev_sdcard_write(uint32_t blk_nr, uint32_t blk_cnt, const uint32_t* buf) { + t_mmcsd_dat_state ds; + t_mmcsd_misc misc; + t_sdcard_status r; + int i, ii; + + misc.blkcnt = blk_cnt; + misc.mflags = MMCSD_MISC_F_WRITE | MMCSD_MISC_F_FIFO_RST | MMCSD_MISC_F_FIFO_32B; + mmcsd_cntl_misc(MMCSDCON, &misc); + + int cmd_nr = CMD24R1_WRITE_BLOCK; + if (blk_cnt > 1) cmd_nr = CMD25R1_WRITE_MULTIPLE_BLOCK; + + for (i = 0; i < 32 / sizeof(uint32_t);) { + mmcsd_write(MMCSDCON, buf[i++]); + } + + uint32_t arg = (sd_sm->is_hc) ? (blk_nr) : (blk_nr << MASK_OFFSET(MMCSD_BLOCK_SIZE)); + r = _sdmmc_cmd(cmd_nr, arg); + if (r != SDCARD_OK) return r; + + while (i < blk_cnt * MMCSD_BLOCK_SIZE / sizeof(uint32_t)) { + if ((ds = mmcsd_wr_state(MMCSDCON)) == MMCSD_SD_OK) { + break; + } + if (ds == MMCSD_SD_TOUT) { + return SDCARD_NO_RESPONSE; + } + // DEBUG_LOG_SD(" *** ST1=0x%.8X ***\n", MMCSDCON->MMCST1); + // if (/* i == 0 || */ds == MMCSD_SD_SENT) { + // lost the DXRDY status bit in the mmcsd_wr_state() call within sdmmc_cmd() + if (i == 32 / sizeof(uint32_t) || ds == MMCSD_SD_SENT) { + for (ii = 0; ii < 32 / sizeof(uint32_t); ii++) { + mmcsd_write(MMCSDCON, buf[i++]); + } + } + } + + /* DEBUG_LOG_SD("Wait write complete!\n"); + for (ii = 0; ii < 8; ii++) { + mmcsd_write(MMCSDCON, 0x0UL); // dummy writes + } + while ((ds = mmcsd_wr_state(MMCSDCON)) != MMCSD_SD_OK) { + DEBUG_LOG_SD(" *** ST1=0x%.8X ***\n", MMCSDCON->MMCST1); + } */ + + DEBUG_LOG_SD("SDMMC WRITE %d bytes\n", i * sizeof(uint32_t)); + + if (blk_cnt <= 1) { + goto done; + } + + r = _sdmmc_cmd_noarg(CMD12R1b_STOP_TRANSMISSION); + if (r != SDCARD_OK) return r; + + do { + ds = mmcsd_busy_state(MMCSDCON); + //DEBUG_LOG_SD(" *** ST0=0x%.8X RSP=0x%.8X ***\n", MMCSDCON->MMCST0, MMCSDCON->MMCRSP[3]); + } while (ds == MMCSD_SD_BUSY); + +done: + sd_sm->ci_stat = SDP_TRAN; + + return SDCARD_OK; +} + +t_sdcard_status dev_sdcard_init(void) { + + t_sdcard_status r; + + _sdmmc_inf_init(); + + r = _sdmmc_card_init(); + if (r != SDCARD_OK) { + DEBUG_LOG_SD("SDMMC card init %s\n", dev_sdcard_err_string(r)); + return r; + } + + r = _sdmmc_get_csd(); + if (r != SDCARD_OK) { + DEBUG_LOG_SD("SDMMC get csd %s\n", dev_sdcard_err_string(r)); + return r; + } + + r = _sdmmc_speed_up(); + r = _sdmmc_get_classes(); + + r = _sdmmc_sel_card(AM18X_TRUE); + if (r != SDCARD_OK) { + DEBUG_LOG_SD("SDMMC sel card %s\n", dev_sdcard_err_string(r)); + return r; + } + + r = _sdmmc_max_buswidth(); + DEBUG_LOG_SD("SDMMC bus width = %dBIT %s\n", (sd_sm->is_bus4bit? 4: 1), dev_sdcard_err_string(r)); + + return r; +} + + + +/*----- Static function implementations ------------------------------*/ + +static int _sdmmc_inf_init(void) { + mmcsd_conf_t conf[1]; + uint32_t freq; + + conf->freq = LOW_CLK; + conf->timeout_rsp = TIMEOUT_RSP_MAX; + conf->timeout_dat = TIMEOUT_DAT_MAX; + mmcsd_con_init(MMCSDCON, conf); + + mmcsd_set_freq(MMCSDCON, LOW_CLK); + + return 0; +} + +static inline uint32_t _sdmmc_resp(void) { + t_mmcsd_resp resp; + + mmcsd_get_resp(MMCSDCON, MMCSD_RESP_SHORT, &resp); + return resp.v[0]; +} + +static t_sdcard_status _sdmmc_print_r1(void) { + union { + uint32_t i; + sdp_r1_stat_t r1_stat; + }u; + + u.i = _sdmmc_resp(); + sd_sm->r1_stat = u.r1_stat; + + sdprot_print_r1_stat(&sd_sm->r1_stat); + + return SDCARD_OK; +} + +static t_sdcard_status _sdmmc_get_cid(void) { + t_mmcsd_resp resp; + + mmcsd_get_resp(MMCSDCON, MMCSD_RESP_LONG, &resp); + + sdprot_get_cid(&sd_sm->cid, resp.v); + + return SDCARD_OK; +} + +static uint32_t _sdmmc_cmd_stat(int nr) { + uint32_t stat; + + if (sdprot_resp_crc(nr) == 0) { + stat = mmcsd_cmd_state(MMCSDCON, AM18X_FALSE); + } else { + stat = mmcsd_cmd_state(MMCSDCON, AM18X_TRUE); + } + return stat; +} + +static t_sdcard_status _sdmmc_cmd(int nr, uint32_t arg) { + sdcard_response_t srt; + t_mmcsd_cmd cmd; + uint32_t stat; + t_sdcard_status r; + int i; + + r = SDCARD_OK; + cmd.index = nr; + cmd.arg = arg; + + if (sdprot_next_stat(nr, sd_sm->ci_stat) == SDP_INV) { + DEBUG_LOG_SD("SDPROT\tCurrent State %s with CMD%d\n", + sdprot_stat_name(sd_sm->ci_stat), nr); + // r = SDCARD_UNSUPPORTED; + // goto done; + } + + cmd.cflags = 0; + switch(srt = sdprot_resp_type(nr)) { + case SDCARD_48BIT_RSP: + cmd.cflags |= MMCSD_CMD_F_RSP | MMCSD_CMD_F_SHORT; + if (sdprot_resp_crc(nr)) { + cmd.cflags |= MMCSD_CMD_F_CRC; + } + break; + case SDCARD_136BIT_RSP: + cmd.cflags |= MMCSD_CMD_F_RSP | MMCSD_CMD_F_LONG; + break; + case SDCARD_NONE_RSP: + default: + cmd.cflags |= MMCSD_CMD_F_NORSP; + break; + } + if (sdprot_need_data(nr) != SDPROT_NO_DATA) { + cmd.cflags |= MMCSD_CMD_F_DATA; + if (sdprot_need_data(nr) == SDPROT_READ_DATA) { + cmd.cflags |= MMCSD_CMD_F_READ; + } else { + cmd.cflags |= MMCSD_CMD_F_WRITE; + // am1808 bug fix + // cmd.cflags &= ~(MMCSD_CMD_F_RSP | MMCSD_CMD_F_LONG); + } + } + + if (sdprot_need_busy(nr)) { + cmd.cflags |= MMCSD_CMD_F_BUSY; + } + + mmcsd_send_cmd(MMCSDCON, &cmd); + + for(i = 0;;) { + stat = _sdmmc_cmd_stat(nr); + if (stat == MMCSD_SC_RSP_TOUT || stat == MMCSD_SC_RSP_OK || stat == MMCSD_SC_CRC_ERR) { + break; + } + if (i++ > SDMMC_REG_RETRY) { + DEBUG_LOG_SD("%s() *** error stat = %d ***\n", __func__, stat); + r = SDCARD_NO_RESPONSE; + goto done; + } + } + if (stat != MMCSD_SC_RSP_OK) { + DEBUG_LOG_SD("*** MMCCMD=0x%.8X ARG=0x%.8X ***", MMCSDCON->MMCCMD, MMCSDCON->MMCARGHL); + DEBUG_LOG_SD(" *** ST0=0x%.8X RSP=0x%.8X ***\n", MMCSDCON->MMCST0, MMCSDCON->MMCRSP[3]); + } + if (stat == MMCSD_SC_CRC_ERR) { + r = SDCARD_CRC_ERROR; + goto done; + } + if (stat == MMCSD_SC_RSP_TOUT) { + r = SDCARD_NO_RESPONSE; + goto done; + } + DEBUG_LOG_SD("SDPROT\tCMD%d OK\n", nr); + + stat = sdprot_next_stat(nr, sd_sm->ci_stat); + if (stat != sd_sm->ci_stat) { + DEBUG_LOG_SD("SDPROT\tTransition from %s to %s\n", + sdprot_stat_name(sd_sm->ci_stat), + sdprot_stat_name(stat)); + sd_sm->ci_stat = stat; + } + +done: + return r; +} + +static t_sdcard_status _sdmmc_acmd(int nr, uint32_t arg) { + t_sdcard_status r; + + r = _sdmmc_cmd(CMD55R1_APP_CMD, sd_sm->rca << 16); + if (r != SDCARD_OK) { + DEBUG_LOG_SD("SDMMC status = 0x%.8X\n", _sdmmc_resp()); + return r; + } + + //_sdmmc_print_r1(); + + return _sdmmc_cmd(nr, arg); +} + +static inline t_sdcard_status _sdmmc_cmd_noarg(int nr) { + return _sdmmc_cmd(nr, SDMMC_ARG_NULL); +} + +static t_sdcard_status _ACMD41(void) { + t_sdcard_status r; + uint32_t ocr; + + ocr = OCR_VOLTAGE_WINDOW(BUS_POWER_VOLTAGE); + do { + // 30 HCS(OCR[30]) + // 23:0 Vdd Voltage Window(OCR[23:0]) + r = _sdmmc_acmd(ACMD41R3_SD_SEND_OP_COND, (ocr | OCR_CCS) & ~OCR_PowerUpEnd); + if (r != SDCARD_OK) { + DEBUG_LOG_SD("SDPROT\tNot SD Memory Card\n"); + return SDCARD_UNSUPPORTED; + } + ocr = _sdmmc_resp(); + if (0 == (ocr & OCR_VOLTAGE_WINDOW(BUS_POWER_VOLTAGE))) { + return SDCARD_UNSUPPORTED; + } + if (ocr & OCR_PowerUpEnd) break; + + DEBUG_LOG_SD("SDPROT\tcard returns busy or host omitted voltage range\n"); + DEBUG_LOG_SD("%s() ocr = 0x%.8X\n", __func__, ocr); + + sd_sm->ci_stat = SDP_IDLE; + } while (AM18X_TRUE); + + DEBUG_LOG_SD("%s() ocr = 0x%.8X\n", __func__, ocr); + + if (ocr & OCR_CCS) { + sd_sm->is_hc = AM18X_TRUE; + } else { + sd_sm->is_hc = AM18X_FALSE; + } + + return SDCARD_OK; +} + +static t_sdcard_status _CMD1(void) { + t_sdcard_status r; + uint32_t ocr, msk; + + ocr = OCR_VOLTAGE_WINDOW(BUS_POWER_VOLTAGE); + msk = OCR_PowerUpEnd; + do { + r = _sdmmc_cmd(CMD1R3_SEND_OP_COND, ocr & ~OCR_PowerUpEnd); + if (r != SDCARD_OK) { + DEBUG_LOG_SD("MMCPROT\tcards with non compatible voltage range\n"); + return r; + } + ocr = _sdmmc_resp(); + if ((ocr & msk) == msk) break; + + DEBUG_LOG_SD("MMCPROT\tcard is busy or\n"); + DEBUG_LOG_SD("\thost omitted voltage range\n"); + sd_sm->ci_stat = SDP_IDLE; + } while (AM18X_TRUE); + + if (ocr & MOCR_VOLTAGE_165to195) { + DEBUG_LOG_SD("MMCPROT\tLow Voltage MultiMediaCard\n"); + } else { + DEBUG_LOG_SD("MMCPROT\tHigh Voltage MultiMediaCard\n"); + } + + DEBUG_LOG_SD("%s() ocr = 0x%.8X\n", __func__, ocr); + + if (0 == (ocr & OCR_VOLTAGE_WINDOW(BUS_POWER_VOLTAGE))) { + return SDCARD_UNSUPPORTED; + } + return SDCARD_OK; +} + +// Protocol +// 4.2.3 Card Initialization and Identification Process +static t_sdcard_status _sdmmc_card_init(void) { + t_sdcard_status r; + int i; + + sd_sm->rca = 0; + sd_sm->is_mmc = 0; + sd_sm->is_bus4bit = 0; + sd_sm->ci_stat = SDP_IDLE; + + for (i = 0; i < 1000; i++); + + _sdmmc_cmd_noarg(CMD0_GO_IDLE_STATE); + DEBUG_LOG_SD("SDPROT\tIdle State(idle)\n"); + + r = _sdmmc_cmd(CMD8R7_SEND_IF_COND, CMD8_VHS_27to36 | CMD8_CHECK_PATTERN); + DEBUG_LOG_SD("SDMMC cmd8() %s\n", dev_sdcard_err_string(r)); + + if (r == SDCARD_NO_RESPONSE) { + DEBUG_LOG_SD("SDPROT\tVer2.00 or later SD Memory Card(voltage mismatch)\n"); + DEBUG_LOG_SD("\tor Ver1.X SD Memory Card\n"); + DEBUG_LOG_SD("\tor not SD Memory Card\n"); + } else if ((_sdmmc_resp() & CMD8_CHECK_PATTERN_MASK) == CMD8_CHECK_PATTERN) { + DEBUG_LOG_SD("SDPROT\tVer2.00 or later SD Memory Card\n"); + } else { + // unsupported + return SDCARD_UNSUPPORTED; + } + + if ((r = _ACMD41()) == SDCARD_OK) { + DEBUG_LOG_SD("SDPROT\tCard returns ready\n"); + DEBUG_LOG_SD("\tVer1.X Standard Capacity SD Memory Card\n"); + } else { + DEBUG_LOG_SD("SDPROT\tNo Response(Non valid command)\n"); + DEBUG_LOG_SD("\tMust be a MultiMediaCard\n"); + DEBUG_LOG_SD("SDPORT\tStart MultiMediaCard initialization process\n"); + DEBUG_LOG_SD("\tstarting at CMD1\n"); + + if ((r = _CMD1()) != SDCARD_OK) { + return r; + } + sd_sm->is_mmc = 1; + sd_sm->rca = MMC_RCA; + } + + DEBUG_LOG_SD("SDPROT\tReady State(ready)\n"); + + for (i = 0; i < SDMMC_CMD_RETRY; i++) { + r = _sdmmc_cmd_noarg(CMD2R2_ALL_SEND_CID); + if (r == SDCARD_OK) break; + } + if (r != SDCARD_OK) { + return r; + } + DEBUG_LOG_SD("SDPROT\tIdentification State(ident)\n"); + + _sdmmc_get_cid(); + // sdprot_print_cid(&sd_sm->cid); + + for (i = 0; i < SDMMC_CMD_RETRY; i++) { + uint32_t arg = 0; + + if (sd_sm->is_mmc) { + arg = sd_sm->rca << 16; + } + r = _sdmmc_cmd(CMD3R6_SEND_RELATIVE, arg); + if (r == SDCARD_OK) break; + } + if (r != SDCARD_OK) { + return r; + } + + DEBUG_LOG_SD("SDPROT\tCard responds with new RCA\n"); + + if (!sd_sm->is_mmc) { + sd_sm->rca = (_sdmmc_resp() >> 16); + } + DEBUG_LOG_SD("SDMMC new RCA = 0x%.4X\n", sd_sm->rca); + + DEBUG_LOG_SD("SDPROT\tcard identification mode <-> data transfer mode\n"); + DEBUG_LOG_SD("SDPROT\tStand by State(stby)\n"); + + return r; +} + +static t_sdcard_status _sdmmc_get_csd(void) { + t_mmcsd_resp lngrsp; + t_sdcard_status r; + int i; + + for (i = 0; i < SDMMC_CMD_RETRY; i++) { + r = _sdmmc_cmd(CMD9R2_SEND_CSD, sd_sm->rca << 16); + if (r == SDCARD_OK) break; + } + if (r != SDCARD_OK) { + return r; + } + + mmcsd_get_resp(MMCSDCON, MMCSD_RESP_LONG, &lngrsp); + sdprot_get_csd(&sd_sm->csd, lngrsp.v); + // sdprot_print_csd(&sd_sm->csd); + + return r; +} + +static t_sdcard_status _sdmmc_speed_up(void) { + uint32_t speed; + + speed = sdprot_trans_speed(&sd_sm->csd); + mmcsd_set_freq(MMCSDCON, speed); + + return SDCARD_OK; +} + +static t_sdcard_status _sdmmc_get_classes(void) { + uint32_t ccc; + int i; + + ccc = sd_sm->csd.CCC; + + DEBUG_LOG_SD("SDMMC Card Supported Classes:"); + for (i = 0; i < 12; i++) { + if (ccc & BIT(i)) { + DEBUG_LOG_SD(" %d", i); + } + } + DEBUG_LOG_SD("\n"); + + DEBUG_LOG_SD("SDMMC Card Size %u bytes\n", sdprot_device_size(&sd_sm->csd)); + + return SDCARD_OK; +} + +static t_sdcard_status _sdmmc_sel_card(bool sel) { + uint32_t arg; + t_sdcard_status r; + + arg = sel? (sd_sm->rca << 16): 0; + r = _sdmmc_cmd(CMD7R1b_SEL_UNSEL_CARD, arg); + if (r != SDCARD_OK) { + return r; + } + + // _sdmmc_print_r1(); + + return SDCARD_OK; +} + +static t_sdcard_status _sdmmc_max_buswidth(void) { + t_sdcard_status r; + + if (sd_sm->is_mmc) { + return SDCARD_OK; + } + + r = _sdmmc_acmd(ACMD6R1_SET_BUS_WIDTH, ACMD6_BW_4BIT); + if (r == SDCARD_OK) { + sd_sm->is_bus4bit = 1; + sd_sm->ci_stat = SDP_TRAN; + } + + if (sd_sm->is_bus4bit) { + t_mmcsd_misc misc; + + misc.mflags = MMCSD_MISC_F_BUS4BIT; + mmcsd_cntl_misc(MMCSDCON, &misc); + } + return r; +} + +/*----- End of file --------------------------------------------------*/ + + diff --git a/cpu/src/kernel/device/dev_sdcard.h b/cpu/src/kernel/device/dev_sdcard.h new file mode 100644 index 00000000..f948ec7a --- /dev/null +++ b/cpu/src/kernel/device/dev_sdcard.h @@ -0,0 +1,72 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file dev_sdcard.h + * + * @brief Public API for SD card device. + */ + +#ifndef DEV_SDCARD_H +#define DEV_SDCARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +#include + +/*----- Macros -------------------------------------------------------*/ + +/*----- Typedefs -----------------------------------------------------*/ + +typedef enum { + SDCARD_OK = 0, + SDCARD_NO_RESPONSE = -1, + SDCARD_CRC_ERROR = -2, + SDCARD_UNSUPPORTED = -3, + SDCARD_ERROR = -4, +} t_sdcard_status; + +/*----- Extern function prototypes -----------------------------------*/ + +t_sdcard_status dev_sdcard_init(void); +t_sdcard_status dev_sdcard_read(uint32_t blk_nr, uint32_t blk_cnt, uint32_t* buf); +t_sdcard_status dev_sdcard_write(uint32_t blk_nr, uint32_t blk_cnt, const uint32_t* buf); +const char* dev_sdcard_err_string(int rt); + +#ifdef __cplusplus +} +#endif +#endif + +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/device/sd_protocol.c b/cpu/src/kernel/device/sd_protocol.c new file mode 100644 index 00000000..88868e30 --- /dev/null +++ b/cpu/src/kernel/device/sd_protocol.c @@ -0,0 +1,438 @@ +/*---------------------------------------------------------------------- +MIT License + +Copyright (c) 2013 - turmary@126.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +Original work by turmary@126.com, modified by novictim 2025 for freetribe. +----------------------------------------------------------------------*/ + +/** + * @file sd_protocol.c. + * + * @brief MMC/SD protocol specific code. + */ + +/*----- Includes -----------------------------------------------------*/ + +#include "am18x_lib.h" +#include "sd_protocol.h" +#include +#include "macros.h" + +/*----- Macros -------------------------------------------------------*/ + +#ifdef DEBUG_SDDRIVER +# define DEBUG_LOG_SD(fmt, ...) DEBUG_LOG(fmt, ##__VA_ARGS__) +#else +# define DEBUG_LOG_SD(...) +#endif + +#define IDLE SDP_IDLE +#define REDY SDP_READY +#define IDNT SDP_IDENT +#define STBY SDP_STBY +#define TRAN SDP_TRAN +#define DATA SDP_DATA +#define RCV SDP_RCV +#define PRG SDP_PRG +#define DIS SDP_DIS +#define INA SDP_INA +#define INV SDP_INV + +/*----- Typedefs -----------------------------------------------------*/ + +typedef struct { + int key; + const char* val; +} kvp_t; + +/*----- Static variable definitions ----------------------------------*/ + +// 4.8 Card State Transition Table +static uint8_t card_stat_trans_table[/*cmd index*/][SDP_CNT] = { +#define RESERVED_CMD {INV, INV, INV, INV, INV, INV, INV, INV, INV, } + + //IDLE,READY,INDENT,STBY,TRAN, DATA, RCV, PRG, DIS + // class 0 + {IDLE, IDLE, IDLE, IDLE, IDLE, IDLE, IDLE, IDLE, IDLE, }, // CMD0 + // The MultiMediaCard 4.8 Card State Transition Table + {REDY, INV, INV, INV, INV, INV, INV, INV, INV, }, // CMD1 + {INV, IDNT, INV, INV, INV, INV, INV, INV, INV, }, // CMD2 + {INV, INV, STBY, STBY, INV, INV, INV, INV, INV, }, // CMD3 + {INV, INV, INV, STBY, INV, INV, INV, INV, INV, }, // CMD4 + RESERVED_CMD, // CMD5 + {INV, INV, INV, INV, DATA, INV, INV, INV, INV, }, // CMD6 + {INV, INV, INV, TRAN, INV, INV, INV, INV, PRG, }, // CMD7 addressed + //{INV, INV, INV, STBY, STBY, STBY, INV, DIS, INV, },//CMD7 not addressed + {IDLE, INV, INV, INV, INV, INV, INV, INV, INV, }, // CMD8 + {INV, INV, INV, STBY, INV, INV, INV, INV, INV, }, // CMD9 + {INV, INV, INV, STBY, INV, INV, INV, INV, INV, }, // CMD10 + RESERVED_CMD, // CMD11 + {INV, INV, INV, INV, INV, TRAN, PRG, INV, INV, }, // CMD12 + {INV, INV, INV, STBY, TRAN, DATA, RCV, PRG, DIS, }, // CMD13 + RESERVED_CMD, // CMD14 + {INV, INV, INV, INA, INA, INA, INA, INA, INA, }, // CMD15 + // class 2 + {INV, INV, INV, INV, TRAN, INV, INV, INV, INV, }, // CMD16 + {INV, INV, INV, INV, DATA, INV, INV, INV, INV, }, // CMD17 + {INV, INV, INV, INV, DATA, INV, INV, INV, INV, }, // CMD18 + RESERVED_CMD, // CMD19 + RESERVED_CMD, // CMD20 + RESERVED_CMD, // CMD21 + {INV, INV, INV, INV, DATA, INV, INV, INV, INV, }, // ACMD22 + {INV, INV, INV, INV, TRAN, INV, INV, INV, INV, }, // ACMD23 + {INV, INV, INV, INV, RCV, INV, INV, INV, INV, }, // CMD24 + {INV, INV, INV, INV, RCV, INV, INV, INV, INV, }, // CMD25 + RESERVED_CMD, // CMD26 + {INV, INV, INV, INV, RCV, INV, INV, INV, INV, }, // CMD27 + // class 6 + {INV, INV, INV, INV, PRG, INV, INV, INV, INV, }, // CMD28 + {INV, INV, INV, INV, PRG, INV, INV, INV, INV, }, // CMD29 + {INV, INV, INV, INV, DATA, INV, INV, INV, INV, }, // CMD30 + RESERVED_CMD, // CMD31 + // class 5 + {INV, INV, INV, INV, TRAN, INV, INV, INV, INV, }, // CMD32 + {INV, INV, INV, INV, TRAN, INV, INV, INV, INV, }, // CMD33 + RESERVED_CMD, // CMD34 + RESERVED_CMD, // CMD35 + RESERVED_CMD, // CMD36 + RESERVED_CMD, // CMD37 + {INV, INV, INV, INV, PRG, INV, INV, INV, INV, }, // CMD38 + RESERVED_CMD, // CMD39 + RESERVED_CMD, // CMD40 + {REDY, INV, INV, INV, INV, INV, INV, INV, INV, }, // ACMD41 + // class 7 + {INV, INV, INV, INV, RCV, INV, INV, INV, INV, }, // CMD42 + RESERVED_CMD, // CMD43 + RESERVED_CMD, // CMD44 + RESERVED_CMD, // CMD45 + RESERVED_CMD, // CMD46 + RESERVED_CMD, // CMD47 + RESERVED_CMD, // CMD48 + RESERVED_CMD, // CMD49 + RESERVED_CMD, // CMD50, + {INV, INV, INV, INV, DATA, INV, INV, INV, INV, }, // ACMD51, + RESERVED_CMD, // CMD52, + RESERVED_CMD, // CMD53, + RESERVED_CMD, // CMD54, + // class 8 + {IDLE, INV, INV, STBY, TRAN, DATA, RCV, PRG, DIS, }, // CMD55 + {INV, INV, INV, INV, DATA, INV, INV, INV, INV, }, // CMD56 + RESERVED_CMD, // CMD57, + RESERVED_CMD, // CMD58, + RESERVED_CMD, // CMD59, + RESERVED_CMD, // CMD60, + RESERVED_CMD, // CMD61, + RESERVED_CMD, // CMD62, + RESERVED_CMD, // CMD63, +}; + +static kvp_t card_state_name[] = { + {SDP_IDLE, "card identification mode(Idle State)"}, + {SDP_READY, "card identification mode(Ready State)"}, + {SDP_IDENT, "card identification mode(Identification State)"}, + {SDP_STBY, "data transfer mode(Stand-by State)"}, + {SDP_TRAN, "data transfer mode(Transfer State)"}, + {SDP_DATA, "data transfer mode(Sending-data State)"}, + {SDP_RCV, "data transfer mode(Receive-data State)"}, + {SDP_PRG, "data transfer mode(Programming State)"}, + {SDP_DIS, "data transfer mode(Disconnect State)"}, +}; + +/*----- Extern function implementations ------------------------------*/ + +sdp_cur_stat_t sdprot_next_stat(int cmd_nr, uint8_t cur_stat) { + if (cmd_nr >= countof(card_stat_trans_table)) return SDP_INV; + if (cur_stat >= SDP_CNT) return SDP_INV; + return card_stat_trans_table[cmd_nr][cur_stat]; +} + +const char* sdprot_stat_name(int stat) { + if (0 <= stat && stat < SDP_CNT) { + return card_state_name[stat].val; + } + if (stat == SDP_INA) { + return "inactive(Inactive State)"; + } + return "Invalid Card State"; +} + +sdcard_response_t sdprot_resp_type(int cmd_nr) { + + switch(cmd_nr) { + case CMD0_GO_IDLE_STATE: + case CMD4_SEND_DSR: + case CMD15_GO_INACTIVE_STATE: + return SDCARD_NONE_RSP; + + case CMD2R2_ALL_SEND_CID: + case CMD9R2_SEND_CSD: + case CMD10R2_SEND_CID: + return SDCARD_136BIT_RSP; + default: + break; + } + + return SDCARD_48BIT_RSP; +} + +int sdprot_resp_crc(int cmd_nr) { + if (cmd_nr == ACMD41R3_SD_SEND_OP_COND + || cmd_nr == CMD1R3_SEND_OP_COND) { + return 0; + } + return 1; +} + +sdprot_datadir_t sdprot_need_data(int cmd_nr) { + switch(cmd_nr) { + default: + return SDPROT_NO_DATA; + case CMD17R1_READ_SINGLE_BLOCK: + case CMD18R1_READ_MULTIPLE_BLOCK: + case CMD30R1_SEND_WRITE_PROT: + case CMD56R1_GEN_CMD: + case ACMD13R1_SD_STATUS: + case ACMD22R1_SEND_NUM_WR_BLOCKS: + case ACMD51R1_SEND_SCR: + return SDPROT_READ_DATA; + case CMD24R1_WRITE_BLOCK: + case CMD25R1_WRITE_MULTIPLE_BLOCK: + case CMD27R1_PROGRAM_CSD: + case CMD42R1_LOCK_UNLOCK: + return SDPROT_WRITE_DATA; + } + return SDPROT_NO_DATA; +} + +int sdprot_need_busy(int cmd_nr) { + switch(cmd_nr) { + default: + return 0; + case CMD7R1b_SEL_UNSEL_CARD: + case CMD8R7_SEND_IF_COND: + case CMD12R1b_STOP_TRANSMISSION: + case CMD28R1b_SET_WRITE_PROT: + case CMD29R1b_CLR_WRITE_PROT: + case CMD38R1b_ERASE: + return 1; + } + return 0; +} + +int sdprot_get_cid(CID_t* cid, const uint32_t* resp) { + cid->CRC = __field_xget(resp[0], 0x7F << 1); + cid->MDT = __field_xget(resp[0], 0xFFF << 8); + cid->PSN = __field_xget(resp[0], 0xFF << 24) | + __field_xget(resp[1], 0xFFFFFF << 0); + cid->PRV = __field_xget(resp[1], 0xFF << 24); + + memcpy((char*)cid->PNM, (char*)(&resp[2]), 5); + cid->PNM[5] = 0; + + cid->OID = __field_xget(resp[3], 0xFFFF << 8); + cid->MID = __field_xget(resp[3], 0xFF << 24); + + return 0; +} + +int sdprot_print_cid(const CID_t* cid) { + + DEBUG_LOG_SD("************ CID register ************\n"); + DEBUG_LOG_SD("Manufacturer ID MID \t0x%.2X\n", cid->MID); + DEBUG_LOG_SD("OEM/Application ID OID \t0x%.4X\n", cid->OID); + DEBUG_LOG_SD("Product name PNM \t%s\n", cid->PNM); + DEBUG_LOG_SD("Product revision PRV \t%d\n", cid->PRV); + DEBUG_LOG_SD("Product serial number PSN\t0x%.8X\n", cid->PSN); + DEBUG_LOG_SD("Manufacturing date MDT \t0x%.3X\n", cid->MDT); + DEBUG_LOG_SD("CRC7 checksum CRC \t0x%.2X\n", cid->CRC); + DEBUG_LOG_SD("**************************************\n"); + + return 0; +} + +int sdprot_get_csd(CSD_t* csd, const uint32_t* resp) { + uint32_t v; + + csd->CSD_STRUCTURE = __field_xget(resp[3], 0x3 << 30); + csd->TAAC = __field_xget(resp[3], 0xFF << 16); + csd->NSAC = __field_xget(resp[3], 0xFF << 8); + csd->TRANS_SPEED = __field_xget(resp[3], 0xFF << 0); + + csd->CCC = __field_xget(resp[2], 0xFFF << 20); + csd->READ_BL_LEN = __field_xget(resp[2], 0xF << 16); + csd->READ_BL_PARTIAL = __field_xget(resp[2], 0x1 << 15); + csd->WRITE_BLK_MISALIGN = __field_xget(resp[2], 0x1 << 14); + csd->READ_BLK_MISALIGN = __field_xget(resp[2], 0x1 << 13); + csd->DSR_IMP = __field_xget(resp[2], 0x1 << 12); + v = __field_xget(resp[2], 0x3FF << 0); + + csd->C_SIZE = (v << 2) | __field_xget(resp[1], 0x3 << 30); + csd->VDD_R_CURR_MIN = __field_xget(resp[1], 0x7 << 27); + csd->VDD_R_CURR_MAX = __field_xget(resp[1], 0x7 << 24); + csd->VDD_W_CURR_MIN = __field_xget(resp[1], 0x7 << 21); + csd->VDD_W_CURR_MAX = __field_xget(resp[1], 0x7 << 18); + csd->C_SIZE_MULT = __field_xget(resp[1], 0x7 << 15); + csd->ERASE_BLK_LEN = __field_xget(resp[1], 0x1 << 14); + csd->SECTOR_SIZE = __field_xget(resp[1], 0x7F << 7); + csd->WP_GRP_SIZE = __field_xget(resp[1], 0x7F << 0); + + csd->WP_GRP_ENABLE = __field_xget(resp[0], 0x1 << 31); + csd->R2W_FACTOR = __field_xget(resp[0], 0x7 << 26); + csd->WRITE_BL_LEN = __field_xget(resp[0], 0xF << 22); + csd->WRITE_BL_PARTIAL = __field_xget(resp[0], 0x1 << 21); + csd->FILE_FORMAT_GRP = __field_xget(resp[0], 0x1 << 15); + csd->COPY = __field_xget(resp[0], 0x1 << 14); + csd->PERM_WRITE_PROTECT = __field_xget(resp[0], 0x1 << 12); + csd->TMP_WRITE_PROTECT = __field_xget(resp[0], 0x1 << 11); + csd->FILE_FORMAT = __field_xget(resp[0], 0x3 << 9); + csd->CRC = __field_xget(resp[0], 0x7F << 1); + return 0; +} + +int sdprot_print_csd(const CSD_t* csd) { + DEBUG_LOG_SD("************ CSD register ************\n"); + DEBUG_LOG_SD("CSD structure \t0x%X\n", csd->CSD_STRUCTURE); + DEBUG_LOG_SD("data read access-time-1 \t0x%X\n", csd->TAAC); + DEBUG_LOG_SD("data read access-time-2 \t0x%X\n", csd->NSAC); + DEBUG_LOG_SD("max. data transfer rate \t0x%X\n", csd->TRANS_SPEED); + DEBUG_LOG_SD("card command classes \t0x%X\n", csd->CCC); + DEBUG_LOG_SD("max. read data block length\t0x%X\n", csd->READ_BL_LEN); + DEBUG_LOG_SD("partial blocks for read allowed\t0x%X\n", csd->READ_BL_PARTIAL); + DEBUG_LOG_SD("write block misalignment \t0x%X\n", csd->WRITE_BLK_MISALIGN); + DEBUG_LOG_SD("read block misalignment \t0x%X\n", csd->READ_BLK_MISALIGN); + DEBUG_LOG_SD("DSR implemented \t0x%X\n", csd->DSR_IMP); + DEBUG_LOG_SD("device size \t0x%X\n", csd->C_SIZE); + DEBUG_LOG_SD("max. read current @VDD min \t0x%X\n", csd->VDD_R_CURR_MIN); + DEBUG_LOG_SD("max. read current @VDD max \t0x%X\n", csd->VDD_R_CURR_MAX); + DEBUG_LOG_SD("max. write current @VDD min\t0x%X\n", csd->VDD_W_CURR_MIN); + DEBUG_LOG_SD("max. write current @VDD max\t0x%X\n", csd->VDD_W_CURR_MAX); + DEBUG_LOG_SD("device size multiplier \t0x%X\n", csd->C_SIZE_MULT); + DEBUG_LOG_SD("erase single block enable \t0x%X\n", csd->ERASE_BLK_LEN); + DEBUG_LOG_SD("erase sector size \t0x%X\n", csd->SECTOR_SIZE); + DEBUG_LOG_SD("write protect group size \t0x%X\n", csd->WP_GRP_SIZE); + DEBUG_LOG_SD("write protect group enable \t0x%X\n", csd->WP_GRP_ENABLE); + DEBUG_LOG_SD("write speed factor \t0x%X\n", csd->R2W_FACTOR); + DEBUG_LOG_SD("max. write data block length\t0x%X\n", csd->WRITE_BL_LEN); + DEBUG_LOG_SD("partial blocks f write allowed\t0x%X\n", csd->WRITE_BL_PARTIAL); + DEBUG_LOG_SD("File format group \t0x%X\n", csd->FILE_FORMAT_GRP); + DEBUG_LOG_SD("copy flag (OTP) \t0x%X\n", csd->COPY); + DEBUG_LOG_SD("permanent write protection \t0x%X\n", csd->PERM_WRITE_PROTECT); + DEBUG_LOG_SD("temporary write protection \t0x%X\n", csd->TMP_WRITE_PROTECT); + DEBUG_LOG_SD("File format \t0x%X\n", csd->FILE_FORMAT); + DEBUG_LOG_SD("CRC \t0x%X\n", csd->CRC); + DEBUG_LOG_SD("**************************************\n"); + return 0; +} + +uint32_t sdprot_trans_speed(const CSD_t* csd) { + // 5.3.2 CSD Register + const uint32_t muls[] = { + 0, 10, 12, 13, + 15, 20, 25, 30, + 35, 40, 45, 50, + 55, 60, 70, 80, + }; + uint32_t v, base, idx, speed; + + v = csd->TRANS_SPEED; + + switch(v & 0x07) { + case 0: base = 100000; break; + case 1: base = 1000000; break; + case 2: base = 10000000; break; + case 3: base = 100000000; break; + default: return 0; + } + + idx = __field_xget(v, 0x78); + speed = base / 10 * muls[idx]; + + DEBUG_LOG_SD("SDPROT\tTRANS_SPEED = %d * (%d / 10) = %d Hz\n", base, muls[idx], speed); + + return speed; +} + +// unit: mA +static const uint16_t vdd_rw_current_min[] = { + 0/*0.5*/, 1, 5, 10, + 25, 35, 60, 100, +}; + +// uint: mA +static const uint16_t vdd_rw_current_max[] = { + 1, 5, 10, 25, + 35, 45, 80, 200, +}; + +// unit: bytes +uint32_t sdprot_device_size(const CSD_t* csd) { + uint32_t bnr, bln; + + bnr = (csd->C_SIZE + 1) << (csd->C_SIZE_MULT + 2); + bln = 1 << csd->READ_BL_LEN; + + DEBUG_LOG_SD("SDPROT\twrite data block length: %d bytes\n", 1 << csd->WRITE_BL_LEN); + DEBUG_LOG_SD("SDPROT\tread data block length: %d bytes\n", bln); + DEBUG_LOG_SD("SDPROT\tblock number: %d blocks\n", bnr); + DEBUG_LOG_SD("SDPROT\tread current: %dmA %dmA\n", + vdd_rw_current_min[csd->VDD_R_CURR_MIN], + vdd_rw_current_max[csd->VDD_R_CURR_MAX]); + DEBUG_LOG_SD("SDPROT\twrite current: %dmA %dmA\n", + vdd_rw_current_min[csd->VDD_W_CURR_MIN], + vdd_rw_current_max[csd->VDD_W_CURR_MAX]); + + return bnr * bln; +} + +int sdprot_print_r1_stat(const sdp_r1_stat_t* r1_stat) { + DEBUG_LOG_SD("************ R1 response ************\n"); + DEBUG_LOG_SD("RES_TEST_MODE \t=0x%X\n", r1_stat->RES_TEST_MODE); + DEBUG_LOG_SD("RES_APP_CMD \t=0x%X\n", r1_stat->RES_APP_CMD); + DEBUG_LOG_SD("AKE_SEQ_ERROR \t=0x%X\n", r1_stat->AKE_SEQ_ERROR); + DEBUG_LOG_SD("RES_SDIO_CARD \t=0x%X\n", r1_stat->RES_SDIO_CARD); + DEBUG_LOG_SD("APP_CMD \t=0x%X\n", r1_stat->APP_CMD); + DEBUG_LOG_SD("RES1 \t=0x%X\n", r1_stat->RES1); + DEBUG_LOG_SD("READY_FOR_DATA \t=0x%X\n", r1_stat->READY_FOR_DATA); + DEBUG_LOG_SD("CURRENT_STATE \t=%s\n", sdprot_stat_name(r1_stat->CURRENT_STATE)); + DEBUG_LOG_SD("ERASE_RESET \t=0x%X\n", r1_stat->ERASE_RESET); + DEBUG_LOG_SD("CARD_ECC_DISABLED\t=0x%X\n", r1_stat->CARD_ECC_DISABLED); + DEBUG_LOG_SD("WP_ERASE_SKIP \t=0x%X\n", r1_stat->WP_ERASE_SKIP); + DEBUG_LOG_SD("CSD_OVERWRITE \t=0x%X\n", r1_stat->CSD_OVERWRITE); + DEBUG_LOG_SD("RES2 \t=0x%X\n", r1_stat->RES2); + DEBUG_LOG_SD("ERROR \t=0x%X\n", r1_stat->ERROR); + DEBUG_LOG_SD("CC_ERROR \t=0x%X\n", r1_stat->CC_ERROR); + DEBUG_LOG_SD("CARD_ECC_FAILED \t=0x%X\n", r1_stat->CARD_ECC_FAILED); + DEBUG_LOG_SD("ILLEGAL_COMMAND \t=0x%X\n", r1_stat->ILLEGAL_COMMAND); + DEBUG_LOG_SD("COM_CRC_ERROR \t=0x%X\n", r1_stat->COM_CRC_ERROR); + DEBUG_LOG_SD("CARD_UNLOCK_FAILED\t=0x%X\n", r1_stat->CARD_UNLOCK_FAILED); + DEBUG_LOG_SD("CARD_IS_LOCKED \t=0x%X\n", r1_stat->CARD_IS_LOCKED); + DEBUG_LOG_SD("WP_VIOLATION \t=0x%X\n", r1_stat->WP_VIOLATION); + DEBUG_LOG_SD("ERASE_PARAM \t=0x%X\n", r1_stat->ERASE_PARAM); + DEBUG_LOG_SD("ERASE_SEQ_ERROR \t=0x%X\n", r1_stat->ERASE_SEQ_ERROR); + DEBUG_LOG_SD("BLOCK_LEN_ERROR \t=0x%X\n", r1_stat->BLOCK_LEN_ERROR); + DEBUG_LOG_SD("ADDRESS_ERROR \t=0x%X\n", r1_stat->ADDRESS_ERROR); + DEBUG_LOG_SD("OUT_OF_RANGE \t=0x%X\n", r1_stat->OUT_OF_RANGE); + DEBUG_LOG_SD("**************************************\n"); + + return 0; +} diff --git a/cpu/src/kernel/device/sd_protocol.h b/cpu/src/kernel/device/sd_protocol.h new file mode 100644 index 00000000..f5fc6205 --- /dev/null +++ b/cpu/src/kernel/device/sd_protocol.h @@ -0,0 +1,280 @@ +/*---------------------------------------------------------------------- +MIT License + +Copyright (c) 2013 - turmary@126.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +Original work by turmary@126.com, modified by novictim 2025 for freetribe. +----------------------------------------------------------------------*/ + +/** + * @file sd_protocol.h + * + * @brief MMC/SD protocol specific code. + */ + +#ifndef SD_PROTOCOL_H +#define SD_PROTOCOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Physical Layer Simplified Specification Version 2.0 + +#define MMC_MAX_CLK 20000000 //Hz +#define SD_MAX_CLK 25000000 //Hz + +// 4.3.13 +#define CMD8_VHS_MASK 0x00000F00 +#define CMD8_VHS_27to36 0x00000100 +#define CMD8_CHECK_PATTERN_MASK 0x000000FF +#define CMD8_CHECK_PATTERN 0x000000AA + +// 4.7.4 Detailed Command Description +typedef enum { + SDCARD_NONE_RSP, + SDCARD_48BIT_RSP, + SDCARD_136BIT_RSP, +} sdcard_response_t; + +// Basic Commands (class 0) +#define CMD0_GO_IDLE_STATE 0 +#define CMD1R3_SEND_OP_COND 1 +#define CMD2R2_ALL_SEND_CID 2 +#define CMD3R6_SEND_RELATIVE 3 +#define CMD4_SEND_DSR 4 +#define CMD7R1b_SEL_UNSEL_CARD 7 +#define CMD8R7_SEND_IF_COND 8 +#define CMD9R2_SEND_CSD 9 +#define CMD10R2_SEND_CID 10 +#define CMD12R1b_STOP_TRANSMISSION 12 +#define CMD13R1_SEND_STATUS 13 +#define CMD15_GO_INACTIVE_STATE 15 +// Block-Oriented Read Commands (class 2) +#define CMD16R1_SET_BLOCKLEN 16 +#define CMD17R1_READ_SINGLE_BLOCK 17 +#define CMD18R1_READ_MULTIPLE_BLOCK 18 +// Block-Oriented Write Commands (class 4) +#define CMD24R1_WRITE_BLOCK 24 +#define CMD25R1_WRITE_MULTIPLE_BLOCK 25 +#define CMD27R1_PROGRAM_CSD 27 +// Block-Oriented Write Protection Commands (class 6) +#define CMD28R1b_SET_WRITE_PROT 28 +#define CMD29R1b_CLR_WRITE_PROT 29 +#define CMD30R1_SEND_WRITE_PROT 30 +// Erase Commands (class 5) +#define CMD32R1_ERASE_WR_BLK_START 32 +#define CMD33R1_ERASE_WR_BLK_END 33 +#define CMD38R1b_ERASE 38 +// Lock Card (class 7) +#define CMD42R1_LOCK_UNLOCK 42 +// Application-specific Commands (class 8) +#define CMD55R1_APP_CMD 55 +#define CMD56R1_GEN_CMD 56 + +// Application Specific Commands used/reserved by SD Memory Card +#define ACMD6R1_SET_BUS_WIDTH 6 +#define ACMD6_BW_1BIT 0x00 +#define ACMD6_BW_4BIT 0x02 +#define ACMD13R1_SD_STATUS 13 +#define ACMD22R1_SEND_NUM_WR_BLOCKS 22 +#define ACMD23R1_SET_WR_BLK_ERASE_COUNT 23 +#define ACMD41R3_SD_SEND_OP_COND 41 +#define ACMD42R1_SET_CLR_CARD_DETECT 42 +#define ACMD51R1_SEND_SCR 51 + +// 4.9 Responses +#define R1_CodeLength 48 +#define R1b_CodeLength 48 +// CID, CSD Register +#define R2_CodeLength 136 +// OCR Register +#define R3_CodeLength 48 +// Published RCA response +#define R6_CodeLength 48 +// Card interface condition +#define R7_CodeLength 48 + +// 4.10 Two Status Information of SD Memory Card +// 4.10.1 Card Status +// The response format R1 contains a 32-bit named card status. +typedef enum { + SDP_IDLE = 0, + SDP_READY, + SDP_IDENT, + SDP_STBY, + SDP_TRAN, + SDP_DATA, + SDP_RCV, + SDP_PRG, + SDP_DIS, + SDP_CNT, + SDP_INA = 16, + SDP_INV = 255, +} sdp_cur_stat_t; + +typedef struct { + uint8_t RES_TEST_MODE :2; + uint8_t RES_APP_CMD :1; + uint8_t AKE_SEQ_ERROR :1; + uint8_t RES_SDIO_CARD :1; + uint8_t APP_CMD :1; + uint8_t RES1 :2; + uint8_t READY_FOR_DATA :1; + uint8_t CURRENT_STATE :4; + uint8_t ERASE_RESET :1; + uint8_t CARD_ECC_DISABLED :1; + uint8_t WP_ERASE_SKIP :1; + uint8_t CSD_OVERWRITE :1; + uint8_t RES2 :2; + uint8_t ERROR :1; + uint8_t CC_ERROR :1; + uint8_t CARD_ECC_FAILED :1; + uint8_t ILLEGAL_COMMAND :1; + uint8_t COM_CRC_ERROR :1; + uint8_t CARD_UNLOCK_FAILED :1; + uint8_t CARD_IS_LOCKED :1; + uint8_t WP_VIOLATION :1; + uint8_t ERASE_PARAM :1; + uint8_t ERASE_SEQ_ERROR :1; + uint8_t BLOCK_LEN_ERROR :1; + uint8_t ADDRESS_ERROR :1; + uint8_t OUT_OF_RANGE :1; +} sdp_r1_stat_t; + + +#define CStatus_AKE_SEQ_ERROR BIT(3) +#define CStatus_APP_CMD BIT(5) +#define CStatus_READY_FOR_DATA BIT(8) +#define CStatus_STAT_MASK (0xF << 9) +#define CStatus_STAT_idle (SDP_STAT_IDLE << 9) +#define CStatus_STAT_ready (SDP_STAT_READY << 9) +#define CStatus_STAT_ident (SDP_STAT_IDENT << 9) +#define CStatus_STAT_stby (SDP_STAT_STBY << 9) +#define CStatus_STAT_tran (SDP_STAT_TRAN << 9) +#define CStatus_STAT_data (SDP_STAT_DATA << 9) +#define CStatus_STAT_rcv (SDP_STAT_RCV << 9) +#define CStatus_STAT_prg (SDP_STAT_PRG << 9) +#define CStatus_STAT_dis (SDP_STAT_DIS << 9) +#define CStatus_ERASE_RESET BIT(13) +#define CStatus_CARD_ECC_DISABLED BIT(14) +#define CStatus_WP_ERASE_SKIP BIT(15) +#define CStatus_CSD_OVERWRITE BIT(16) +#define CStatus_ERROR BIT(19) +#define CStatus_CC_ERROR BIT(20) +#define CStatus_CARD_ECC_FAILED BIT(21) +#define CStatus_ILLEGAL_COMMAND BIT(22) +#define CStatus_COM_CRC_ERROR BIT(23) +#define CStatus_CARD_UNLOCK_FAILED BIT(24) +#define CStatus_CARD_IS_LOCKED BIT(25) +#define CStatus_WP_VIOLATION BIT(26) +#define CStatus_ERASE_PARAM BIT(27) +#define CStatus_ERASE_SEQ_ERROR BIT(28) +#define CStatus_BLOCK_LEN_ERROR BIT(29) +#define CStatus_ADDRESS_ERROR BIT(30) +#define CStatus_OUT_OF_RANGE BIT(31) +// 4.10.2 SD Status +// The SD Status is sent to the host over the DAT bus +// as a response to ACMD13, have a length 512 bit. + +// 5.1 OCR Register +#define OCR_VOLTAGE_WINDOW(MV) BIT(MV / 100 - 12) +#define IS_VALID_OCR_VOLTAGE(MV) (2700 <= MV && MV <= 3600) + +#define OCR_VOLTAGE_MASK 0x00FF8000 +#define OCR_CCS 0x40000000 +#define OCR_PowerUpEnd 0x80000000 +#define MOCR_VOLTAGE_165to195 0x80 + +// 5.2 CID register +typedef struct { // width, offset + uint8_t MID; // 8, 120 + uint16_t OID; // 16, 104 + uint8_t PNM[6]; // 40, 64 + uint8_t PRV; // 8, 56 + uint32_t PSN; // 32, 24 + uint16_t MDT; // 12, 8 + uint8_t CRC; // 7, 1 +} CID_t; + +// 5.3.2 CSD Register +typedef struct { + uint8_t CSD_STRUCTURE; + uint8_t TAAC; + uint8_t NSAC; + uint8_t TRANS_SPEED; + uint16_t CCC; + uint8_t READ_BL_LEN; + uint8_t READ_BL_PARTIAL; + uint8_t WRITE_BLK_MISALIGN; + uint8_t READ_BLK_MISALIGN; + uint8_t DSR_IMP; + uint16_t C_SIZE; + uint8_t VDD_R_CURR_MIN; + uint8_t VDD_R_CURR_MAX; + uint8_t VDD_W_CURR_MIN; + uint8_t VDD_W_CURR_MAX; + uint8_t C_SIZE_MULT; + uint8_t ERASE_BLK_LEN; + uint8_t SECTOR_SIZE; + uint8_t WP_GRP_SIZE; + uint8_t WP_GRP_ENABLE; + uint8_t R2W_FACTOR; + uint8_t WRITE_BL_LEN; + uint8_t WRITE_BL_PARTIAL; + uint8_t FILE_FORMAT_GRP; + uint8_t COPY; + uint8_t PERM_WRITE_PROTECT; + uint8_t TMP_WRITE_PROTECT; + uint8_t FILE_FORMAT; + uint8_t CRC; +} CSD_t; + +typedef enum { + SDPROT_NO_DATA = 0, + SDPROT_READ_DATA, + SDPROT_WRITE_DATA, +} sdprot_datadir_t; + +/*----- Extern function prototypes -----------------------------------*/ + +sdcard_response_t sdprot_resp_type(int cmd_nr); +int sdprot_resp_crc(int cmd_nr); +sdprot_datadir_t sdprot_need_data(int cmd_nr); +int sdprot_need_busy(int cmd_nr); +int sdprot_get_cid(CID_t* cid, const uint32_t* resp); +int sdprot_print_cid(const CID_t* cid); +int sdprot_get_csd(CSD_t* csd, const uint32_t* resp); +int sdprot_print_csd(const CSD_t* csd); +uint32_t sdprot_trans_speed(const CSD_t* csd); +uint32_t sdprot_device_size(const CSD_t* csd); +sdp_cur_stat_t sdprot_next_stat(int cmd_nr, uint8_t cur_stat); +const char* sdprot_stat_name(int stat); +int sdprot_print_r1_stat(const sdp_r1_stat_t* r1_stat); + + +#ifdef __cplusplus +} +#endif +#endif + +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/peripheral/per_mmcsd.c b/cpu/src/kernel/peripheral/per_mmcsd.c new file mode 100644 index 00000000..b92c9304 --- /dev/null +++ b/cpu/src/kernel/peripheral/per_mmcsd.c @@ -0,0 +1,475 @@ +/*---------------------------------------------------------------------- +MIT License + +Copyright (c) 2013 - turmary@126.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +Original work by turmary@126.com, modified by novictim 2025 for freetribe. +----------------------------------------------------------------------*/ + +/** + * @file per_mmcsd.c. + * + * @brief Configuration and handling of MMC/SD controller peripheral. + */ + +/*----- Includes -----------------------------------------------------*/ + +#include "am18x_map.h" + +// @TODO: remove these 3 +#include +#include +#include +#include + +#include "macros.h" +#include "ft_error.h" + +#include "per_mmcsd.h" + +/*----- Macros -------------------------------------------------------*/ + +#ifdef DEBUG_SDDRIVER +# define DEBUG_LOG_SD(fmt, ...) DEBUG_LOG(fmt, ##__VA_ARGS__) +#else +# define DEBUG_LOG_SD(...) +#endif + +#define FCLK_FREQ (300000000/2) // PLL0_SYSCLK2=PLL0/2, assume PLL0=300mhz +#define MMCSD_INIT_FREQ 400000 // 400kHz +#define MMCSD_MAX_FREQ 50000000 // 50MHz + +/*----- Extern function implementations ------------------------------*/ + +// static uint32_t mmcsd_get_freq(const MMCSD_con_t* mcon) { +// uint32_t reg, f; + +// reg = mcon->MMCCLK; +// f = dev_get_freq(DCLK_ID_MMC_SDS); +// f /= (2 * (__field_xget(reg, MMCCLK_CLKRT_MASK) + 1)); +// if (FIELD_GET(reg, MMCCLK_DIV4_MASK) == MMCCLK_DIV4_div4) { +// f /= 2; +// } +// return f; +// } + +t_status mmcsd_set_freq(MMCSD_con_t* mcon, uint32_t freq) { + // uint32_t reg, msk, v; + + // if (freq == 0x0UL) { + // return mmcsd_get_freq(mcon); + // } + + // reg = mcon->MMCCLK; + // msk = MMCCLK_CLKRT_MASK; + // v = dev_get_freq(DCLK_ID_MMC_SDS) / (freq * 2); + // if (FIELD_GET(reg, MMCCLK_DIV4_MASK) == MMCCLK_DIV4_div4) { + // v /= 2; + // } + // if (v == 0) v = 1; + // mcon->MMCCLK = FIELD_SET(reg, msk, MMCCLK_CLKRT_VAL(v)); + + // if (mmcsd_get_freq(mcon) > freq) { + // v++; + // mcon->MMCCLK = FIELD_SET(reg, msk, MMCCLK_CLKRT_VAL(v)); + // } + + // return mmcsd_get_freq(mcon); + + + // note: MMCSD controller gets driven by PLL0_SYSCLK2 which is PLL0 + // divided by 2. + + if (freq > MMCSD_MAX_FREQ) { + return MMCSD_INVALID_FREQ_ERROR; + } + + // FCLK_FREQ/(2*(CLKRT+1)) + // (150000000/(2*(187+1)))/1000 = 398.94 khz + // CLKRT = ((FCLK_FREQ/freq)/2)-1 + uint8_t clkrt = (uint8_t)(((FCLK_FREQ + freq) / (2 * freq)) - 1); + mcon->MMCCLK = FIELD_SET(mcon->MMCCLK, MMCCLK_CLKRT_MASK, clkrt); // no shift + DEBUG_LOG_SD("Clock clkrt: %u freq: %u", (unsigned int)clkrt, freq); + + return SUCCESS; +} + +t_status mmcsd_con_init(MMCSD_con_t* mcon, const mmcsd_conf_t* conf) { + uint32_t reg, msk, v; + + // 1. Place the MMC/SD controller in its reset state + msk = MMCCTL_CMDRST_MASK | MMCCTL_DATRST_MASK; + v = MMCCTL_CMDRST_disabled | MMCCTL_DATRST_disabled; + mcon->MMCCTL = FIELD_SET(0, msk, v); + + // 2. Write the required values to other registers + // Initialize the MMC Control Register + reg = mcon->MMCCTL; + msk = MMCCTL_WIDTH0_MASK | MMCCTL_WIDTH1_MASK; + v = MMCCTL_WIDTH0_1bit | MMCCTL_WIDTH1_1_4bit; + mcon->MMCCTL = FIELD_SET(reg, msk, v); + + // // Initializing the Clock Controller Regisr + // reg = mcon->MMCCLK; + // msk = MMCCLK_DIV4_MASK | MMCCLK_CLKEN_MASK; + // v = MMCCLK_DIV4_div2 | MMCCLK_CLKEN_low; + // mcon->MMCCLK = FIELD_SET(reg, msk, v); + mmcsd_set_freq(mcon, conf->freq); + + // Initialize the Interrupt Mask Register + mcon->MMCIM = 0x0UL; + + // Initialize the Time-Out Registers + v = FIELD_SET(0, MMCTOR_TOD25_16_MASK, MMCTOR_TOD25_16_VAL(conf->timeout_dat)); + v = FIELD_SET(v, MMCTOR_TOR_MASK, MMCTOR_TOR_VAL(conf->timeout_rsp)); + mcon->MMCTOR = v; + + reg = mcon->MMCTOR; + v = MMCTOD_TOD15_0_VAL(conf->timeout_dat); + mcon->MMCTOD = FIELD_SET(reg, MMCTOD_TOD15_0_MASK, v); + + // Initialize the Data Block Registers + mcon->MMCBLEN = MMCSD_BLOCK_SIZE; + mcon->MMCNBLK = 1UL; + + // 3. release the MMC/SD controller from its reset state + v = MMCCTL_CMDRST_enabled | MMCCTL_DATRST_enabled; + mcon->MMCCTL = FIELD_SET(0, msk, v); + + // 4. Enable the MMCSD_CLK pin so that the memory clock is + // sent to the memory card + reg = mcon->MMCCLK; + mcon->MMCCLK = FIELD_SET(reg, MMCCLK_CLKEN_MASK, MMCCLK_CLKEN_enabled); + + return SUCCESS; +} + +t_status mmcsd_send_cmd(MMCSD_con_t* mcon, const t_mmcsd_cmd* cmd) { + uint32_t reg, msk, v; + uint32_t idx; + + assert(mcon); + assert(cmd); + assert(cmd->index < 0x40); + + idx = cmd->index; + + mcon->MMCCIDX = 0; + mcon->MMCRSP[0] = 0; + mcon->MMCRSP[1] = 0; + mcon->MMCRSP[2] = 0; + mcon->MMCRSP[3] = 0; + + reg = mcon->MMCCMD; + // Bug Fix, DCLR_clear is going data shift 2 bytes + reg = FIELD_SET(reg, MMCCMD_DCLR_MASK, MMCCMD_DCLR_none); + + if (cmd->cflags & MMCSD_CMD_F_BUSY) { + reg = FIELD_SET(reg, MMCCMD_BSYEXP_MASK, MMCCMD_BSYEXP_expected); + } else { + reg = FIELD_SET(reg, MMCCMD_BSYEXP_MASK, MMCCMD_BSYEXP_none); + } + + msk = MMCCMD_WDATX_MASK; + if (cmd->cflags & MMCSD_CMD_F_DATA) { + reg = FIELD_SET(reg, msk, MMCCMD_WDATX_yes); + reg = FIELD_SET(reg, MMCCMD_STRMTP_MASK, MMCCMD_STRMTP_block); + + if (cmd->cflags & MMCSD_CMD_F_WRITE) { + reg = FIELD_SET(reg, MMCCMD_DTRW_MASK, MMCCMD_DTRW_write); + } else { + reg = FIELD_SET(reg, MMCCMD_DMATRIG_MASK, MMCCMD_DMATRIG_triggered); + reg = FIELD_SET(reg, MMCCMD_DTRW_MASK, MMCCMD_DTRW_read); + } + } else { + reg = FIELD_SET(reg, msk, MMCCMD_WDATX_no); + } + + msk = MMCCMD_RSPFMT_MASK; + v = MMCCMD_RSPFMT_none; + if (cmd->cflags & MMCSD_CMD_F_RSP) { + if (cmd->cflags & MMCSD_CMD_F_LONG) { + v = MMCCMD_RSPFMT_136b; + } else if (cmd->cflags & MMCSD_CMD_F_CRC) { + v = MMCCMD_RSPFMT_48bCRC; + } else { + v = MMCCMD_RSPFMT_48b; + } + } + reg = FIELD_SET(reg, msk, v); + + reg = FIELD_SET(reg, MMCCMD_CMD_MASK, MMCCMD_CMD_VAL(idx)); + + mcon->MMCARGHL = cmd->arg; + mcon->MMCCMD = reg; + + if (cmd->cflags & MMCSD_CMD_F_DATA) { + DEBUG_LOG_SD("*** MMCCMD = 0x%.8X ***\n", mcon->MMCCMD); + } + + return SUCCESS; +} + +#define TRACK_SAMPLES 40000 +#define TRACK_SAVES 0x400 +t_mmcsd_cmd_state mmcsd_cmd_state(const MMCSD_con_t* mcon, am18x_bool need_crc) { +#if 1 + uint32_t reg; + + reg = mcon->MMCST0; + if (FIELD_GET(reg, MMCST0_RSPDNE_MASK) == MMCST0_RSPDNE_done) { + return MMCSD_SC_RSP_OK; + } + if (FIELD_GET(reg, MMCST0_CRCRS_MASK) == MMCST0_CRCRS_detected) { + return MMCSD_SC_CRC_ERR; + } + if (FIELD_GET(reg, MMCST0_TOUTRS_MASK) == MMCST0_TOUTRS_occurred) { + return MMCSD_SC_RSP_TOUT; + } +#else + { + uint32_t reg_tracks[TRACK_SAVES]; + int i, n = 0; + + reg_tracks[n++] = mcon->MMCST0; + for (i = 0; i < TRACK_SAMPLES; i++) { + if (reg_tracks[n - 1] != (reg_tracks[n] = mcon->MMCST0)) { + n++; + } + } + + DEBUG_LOG_SD("%s() \n", __func__); + for (i = 0; i < n; i++) { + DEBUG_LOG_SD("[%.3d] = 0x%.8X\n", i, reg_tracks[i]); + if (FIELD_GET(reg_tracks[i], MMCST0_RSPDNE_MASK) == MMCST0_RSPDNE_done + //|| FIELD_GET(reg_tracks[i], MMCST0_BSYDNE_MASK) == MMCST0_BSYDNE_done + ) { + return MMCSD_SC_RSP_OK; + } + if (FIELD_GET(reg_tracks[i], MMCST0_CRCRS_MASK) == MMCST0_CRCRS_detected) { + return MMCSD_SC_CRC_ERR; + } + if (FIELD_GET(reg_tracks[i], MMCST0_TOUTRS_MASK) == MMCST0_TOUTRS_occurred) { + return MMCSD_SC_RSP_TOUT; + } + } + } +#endif + return MMCSD_SC_NONE; +} + +t_mmcsd_dat_state mmcsd_busy_state(const MMCSD_con_t* mcon) { + return MMCSD_SD_OK; +} + +t_mmcsd_dat_state mmcsd_rd_state(const MMCSD_con_t* mcon) { +#if 1 + uint32_t reg; + + reg = mcon->MMCST0; + if (FIELD_GET(reg, MMCST0_CRCRD_MASK) == MMCST0_CRCRD_detected) { + return MMCSD_SD_CRC_ERR; + } + if (FIELD_GET(reg, MMCST0_TOUTRD_MASK) == MMCST0_TOUTRD_occurred) { + return MMCSD_SD_TOUT; + } + if (FIELD_GET(reg, MMCST0_DRRDY_MASK) == MMCST0_DRRDY_ready) { + return MMCSD_SD_RECVED; + } + if (FIELD_GET(reg, MMCST0_DATDNE_MASK) == MMCST0_DATDNE_done) { + return MMCSD_SD_OK; + } +#else + uint32_t reg_tracks[TRACK_SAVES]; + int i, n = 0; + + reg_tracks[n++] = mcon->MMCST0; + for (i = 0; i < TRACK_SAMPLES; i++) { + if (reg_tracks[n - 1] != (reg_tracks[n] = mcon->MMCST0)) { + n++; + } + } + + DEBUG_LOG_SD("%s() \n", __func__); + for (i = 0; i < n; i++) { + DEBUG_LOG_SD("[%.3d] = 0x%.8X\n", i, reg_tracks[i]); + } + for (i = 0; i < n; i++) { + if (FIELD_GET(reg_tracks[i], MMCST0_CRCRD_MASK) == MMCST0_CRCRD_detected) { + return MMCSD_SD_CRC_ERR; + } + if (FIELD_GET(reg_tracks[i], MMCST0_TOUTRD_MASK) == MMCST0_TOUTRD_occurred) { + return MMCSD_SD_TOUT; + } + if (FIELD_GET(reg_tracks[i], MMCST0_DRRDY_MASK) == MMCST0_DRRDY_ready) { + return MMCSD_SD_RECVED; + } + if (FIELD_GET(reg_tracks[i], MMCST0_DATDNE_MASK) == MMCST0_DATDNE_done) { + return MMCSD_SD_OK; + } + } +#endif + return MMCSD_SD_NONE; +} + +t_mmcsd_dat_state mmcsd_wr_state(const MMCSD_con_t* mcon) { +#if 1 + uint32_t reg; + + reg = mcon->MMCST0; + if (FIELD_GET(reg, MMCST0_CRCWR_MASK) == MMCST0_CRCWR_detected) { + return MMCSD_SD_CRC_ERR; + } + if (FIELD_GET(reg, MMCST0_DXRDY_MASK) == MMCST0_DXRDY_ready) { + return MMCSD_SD_SENT; + } + if (FIELD_GET(reg, MMCST0_DATDNE_MASK) == MMCST0_DATDNE_done) { + return MMCSD_SD_OK; + } +#else + uint32_t reg_tracks[TRACK_SAVES]; + int i, n = 0; + + reg_tracks[n++] = mcon->MMCST0; + for (i = 0; i < TRACK_SAMPLES; i++) { + if (reg_tracks[n - 1] != (reg_tracks[n] = mcon->MMCST0)) { + n++; + } + } + + DEBUG_LOG_SD("%s() \n", __func__); + for (i = 0; i < n; i++) { + DEBUG_LOG_SD("[%.3d] = 0x%.8X\n", i, reg_tracks[i]); + } + for (i = 0; i < n; i++) { + if (FIELD_GET(reg_tracks[i], MMCST0_CRCWR_MASK) == MMCST0_CRCWR_detected) { + return MMCSD_SD_CRC_ERR; + } + if (FIELD_GET(reg_tracks[i], MMCST0_DXRDY_MASK) == MMCST0_DXRDY_ready) { + return MMCSD_SD_SENT; + } + if (FIELD_GET(reg_tracks[i], MMCST0_DATDNE_MASK) == MMCST0_DATDNE_done) { + return MMCSD_SD_OK; + } + } +#endif + return MMCSD_SD_NONE; +} + +t_status mmcsd_get_resp(const MMCSD_con_t* mcon, t_mmcsd_resp_type type, t_mmcsd_resp* resp) { + int i; + + assert(mcon); + assert(resp); + + switch(type) { + case MMCSD_RESP_LONG: + for (i = 0; i < 4; i++) { + resp->v[i] = mcon->MMCRSP[i]; + } + break; + case MMCSD_RESP_SHORT: + default: + resp->v[0] = mcon->MMCRSP[3]; + break; + } + return SUCCESS; +} + +t_status mmcsd_cntl_misc(MMCSD_con_t* mcon, const t_mmcsd_misc* misc) { + uint32_t reg, msk, v; + + if (misc->mflags & MMCSD_MISC_F_BUS4BIT) { + // Initialize the MMC Control Register + reg = mcon->MMCCTL; + msk = MMCCTL_WIDTH0_MASK | MMCCTL_WIDTH1_MASK; + v = MMCCTL_WIDTH0_4bit | MMCCTL_WIDTH1_1_4bit; + mcon->MMCCTL = FIELD_SET(reg, msk, v); + return SUCCESS; + } + + // setting block count + if (misc->blkcnt) { + mcon->MMCNBLK = misc->blkcnt; + } + + // 7. Set the FIFO direction to transmit/receive + reg = 0; + msk = MMCFIFOCTL_FIFODIR_MASK; + if (misc->mflags & MMCSD_MISC_F_WRITE) { + v = MMCFIFOCTL_FIFODIR_write; + } else { + v = MMCFIFOCTL_FIFODIR_read; + } + reg = FIELD_SET(reg, msk, v); + + // 8. Set the access width + msk = MMCFIFOCTL_ACCWD_MASK; + reg = FIELD_SET(reg, msk, MMCFIFOCTL_ACCWD_4bytes); + + // 9. Set the FIFO threshold + msk = MMCFIFOCTL_FIFOLEV_MASK; + if (misc->mflags & MMCSD_MISC_F_FIFO_64B) { + reg = FIELD_SET(reg, msk, MMCFIFOCTL_FIFOLEV_64B); + } else { + reg = FIELD_SET(reg, msk, MMCFIFOCTL_FIFOLEV_32B); + } + + // 6. Reset the FIFO + // reg = mcon->MMCFIFOCTL; + msk = MMCFIFOCTL_FIFORST_MASK; + if (misc->mflags & MMCSD_MISC_F_FIFO_RST) { + mcon->MMCFIFOCTL = FIELD_SET(reg, msk, MMCFIFOCTL_FIFORST_reset); + } + + mcon->MMCFIFOCTL = reg; + + DEBUG_LOG_SD("*** MMCFIFOCTL = 0x%.8X ***\n", mcon->MMCFIFOCTL); + + reg = mcon->MMCIM; + msk = MMCIM_ECRCRS_MASK | MMCIM_ETOUTRS_MASK | MMCIM_ERSPDNE_MASK | MMCIM_EDATDNE_MASK; + v = MMCIM_ECRCRS_enabled | MMCIM_ETOUTRS_enabled | MMCIM_ERSPDNE_enabled | MMCIM_EDATDNE_enabled; + reg = FIELD_SET(reg, msk, v); + if (misc->mflags & MMCSD_MISC_F_WRITE) { + // 10. Enable the DXRDYINT interrupt + msk = MMCIM_EDXRDY_MASK | MMCIM_ECRCWR_MASK; + v = MMCIM_EDXRDY_enabled | MMCIM_ECRCWR_enabled; + } else { + // 11. Enable the DRRDYINT interrupt + msk = MMCIM_EDRRDY_MASK | MMCIM_ECRCRD_MASK | MMCIM_ETOUTRD_MASK; + v = MMCIM_EDRRDY_enabled | MMCIM_ECRCRD_enabled | MMCIM_ETOUTRD_enabled; + } + mcon->MMCIM = FIELD_SET(reg, msk, v); + + return SUCCESS; +} + +uint32_t mmcsd_read(const MMCSD_con_t* mcon) { + return mcon->MMCDRR; +} + +t_status mmcsd_write(MMCSD_con_t* mcon, uint32_t data) { + mcon->MMCDXR = data; + return SUCCESS; +} + +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/peripheral/per_mmcsd.h b/cpu/src/kernel/peripheral/per_mmcsd.h new file mode 100644 index 00000000..d47f3b1d --- /dev/null +++ b/cpu/src/kernel/peripheral/per_mmcsd.h @@ -0,0 +1,145 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file per_mmcsd.h + * + * @brief Public API for MMC/SD controller peripheral. + */ + +#ifndef PER_MMCSD_H +#define PER_MMCSD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +#include + +/*----- Macros -------------------------------------------------------*/ + +#define MMCSD_BLOCK_SIZE 0x200 +#define TIMEOUT_RSP_MAX 0xFFUL +#define TIMEOUT_DAT_MAX 0x3FFFFFFUL + +/*----- Typedefs -----------------------------------------------------*/ + +typedef struct { + uint32_t freq; + uint32_t timeout_rsp; + uint32_t timeout_dat; +} mmcsd_conf_t; + +typedef enum { + MMCSD_CMD_F_NORSP = 0, + MMCSD_CMD_F_RSP = BIT(1), + MMCSD_CMD_F_SHORT = 0, + MMCSD_CMD_F_LONG = BIT(2), + MMCSD_CMD_F_NOCRC = 0, + MMCSD_CMD_F_CRC = BIT(3), + MMCSD_CMD_F_NODATA = 0, + MMCSD_CMD_F_DATA = BIT(4), + MMCSD_CMD_F_READ = 0, + MMCSD_CMD_F_WRITE = BIT(5), + MMCSD_CMD_F_NOBUSY = 0, + MMCSD_CMD_F_BUSY = BIT(6), +} t_mmcsd_cflags; + +typedef struct { + uint8_t index; + uint32_t cflags; + uint32_t arg; +} t_mmcsd_cmd; + +typedef enum { + MMCSD_SC_NONE, + MMCSD_SC_RSP_OK, + MMCSD_SC_CRC_ERR, + MMCSD_SC_RSP_TOUT, +} t_mmcsd_cmd_state; + +typedef enum { + MMCSD_SD_NONE, + MMCSD_SD_SENT, + MMCSD_SD_RECVED, + MMCSD_SD_CRC_ERR, + MMCSD_SD_TOUT, + MMCSD_SD_OK, + MMCSD_SD_BUSY, + MMCSD_SD_DONE, +} t_mmcsd_dat_state; + +typedef struct { + uint32_t v[4]; +} t_mmcsd_resp; + +typedef enum { + MMCSD_RESP_SHORT, + MMCSD_RESP_LONG, +} t_mmcsd_resp_type; + +typedef enum { + MMCSD_MISC_F_FIFO_RST = BIT(0), + MMCSD_MISC_F_FIFO_32B = 0, + MMCSD_MISC_F_FIFO_64B = BIT(1), + MMCSD_MISC_F_READ = 0, + MMCSD_MISC_F_WRITE = BIT(2), + MMCSD_MISC_F_BUSY = BIT(3), + MMCSD_MISC_F_BUS4BIT = BIT(4), +} t_mmcsd_mflags; + +typedef struct { + uint16_t mflags; + uint16_t blkcnt; +} t_mmcsd_misc; + +/*----- Extern function prototypes -----------------------------------*/ + +t_status mmcsd_con_init(MMCSD_con_t* mcon, const mmcsd_conf_t* conf); +t_status mmcsd_set_freq(MMCSD_con_t* mcon, uint32_t freq); +t_status mmcsd_send_cmd(MMCSD_con_t* mcon, const t_mmcsd_cmd* cmd); +t_mmcsd_cmd_state mmcsd_cmd_state(const MMCSD_con_t* mcon, am18x_bool need_crc); +t_mmcsd_dat_state mmcsd_busy_state(const MMCSD_con_t* mcon); +t_mmcsd_dat_state mmcsd_rd_state(const MMCSD_con_t* mcon); +t_mmcsd_dat_state mmcsd_wr_state(const MMCSD_con_t* mcon); +t_status mmcsd_get_resp(const MMCSD_con_t* mcon, t_mmcsd_resp_type type, t_mmcsd_resp* resp); +t_status mmcsd_cntl_misc(MMCSD_con_t* mcon, const t_mmcsd_misc* misc); +uint32_t mmcsd_read(const MMCSD_con_t* mcon); +t_status mmcsd_write(MMCSD_con_t* mcon, uint32_t data); + + +#ifdef __cplusplus +} +#endif +#endif + +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/service/svc_diskio.c b/cpu/src/kernel/service/svc_diskio.c new file mode 100644 index 00000000..82e103b2 --- /dev/null +++ b/cpu/src/kernel/service/svc_diskio.c @@ -0,0 +1,162 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file svc_io.c. + * + * @brief Disk IO service layer. + */ + +/*----- Includes -----------------------------------------------------*/ + +#include +#include + +#include "macros.h" + +#include "dev_sdcard.h" +#include "svc_diskio.h" + +/*----- Macros -------------------------------------------------------*/ + +/*----- Typedefs -----------------------------------------------------*/ + +/*----- Extern variable definitions ----------------------------------*/ + +FATFS g_fatfs; +PARTITION VolToPart[FF_VOLUMES] = { { 0, 0 } }; + +/*----- Extern variable definitions ----------------------------------*/ + +/*----- Static function prototypes -----------------------------------*/ + +/*----- Extern function implementations ------------------------------*/ + +/** + * @brief Initialize Disk Drive. + * + * @param pdrv Physical drive number (0) + */ +DSTATUS disk_initialize(BYTE pdrv) { + DSTATUS stat; + int result; + + if (SDCARD_OK != dev_sdcard_init()) { + DEBUG_LOG("disk_initialize() dev_sdcard_init() failed"); + return STA_NODISK; + } + + return 0; +} + +/** + * @brief Returns the current status of a drive. + * + * @param drv Physical drive number (0) + */ +DSTATUS disk_status(BYTE drv) { + return 0; +} + +/** + * @brief Read sector(s) from the disk drive. + * + * @param drv Physical drive number (0) + * @param buff Pointer to the data buffer to store read data + * @param sector Start sector in LBA + * @param count Number of sectors to read + */ +DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) { + + DEBUG_LOG("disk_read(%i, %p, %i, %i)", (int)pdrv, (void*)buff, (int)sector, (int)count); + + uint32_t *buf_u32 = (uint32_t*)buff; // be aware of this + t_sdcard_status st = dev_sdcard_read(sector, count, buf_u32); + + if (SDCARD_OK != st) { + DEBUG_LOG("disk_read->dev_sdcard_read error: %i", (int)st); + return RES_ERROR; + } + + return RES_OK; +} + +/** + * @brief Write sector(s) to the disk drive. + * + * @param pdrv Physical drive number (0) + * @param buff Data to be written + * @param sector Start sector in LBA + * @param count Number of sectors to write + */ +DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) { + + DEBUG_LOG("disk_write(%i, %p, %i, %i)", (int)pdrv, (void*)buff, (int)sector, (int)count); + + const uint32_t *buf_u32 = (const uint32_t*)buff; // be aware of this + t_sdcard_status st = dev_sdcard_write(sector, count, buf_u32); + + if (SDCARD_OK != st) { + DEBUG_LOG("disk_write->dev_sdcard_write error: %i", (int)st); + } + + return RES_OK; +} + +/** + * @brief Control interface between SD card driver and FatFS. FatFS + * calls this function to inform itself about the SD card driver. + * + * @param pdrv Physical drive number (0) + * @param cmd Control code + * @param buff Buffer to send/receive control data + */ +DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) { + return 0; +} + +/** + * @brief Real time clock service to be called from FatFS module. + * Any valid time must be returned even if the system does + * not support a real time clock. + */ +DWORD get_fattime(void) { + // @TODO: implement RTC + return ((2007UL-1980) << 25) // Year 2007 + | (6UL << 21) // Month June + | (5UL << 16) // Day 5 + | (11U << 11) // Hour 11 + | (38U << 5) // Min 38 + | (0U >> 1); // Sec 0 +} + +/*----- Static function implementations ------------------------------*/ + +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/service/svc_diskio.h b/cpu/src/kernel/service/svc_diskio.h new file mode 100644 index 00000000..28c87e8c --- /dev/null +++ b/cpu/src/kernel/service/svc_diskio.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file svc_diskio.h + * + * @brief Public API for Disk IO service layer. + */ + +#ifndef SVC_IO_H +#define SVC_IO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +/*----- Macros -------------------------------------------------------*/ + +/*----- Typedefs -----------------------------------------------------*/ + +/*----- Extern function prototypes -----------------------------------*/ + + + +#ifdef __cplusplus +} +#endif +#endif + +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/system/ffconf.h b/cpu/src/kernel/system/ffconf.h new file mode 100644 index 00000000..671cf0fa --- /dev/null +++ b/cpu/src/kernel/system/ffconf.h @@ -0,0 +1,313 @@ +/*---------------------------------------------------------------------------/ +/ Configurations of FatFs Module +/---------------------------------------------------------------------------*/ + +#define FFCONF_DEF 80386 /* Revision ID */ + +/*---------------------------------------------------------------------------/ +/ Function Configurations +/---------------------------------------------------------------------------*/ + +#define FF_FS_READONLY 0 +/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) +/ Read-only configuration removes writing API functions, f_write(), f_sync(), +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() +/ and optional writing functions as well. */ + + +#define FF_FS_MINIMIZE 0 +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: Basic functions are fully enabled. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. +/ 3: f_lseek() function is removed in addition to 2. */ + + +#define FF_USE_FIND 0 +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ + + +#define FF_USE_MKFS 0 +/* This option switches f_mkfs(). (0:Disable or 1:Enable) */ + + +#define FF_USE_FASTSEEK 0 +/* This option switches fast seek feature. (0:Disable or 1:Enable) */ + + +#define FF_USE_EXPAND 0 +/* This option switches f_expand(). (0:Disable or 1:Enable) */ + + +#define FF_USE_CHMOD 0 +/* This option switches attribute control API functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ + + +#define FF_USE_LABEL 0 +/* This option switches volume label API functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + + +#define FF_USE_FORWARD 0 +/* This option switches f_forward(). (0:Disable or 1:Enable) */ + + +#define FF_USE_STRFUNC 0 +#define FF_PRINT_LLI 0 +#define FF_PRINT_FLOAT 0 +#define FF_STRF_ENCODE 0 +/* FF_USE_STRFUNC switches string API functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. +/ +/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 +/ makes f_printf() support floating point argument. These features want C99 or later. +/ When FF_LFN_UNICODE >= 1 with LFN enabled, string API functions convert the character +/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE +/ to be read/written via those functions. +/ +/ 0: ANSI/OEM in current CP +/ 1: Unicode in UTF-16LE +/ 2: Unicode in UTF-16BE +/ 3: Unicode in UTF-8 +*/ + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/---------------------------------------------------------------------------*/ + +#define FF_CODE_PAGE 932 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect code page setting can cause a file open failure. +/ +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) +/ 0 - Include all code pages above and configured by f_setcp() +*/ + + +#define FF_USE_LFN 1 +#define FF_MAX_LFN 255 +/* The FF_USE_LFN switches the support for LFN (long file name). +/ +/ 0: Disable LFN. FF_MAX_LFN has no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN feature +/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and +/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. +/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can +/ be in range of 12 to 255. It is recommended to be set 255 to fully support the LFN +/ specification. +/ When use stack for the working buffer, take care on stack overflow. When use heap +/ memory for the working buffer, memory management functions, ff_memalloc() and +/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ + + +#define FF_LFN_UNICODE 0 +/* This option switches the character encoding on the API when LFN is enabled. +/ +/ 0: ANSI/OEM in current CP (TCHAR = char) +/ 1: Unicode in UTF-16 (TCHAR = WCHAR) +/ 2: Unicode in UTF-8 (TCHAR = char) +/ 3: Unicode in UTF-32 (TCHAR = DWORD) +/ +/ Also behavior of string I/O functions will be affected by this option. +/ When LFN is not enabled, this option has no effect. */ + + +#define FF_LFN_BUF 255 +#define FF_SFN_BUF 12 +/* This set of options defines size of file name members in the FILINFO structure +/ which is used to read out directory items. These values should be suffcient for +/ the file names to read. The maximum possible length of the read file name depends +/ on character encoding. When LFN is not enabled, these options have no effect. */ + + +#define FF_FS_RPATH 0 +/* This option configures support for relative path feature. +/ +/ 0: Disable relative path and remove related API functions. +/ 1: Enable relative path and dot names. f_chdir() and f_chdrive() are available. +/ 2: f_getcwd() is available in addition to 1. +*/ + + +#define FF_PATH_DEPTH 10 +/* This option defines maximum depth of directory in the exFAT volume. It is NOT +/ relevant to FAT/FAT32 volume. +/ For example, FF_PATH_DEPTH = 3 will able to follow a path "/dir1/dir2/dir3/file" +/ but a sub-directory in the dir3 will not able to be followed and set current +/ directory. +/ The size of filesystem object (FATFS) increases FF_PATH_DEPTH * 24 bytes. +/ When FF_FS_EXFAT == 0 or FF_FS_RPATH == 0, this option has no effect. +*/ + + + +/*---------------------------------------------------------------------------/ +/ Drive/Volume Configurations +/---------------------------------------------------------------------------*/ + +#define FF_VOLUMES 1 +/* Number of volumes (logical drives) to be used. (1-10) */ + + +#define FF_STR_VOLUME_ID 0 +#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" +/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. +/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive +/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each +/ logical drive. Number of items must not be less than FF_VOLUMES. Valid +/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are +/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is +/ not defined, a user defined volume string table is needed as: +/ +/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... +*/ + + +#define FF_MULTI_PARTITION 1 +/* This option switches support for multiple volumes on the physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When this feature is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ will be available. */ + + +#define FF_MIN_SS 512 +#define FF_MAX_SS 512 +/* This set of options configures the range of sector size to be supported. (512, +/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and +/ harddisk, but a larger value may be required for on-board flash memory and some +/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is +/ configured for variable sector size mode and disk_ioctl() needs to implement +/ GET_SECTOR_SIZE command. */ + + +#define FF_LBA64 0 +/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) +/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ + + +#define FF_MIN_GPT 0x10000000 +/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs() and +/ f_fdisk(). 2^32 sectors maximum. This option has no effect when FF_LBA64 == 0. */ + + +#define FF_USE_TRIM 0 +/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) +/ To enable this feature, also CTRL_TRIM command should be implemented to +/ the disk_ioctl(). */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/---------------------------------------------------------------------------*/ + +#define FF_FS_TINY 0 +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is reduced FF_MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the filesystem object (FATFS) is used for the file data transfer. */ + + +#define FF_FS_EXFAT 0 +/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) +/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) +/ Note that enabling exFAT discards ANSI C (C89) compatibility. */ + + +#define FF_FS_NORTC 0 +#define FF_NORTC_MON 1 +#define FF_NORTC_MDAY 1 +#define FF_NORTC_YEAR 2025 +/* The option FF_FS_NORTC switches timestamp feature. If the system does not have +/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the +/ timestamp feature. Every object modified by FatFs will have a fixed timestamp +/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. +/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() need to be added +/ to the project to read current time form real-time clock. FF_NORTC_MON, +/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. +/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */ + + +#define FF_FS_CRTIME 0 +/* This option enables(1)/disables(0) the timestamp of the file created. When +/ set 1, the file created time is available in FILINFO structure. */ + + +#define FF_FS_NOFSINFO 0 +/* If you need to know the correct free space on the FAT32 volume, set bit 0 of +/ this option, and f_getfree() on the first time after volume mount will force +/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. +/ +/ bit0=0: Use free cluster count in the FSINFO if available. +/ bit0=1: Do not trust free cluster count in the FSINFO. +/ bit1=0: Use last allocated cluster number in the FSINFO if available. +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. +*/ + + +#define FF_FS_LOCK 0 +/* The option FF_FS_LOCK switches file lock function to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY +/ is 1. +/ +/ 0: Disable file lock function. To avoid volume corruption, application program +/ should avoid illegal open, remove and rename to the open objects. +/ >0: Enable file lock function. The value defines how many files/sub-directories +/ can be opened simultaneously under file lock control. Note that the file +/ lock control is independent of re-entrancy. */ + + +#define FF_FS_REENTRANT 0 +#define FF_FS_TIMEOUT 1000 +/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs +/ module itself. Note that regardless of this option, file access to different +/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() +/ and f_fdisk(), are always not re-entrant. Only file/directory access to +/ the same volume is under control of this featuer. +/ +/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect. +/ 1: Enable re-entrancy. Also user provided synchronization handlers, +/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give(), +/ must be added to the project. Samples are available in ffsystem.c. +/ +/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick. +*/ + + + +/*--- End of configuration options ---*/ diff --git a/cpu/src/main.c b/cpu/src/main.c index 23df9ace..73d788ac 100644 --- a/cpu/src/main.c +++ b/cpu/src/main.c @@ -38,6 +38,17 @@ under the terms of the GNU Affero General Public License as published by #include + +// BEGIN FreeRTOS +#include "FreeRTOS.h" +#include "task.h" +// END FreeRTOS + +// BEGIN FF +#include "ff.h" +#include "macros.h" +// END FF + #include "knl_main.h" #include "usr_main.h" @@ -55,6 +66,102 @@ under the terms of the GNU Affero General Public License as published by /*----- Extern function implementations ------------------------------*/ + + +DIR dir; // Directory object +FILINFO fno; // File information structure + +void list_root(void) { + FRESULT res; + + extern FATFS g_fatfs; + res = f_mount(&g_fatfs, "", 0); + if (FR_OK != res) { + DEBUG_LOG("f_mount failed: %i", (int)res); + return; + } + + // Open root directory + res = f_opendir(&dir, "/"); + if (res == FR_OK) { + for (;;) { + res = f_readdir(&dir, &fno); // Read next item + if (res != FR_OK || fno.fname[0] == 0) break; // Exit on error or end of dir + + if (fno.fattrib & AM_DIR) { + DEBUG_LOG("[DIR] %s", fno.fname); + } else { + DEBUG_LOG(" %s (%lu bytes)", fno.fname, (unsigned long)fno.fsize); + } + } + f_closedir(&dir); + } else { + DEBUG_LOG("Failed to open root directory (%d)", res); + } + + // FIL f; + // UINT bytes_written; + // res = f_open(&f, "/test.txt", FA_CREATE_ALWAYS | FA_WRITE); + // if (FR_OK != res) { + // DEBUG_LOG("f_open result: %i", res); + // return; + // } + // res = f_write(&f, "Hello World!", 12, &bytes_written); + // if (FR_OK != res) { + // DEBUG_LOG("f_write result: %i", res); + // return; + // } + // DEBUG_LOG("bytes written: %u", bytes_written); + // res = f_close(&f); + // if (FR_OK != res) { + // DEBUG_LOG("f_close result: %i", res); + // return; + // } +} + + +// static uint8_t s_buf[512]; + +// static void _print_test_block() { + +// uint32_t blk_nr = 8192; +// uint32_t blk_cnt = 1; + +// t_sdcard_status st = dev_sdcard_read(blk_nr, blk_cnt, (uint32_t*)s_buf); +// if (SDCARD_OK != st) { +// DEBUG_LOG("dev_sdcard_read error: %i", (int)st); +// return; +// } + +// for (int i = 0; i < 512/16; i++) { +// int o = 16*i; +// DEBUG_LOG("buf = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", +// (unsigned int)s_buf[o+0], +// (unsigned int)s_buf[o+1], +// (unsigned int)s_buf[o+2], +// (unsigned int)s_buf[o+3], + +// (unsigned int)s_buf[o+4], +// (unsigned int)s_buf[o+5], +// (unsigned int)s_buf[o+6], +// (unsigned int)s_buf[o+7], + +// (unsigned int)s_buf[o+8 ], +// (unsigned int)s_buf[o+9 ], +// (unsigned int)s_buf[o+10], +// (unsigned int)s_buf[o+11], + +// (unsigned int)s_buf[o+12], +// (unsigned int)s_buf[o+13], +// (unsigned int)s_buf[o+14], +// (unsigned int)s_buf[o+15] +// ); +// } + +// } + + + /** * @brief Run kernel and app. * @@ -65,6 +172,14 @@ int main(void) { knl_main_task(); } + + // dev_sdcard_init(); + // _print_test_block(); + + list_root(); + + + /// TODO: Implement scheduler. // while (true) {