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) {