diff --git a/firmware/SOURCES b/firmware/SOURCES index 1a064f5c77..08d81c6edc 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1777,9 +1777,6 @@ target/mips/ingenic_x1000/usb-x1000.c #if (CONFIG_STORAGE & (STORAGE_SD|STORAGE_MMC|STORAGE_ATA)) target/mips/ingenic_x1000/msc-x1000.c #endif -#if (CONFIG_STORAGE & STORAGE_SD) -target/mips/ingenic_x1000/sd-x1000.c -#endif #ifdef BOOTLOADER target/mips/ingenic_x1000/installer-x1000.c target/mips/ingenic_x1000/spl-start.S @@ -1821,6 +1818,7 @@ target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c +target/mips/ingenic_x1000/sdmmc-x1000-common.c target/mips/ingenic_x1000/spl-nand-x1000.c #endif /* FIIO_M3K */ @@ -1830,6 +1828,7 @@ target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c +target/mips/ingenic_x1000/sdmmc-x1000-common.c target/mips/ingenic_x1000/spl-nand-x1000.c #endif /* SHANLING_Q1 */ @@ -1839,6 +1838,7 @@ target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c target/mips/ingenic_x1000/erosqnative/button-erosqnative.c target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c target/mips/ingenic_x1000/erosqnative/power-erosqnative.c +target/mips/ingenic_x1000/sdmmc-x1000-common.c target/mips/ingenic_x1000/spl-nand-x1000.c #endif /* EROS_QN */ diff --git a/firmware/export/config/erosqnative.h b/firmware/export/config/erosqnative.h index cfba1b8ff2..e90670fac2 100644 --- a/firmware/export/config/erosqnative.h +++ b/firmware/export/config/erosqnative.h @@ -95,6 +95,9 @@ #define STORAGE_WANTS_ALIGN #define STORAGE_NEEDS_BOUNCE_BUFFER +/* One SD card slot */ +#define SDMMC_HOST_NUM_SD_CONTROLLERS 1 + /* Power management */ #define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE | PERCENTAGE_MEASURE/*|CURRENT_MEASURE*/) #define CONFIG_CHARGING CHARGING_MONITOR diff --git a/firmware/export/config/fiiom3k.h b/firmware/export/config/fiiom3k.h index baf633be70..1093d91a31 100644 --- a/firmware/export/config/fiiom3k.h +++ b/firmware/export/config/fiiom3k.h @@ -84,6 +84,9 @@ #define STORAGE_WANTS_ALIGN #define STORAGE_NEEDS_BOUNCE_BUFFER +/* One SD card slot */ +#define SDMMC_HOST_NUM_SD_CONTROLLERS 1 + /* RTC settings */ #define CONFIG_RTC RTC_X1000 /* TODO: implement HAVE_RTC_ALARM */ diff --git a/firmware/export/config/shanlingq1.h b/firmware/export/config/shanlingq1.h index 289f090187..6b8ef7fb16 100644 --- a/firmware/export/config/shanlingq1.h +++ b/firmware/export/config/shanlingq1.h @@ -67,6 +67,9 @@ #define STORAGE_WANTS_ALIGN #define STORAGE_NEEDS_BOUNCE_BUFFER +/* One SD card slot */ +#define SDMMC_HOST_NUM_SD_CONTROLLERS 1 + /* RTC settings */ #define CONFIG_RTC RTC_X1000 /* TODO: implement HAVE_RTC_ALARM */ diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.c b/firmware/target/mips/ingenic_x1000/msc-x1000.c index 0ce6ff4990..37ea53340d 100644 --- a/firmware/target/mips/ingenic_x1000/msc-x1000.c +++ b/firmware/target/mips/ingenic_x1000/msc-x1000.c @@ -6,7 +6,7 @@ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * - * Copyright (C) 2021 Aidan MacDonald + * Copyright (C) 2021-2026 Aidan MacDonald * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,7 +17,6 @@ * KIND, either express or implied. * ****************************************************************************/ - #include "system.h" #include "panic.h" #include "led.h" @@ -30,901 +29,368 @@ #include #include -/* #define LOGF_ENABLE */ -#include "logf.h" - -/* TODO - this needs some auditing to better handle errors - * - * There should be a clearer code path involving errors. Especially we should - * ensure that removing the card always resets the driver to a sane state. +/* + * TODO: Clock helpers could be factored out of this file */ -#define DEBOUNCE_TIME (HZ/10) - -static const msc_config msc_configs[] = { -#if defined(FIIO_M3K) -#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A - { - .msc_nr = 0, - .msc_type = MSC_TYPE_SD, - .bus_width = 4, - .label = "microSD", - .cd_gpio = GPIO_MSC0_CD, - .cd_active_level = 0, - }, -#elif defined(SHANLING_Q1) -#define MSC_CLOCK_SOURCE X1000_CLK_MPLL - { - .msc_nr = 0, - .msc_type = MSC_TYPE_SD, - .bus_width = 4, - .label = "microSD", - .cd_gpio = GPIO_MSC0_CD, - .cd_active_level = 0, - }, - /* NOTE: SDIO wifi card is on msc1 */ -#elif defined(EROS_QN) -#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A - { - .msc_nr = 0, - .msc_type = MSC_TYPE_SD, - .bus_width = 4, - .label = "microSD", - .cd_gpio = GPIO_MSC0_CD, - .cd_active_level = 0, - }, -#else -# error "Please add X1000 MSC config" -#endif - {.msc_nr = -1}, -}; - -static const msc_config* msc_lookup_config(int msc) +static uint32_t get_msc_clkgr_bit(int msc) { - for(int i = 0; i < MSC_COUNT; ++i) - if(msc_configs[i].msc_nr == msc) - return &msc_configs[i]; - return NULL; + if (msc == 0) + return BM_CPM_CLKGR_MSC0; + else + return BM_CPM_CLKGR_MSC1; } -static msc_drv msc_drivers[MSC_COUNT]; - -static void msc0_cd_interrupt(void); -static void msc1_cd_interrupt(void); - -/* --------------------------------------------------------------------------- - * Initialization - */ +static bool msc_is_gated(int msc) +{ + return REG_CPM_CLKGR & get_msc_clkgr_bit(msc); +} static void msc_gate_clock(int msc, bool gate) { - int bit; - if(msc == 0) - bit = BM_CPM_CLKGR_MSC0; + if (gate) + REG_CPM_CLKGR |= get_msc_clkgr_bit(msc); else - bit = BM_CPM_CLKGR_MSC1; - - if(gate) - REG_CPM_CLKGR |= bit; - else - REG_CPM_CLKGR &= ~bit; + REG_CPM_CLKGR &= ~get_msc_clkgr_bit(msc); } -static void msc_init_one(msc_drv* d, int msc) +static void msc_set_clock(int msc, uint32_t src_freq, uint32_t bus_freq) { - /* Lookup config */ - d->drive_nr = -1; - d->config = msc_lookup_config(msc); - if(!d->config) { - d->msc_nr = -1; - return; - } - - /* Initialize driver state */ - d->msc_nr = msc; - d->driver_flags = 0; - d->clk_status = 0; - d->cmdat_def = jz_orf(MSC_CMDAT, RTRG_V(GE32), TTRG_V(LE32)); - d->req = NULL; - d->iflag_done = 0; - d->card_present = 1; - d->card_present_last = 1; - d->req_running = 0; - mutex_init(&d->lock); - semaphore_init(&d->cmd_done, 1, 0); - - /* Ensure correct clock source */ - jz_writef(CPM_MSC0CDR, CE(1), CLKDIV(0), - CLKSRC(MSC_CLOCK_SOURCE == X1000_CLK_MPLL ? 1 : 0)); - while(jz_readf(CPM_MSC0CDR, BUSY)); - jz_writef(CPM_MSC0CDR, CE(0)); - - /* Initialize the hardware */ - msc_gate_clock(msc, false); - msc_full_reset(d); - system_enable_irq(msc == 0 ? IRQ_MSC0 : IRQ_MSC1); - - /* Setup the card detect IRQ */ - if(d->config->cd_gpio != GPIO_NONE) { - if(gpio_get_level(d->config->cd_gpio) != d->config->cd_active_level) { - d->card_present = 0; - d->card_present_last = 0; - } - - system_set_irq_handler(GPIO_TO_IRQ(d->config->cd_gpio), - msc == 0 ? msc0_cd_interrupt : msc1_cd_interrupt); - gpio_set_function(d->config->cd_gpio, GPIOF_IRQ_EDGE(1)); - gpio_flip_edge_irq(d->config->cd_gpio); - gpio_enable_irq(d->config->cd_gpio); - } -} - -void msc_init(void) -{ - /* Only do this once -- each storage subsystem calls us in its init */ - static bool done = false; - if(done) - return; - done = true; - - /* Set up each MSC driver according to msc_configs */ - for(int i = 0; i < MSC_COUNT; ++i) - msc_init_one(&msc_drivers[i], i); -} - -msc_drv* msc_get(int type, int index) -{ - for(int i = 0, m = 0; i < MSC_COUNT; ++i) { - if(msc_drivers[i].config == NULL) - continue; - if(type == MSC_TYPE_ANY || msc_drivers[i].config->msc_type == type) - if(index == m++) - return &msc_drivers[i]; - } - - return NULL; -} - -msc_drv* msc_get_by_drive(int drive_nr) -{ - for(int i = 0; i < MSC_COUNT; ++i) - if(msc_drivers[i].drive_nr == drive_nr) - return &msc_drivers[i]; - return NULL; -} - -void msc_lock(msc_drv* d) -{ - mutex_lock(&d->lock); -} - -void msc_unlock(msc_drv* d) -{ - mutex_unlock(&d->lock); -} - -void msc_full_reset(msc_drv* d) -{ - msc_lock(d); - msc_set_clock_mode(d, MSC_CLK_AUTOMATIC); - msc_set_speed(d, MSC_SPEED_INIT); - msc_set_width(d, 1); - msc_ctl_reset(d); - d->driver_flags = 0; - memset(&d->cardinfo, 0, sizeof(tCardInfo)); - msc_unlock(d); -} - -bool msc_card_detect(msc_drv* d) -{ - if(d->config->cd_gpio == GPIO_NONE) - return true; - - return gpio_get_level(d->config->cd_gpio) == d->config->cd_active_level; -} - -void msc_led_trigger(void) -{ - bool state = false; - for(int i = 0; i < MSC_COUNT; ++i) - if(msc_drivers[i].req_running) - state = true; - - led(state); -} - -/* --------------------------------------------------------------------------- - * Controller API - */ - -void msc_ctl_reset(msc_drv* d) -{ - /* Ingenic code suggests a reset changes clkrt */ - int clkrt = REG_MSC_CLKRT(d->msc_nr); - - /* Send reset -- bit is NOT self clearing */ - jz_overwritef(MSC_CTRL(d->msc_nr), RESET(1)); - udelay(100); - jz_writef(MSC_CTRL(d->msc_nr), RESET(0)); - - /* Verify reset in the status register */ - long deadline = current_tick + HZ; - while(jz_readf(MSC_STAT(d->msc_nr), IS_RESETTING) && - current_tick < deadline) { - sleep(1); - } - - /* Ensure the clock state is as expected */ - if(d->clk_status & MSC_CLKST_AUTO) - jz_writef(MSC_LPM(d->msc_nr), ENABLE(1)); - else if(d->clk_status & MSC_CLKST_ENABLE) - jz_overwritef(MSC_CTRL(d->msc_nr), CLOCK_V(START)); - else - jz_overwritef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP)); - - /* Clear and mask interrupts */ - REG_MSC_IMASK(d->msc_nr) = 0xffffffff; - REG_MSC_IFLAG(d->msc_nr) = 0xffffffff; - - /* Restore clkrt */ - REG_MSC_CLKRT(d->msc_nr) = clkrt; -} - -void msc_set_clock_mode(msc_drv* d, int mode) -{ - int cur_mode = (d->clk_status & MSC_CLKST_AUTO) ? MSC_CLK_AUTOMATIC - : MSC_CLK_MANUAL; - if(mode == cur_mode) - return; - - d->clk_status &= ~MSC_CLKST_ENABLE; - if(mode == MSC_CLK_AUTOMATIC) { - d->clk_status |= MSC_CLKST_AUTO; - jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP)); - jz_writef(MSC_LPM(d->msc_nr), ENABLE(1)); - } else { - d->clk_status &= ~MSC_CLKST_AUTO; - jz_writef(MSC_LPM(d->msc_nr), ENABLE(0)); - jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP)); - } -} - -void msc_enable_clock(msc_drv* d, bool enable) -{ - if(d->clk_status & MSC_CLKST_AUTO) - return; - - bool is_enabled = (d->clk_status & MSC_CLKST_ENABLE); - if(enable == is_enabled) - return; - - if(enable) { - jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(START)); - d->clk_status |= MSC_CLKST_ENABLE; - } else { - jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP)); - d->clk_status &= ~MSC_CLKST_ENABLE; - } -} - -void msc_set_speed(msc_drv* d, int rate) -{ - /* Shut down clock while we change frequencies */ - if(d->clk_status & MSC_CLKST_ENABLE) - jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(STOP)); - - /* Wait for clock to go idle */ - while(jz_readf(MSC_STAT(d->msc_nr), CLOCK_EN)) + /* Wait for clock to go idle from any ongoing operation */ + while (jz_readf(MSC_STAT(msc), CLOCK_EN)) sleep(1); - /* freq1 is output by MSCxDIV; freq2 is output by MSC_CLKRT */ - uint32_t freq1 = rate; - uint32_t freq2 = rate; - if(freq1 < MSC_SPEED_FAST) - freq1 = MSC_SPEED_FAST; - - /* Handle MSCxDIV */ - uint32_t src_freq = clk_get(MSC_CLOCK_SOURCE) / 2; - uint32_t div = clk_calc_div(src_freq, freq1); - if(d->msc_nr == 0) { + /* + * Handle MSCxDIV, the max CLKRT division factor is 1/128 + * which allows us to get a 400 KHz clock from up to 50 MHz + * so there is no point using a lower MSCxDIV clock. + */ + uint32_t mscdiv_freq = MAX(bus_freq, 50000000); + uint32_t div = clk_calc_div(src_freq / 2, mscdiv_freq); + if (msc == 0) + { jz_writef(CPM_MSC0CDR, CE(1), CLKDIV(div - 1)); while(jz_readf(CPM_MSC0CDR, BUSY)); jz_writef(CPM_MSC0CDR, CE(0)); - } else { + } + else + { jz_writef(CPM_MSC1CDR, CE(1), CLKDIV(div - 1)); while(jz_readf(CPM_MSC1CDR, BUSY)); jz_writef(CPM_MSC1CDR, CE(0)); } /* Handle MSC_CLKRT */ - uint32_t clkrt = clk_calc_shift(src_freq/div, freq2); - REG_MSC_CLKRT(d->msc_nr) = clkrt; + uint32_t clkrt = clk_calc_shift(src_freq / (2 * div), bus_freq); + REG_MSC_CLKRT(msc) = clkrt; - /* Handle frequency dependent timing settings + /* + * Handle frequency dependent timing settings * TODO - these settings might be SD specific... */ - uint32_t out_freq = (src_freq/div) >> clkrt; - if(out_freq > MSC_SPEED_FAST) { - jz_writef(MSC_LPM(d->msc_nr), + uint32_t out_freq = (src_freq / (2 * div)) >> clkrt; + if (out_freq > 25000000) + { + jz_writef(MSC_LPM(msc), DRV_SEL_V(RISE_EDGE_DELAY_QTR_PHASE), SMP_SEL_V(RISE_EDGE_DELAYED)); - jz_writef(MSC_CTRL2(d->msc_nr), SPEED_V(HIGHSPEED)); - } else { - jz_writef(MSC_LPM(d->msc_nr), + jz_writef(MSC_CTRL2(msc), SPEED_V(HIGHSPEED)); + } + else + { + jz_writef(MSC_LPM(msc), DRV_SEL_V(FALL_EDGE), SMP_SEL_V(RISE_EDGE)); - jz_writef(MSC_CTRL2(d->msc_nr), SPEED_V(DEFAULT)); + jz_writef(MSC_CTRL2(msc), SPEED_V(DEFAULT)); } - - /* Restart clock if it was running before */ - if(d->clk_status & MSC_CLKST_ENABLE) - jz_writef(MSC_CTRL(d->msc_nr), CLOCK_V(START)); } -void msc_set_width(msc_drv* d, int width) +void x1000_msc_init(struct x1000_msc_controller* ctl, + struct x1000_msc_dma_desc *dma_desc, + int msc_nr, uint32_t src_clk_freq) { - /* Bus width is controlled per command with MSC_CMDAT. */ - if(width == 8) - jz_vwritef(d->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(8BIT)); - else if(width == 4) - jz_vwritef(d->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(4BIT)); + memset(ctl, 0, sizeof(*ctl)); + + ctl->msc_nr = msc_nr; + ctl->src_clk_freq = src_clk_freq; + ctl->dma_desc = dma_desc; + ctl->cmdat_def = jz_orf(MSC_CMDAT, RTRG_V(GE32), TTRG_V(LE32), INIT(1)); + + semaphore_init(&ctl->sem, 1, 0); + + /* Start off gated, sdmmc_host will power us on */ + msc_gate_clock(ctl->msc_nr, true); +} + +void x1000_msc_set_power_enabled(void *controller, bool enabled) +{ + struct x1000_msc_controller *ctl = controller; + + if (enabled) + { + msc_gate_clock(ctl->msc_nr, false); + + /* Controller reset */ + jz_overwritef(MSC_CTRL(ctl->msc_nr), RESET(1)); + udelay(100); + jz_writef(MSC_CTRL(ctl->msc_nr), RESET(0)); + while (jz_readf(MSC_STAT(ctl->msc_nr), IS_RESETTING)); + + /* Ensure interrupt state is clear */ + REG_MSC_IMASK(ctl->msc_nr) = 0xFFFFFFFFu; + REG_MSC_IFLAG(ctl->msc_nr) = 0xFFFFFFFFu; + + /* Set requested bus clock frequency */ + msc_set_clock(ctl->msc_nr, ctl->src_clk_freq, + sdmmc_host_get_bus_freq(ctl->bus_clock)); + + /* Set INIT bit and enable auto clock management */ + ctl->cmdat_def |= BM_MSC_CMDAT_INIT; + jz_writef(MSC_LPM(ctl->msc_nr), ENABLE(1)); + } else - jz_vwritef(d->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(1BIT)); -} + { + /* Disable and gate clock */ + jz_writef(MSC_LPM(ctl->msc_nr), ENABLE(0)); + jz_writef(MSC_CTRL(ctl->msc_nr), CLOCK_V(STOP)); -/* --------------------------------------------------------------------------- - * Request API - */ - -/* Note -- this must only be called with IRQs disabled */ -static void msc_finish_request(msc_drv* d, int status) -{ - REG_MSC_IMASK(d->msc_nr) = 0xffffffff; - REG_MSC_IFLAG(d->msc_nr) = 0xffffffff; - if(d->req->flags & MSC_RF_DATA) - jz_writef(MSC_DMAC(d->msc_nr), ENABLE(0)); - - d->req->status = status; - d->req_running = 0; - d->iflag_done = 0; - - msc_led_trigger(); - timeout_cancel(&d->cmd_tmo); - semaphore_release(&d->cmd_done); -} - -static int msc_req_timeout(struct timeout* tmo) -{ - msc_drv* d = (msc_drv*)tmo->data; - msc_async_abort(d, MSC_REQ_LOCKUP); - return 0; -} - -void msc_async_start(msc_drv* d, msc_req* r) -{ - /* Determined needed cmdat and interrupts */ - unsigned cmdat = d->cmdat_def; - d->iflag_done = jz_orm(MSC_IFLAG, END_CMD_RES); - - cmdat |= jz_orf(MSC_CMDAT, RESP_FMT(r->resptype & ~MSC_RESP_BUSY)); - if(r->resptype & MSC_RESP_BUSY) - cmdat |= jz_orm(MSC_CMDAT, BUSY); - - if(r->flags & MSC_RF_INIT) - cmdat |= jz_orm(MSC_CMDAT, INIT); - - if(r->flags & MSC_RF_DATA) { - cmdat |= jz_orm(MSC_CMDAT, DATA_EN); - if(r->flags & MSC_RF_PROG) - d->iflag_done = jz_orm(MSC_IFLAG, WR_ALL_DONE); - else - d->iflag_done = jz_orm(MSC_IFLAG, DMA_DATA_DONE); + msc_gate_clock(ctl->msc_nr, true); } +} - if(r->flags & MSC_RF_WRITE) - cmdat |= jz_orm(MSC_CMDAT, WRITE_READ); +void x1000_msc_set_bus_width(void *controller, uint32_t width) +{ + struct x1000_msc_controller *ctl = controller; - if(r->flags & MSC_RF_AUTO_CMD12) - cmdat |= jz_orm(MSC_CMDAT, AUTO_CMD12); + switch (width) + { + case SDMMC_BUS_WIDTH_1BIT: + jz_vwritef(ctl->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(1BIT)); + break; - if(r->flags & MSC_RF_ABORT) - cmdat |= jz_orm(MSC_CMDAT, IO_ABORT); + case SDMMC_BUS_WIDTH_4BIT: + jz_vwritef(ctl->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(4BIT)); + break; - unsigned imask = jz_orm(MSC_IMASK, + case SDMMC_BUS_WIDTH_8BIT: + jz_vwritef(ctl->cmdat_def, MSC_CMDAT, BUS_WIDTH_V(8BIT)); + break; + + default: + panicf("%s", __func__); + } +} + +void x1000_msc_set_bus_clock(void *controller, uint32_t clock) +{ + struct x1000_msc_controller *ctl = controller; + uint32_t bus_freq = sdmmc_host_get_bus_freq(clock); + + ctl->bus_clock = clock; + + if (!msc_is_gated(ctl->msc_nr)) + msc_set_clock(ctl->msc_nr, ctl->src_clk_freq, bus_freq); +} + +int x1000_msc_submit_command(void *controller, + const struct sdmmc_host_command *cmd, + struct sdmmc_host_response *resp) +{ + struct x1000_msc_controller *ctl = controller; + uint32_t cmdat = ctl->cmdat_def; + uint32_t imask = jz_orm(MSC_IMASK, CRC_RES_ERROR, CRC_READ_ERROR, CRC_WRITE_ERROR, TIME_OUT_RES, TIME_OUT_READ, END_CMD_RES); - imask |= d->iflag_done; - /* Program the controller */ - if(r->flags & MSC_RF_DATA) { - REG_MSC_NOB(d->msc_nr) = r->nr_blocks; - REG_MSC_BLKLEN(d->msc_nr) = r->block_len; + void *buff_addr = cmd->buffer; + size_t buff_size = cmd->nr_blocks * cmd->block_len; + + /* INIT is only sent for the first command after power up */ + jz_vwritef(ctl->cmdat_def, MSC_CMDAT, INIT(0)); + + /* Response type setting */ + if (cmd->flags & SDMMC_RESP_BUSY) + jz_vwritef(cmdat, MSC_CMDAT, BUSY(1)); + + switch (SDMMC_RESP_LENGTH(cmd->flags)) + { + case SDMMC_RESP_NONE: + jz_vwritef(cmdat, MSC_CMDAT, RESP_FMT(0)); + + ctl->resp_len = 0; + break; + + case SDMMC_RESP_SHORT: + if (cmd->flags & SDMMC_RESP_NOCRC) + jz_vwritef(cmdat, MSC_CMDAT, RESP_FMT(3)); + else + jz_vwritef(cmdat, MSC_CMDAT, RESP_FMT(1)); + + ctl->resp_len = 1; + break; + + case SDMMC_RESP_LONG: + jz_vwritef(cmdat, MSC_CMDAT, RESP_FMT(2)); + ctl->resp_len = 4; + break; + + default: + panicf("%s", __func__); + break; } - REG_MSC_CMD(d->msc_nr) = r->command; - REG_MSC_ARG(d->msc_nr) = r->argument; - REG_MSC_CMDAT(d->msc_nr) = cmdat; + /* Data transfer setup */ + if (SDMMC_DATA_PRESENT(cmd->flags)) + { + if ((uintptr_t)buff_addr & (CACHEALIGN_SIZE - 1)) + panicf("%s: unaligned buffer", __func__); - REG_MSC_IFLAG(d->msc_nr) = imask; - REG_MSC_IMASK(d->msc_nr) &= ~imask; + ctl->dma_desc->nda = 0; + ctl->dma_desc->mem = PHYSADDR(buff_addr); + ctl->dma_desc->len = buff_size; + ctl->dma_desc->cmd = 2; /* ID=0, ENDI=1, LINK=0 */ + commit_dcache_range(ctl->dma_desc, sizeof(*ctl->dma_desc)); - if(r->flags & MSC_RF_DATA) { - d->dma_desc.nda = 0; - d->dma_desc.mem = PHYSADDR(r->data); - d->dma_desc.len = r->nr_blocks * r->block_len; - d->dma_desc.cmd = 2; /* ID=0, ENDI=1, LINK=0 */ - commit_dcache_range(&d->dma_desc, sizeof(d->dma_desc)); + if (SDMMC_DATA_DIR(cmd->flags) == SDMMC_DATA_WRITE) + { + commit_dcache_range(buff_addr, buff_size); - if(r->flags & MSC_RF_WRITE) - commit_dcache_range(r->data, d->dma_desc.len); + jz_vwritef(cmdat, MSC_CMDAT, WRITE_READ(1)); + ctl->iflag_done = jz_orm(MSC_IMASK, WR_ALL_DONE); + } else - discard_dcache_range(r->data, d->dma_desc.len); + { + discard_dcache_range(buff_addr, buff_size); - /* Unaligned address for DMA doesn't seem to work correctly. - * FAT FS driver should ensure proper alignment of all buffers, - * so in practice this panic should not occur, but if it does - * I want to hear about it. */ - if(UNLIKELY(d->dma_desc.mem & 3)) { - panicf("msc%d bad align: %08x", d->msc_nr, - (unsigned)d->dma_desc.mem); + ctl->iflag_done = jz_orm(MSC_IMASK, DMA_DATA_DONE); } - jz_writef(MSC_DMAC(d->msc_nr), MODE_SEL(0), INCR(0), DMASEL(0)); - REG_MSC_DMANDA(d->msc_nr) = PHYSADDR(&d->dma_desc); + jz_vwritef(cmdat, MSC_CMDAT, DATA_EN(1)); + + jz_writef(MSC_DMAC(ctl->msc_nr), MODE_SEL(0), INCR(0), DMASEL(0)); + REG_MSC_DMANDA(ctl->msc_nr) = PHYSADDR(ctl->dma_desc); + } + else + { + /* + * PROG_DONE is actually waiting for the busy signal so is + * required for all commands with R1b response (like CMD12). + * For writes, the WR_ALL_DONE interrupt subsumes PROG_DONE. + */ + if (cmd->flags & SDMMC_RESP_BUSY) + ctl->iflag_done = jz_orm(MSC_IMASK, PROG_DONE); + else + ctl->iflag_done = jz_orm(MSC_IMASK, END_CMD_RES); } - /* Begin processing */ - d->req = r; - d->req_running = 1; - msc_led_trigger(); - jz_writef(MSC_CTRL(d->msc_nr), START_OP(1)); - if(r->flags & MSC_RF_DATA) - jz_writef(MSC_DMAC(d->msc_nr), ENABLE(1)); + REG_MSC_NOB(ctl->msc_nr) = cmd->nr_blocks; + REG_MSC_BLKLEN(ctl->msc_nr) = cmd->block_len; + REG_MSC_CMD(ctl->msc_nr) = cmd->command; + REG_MSC_ARG(ctl->msc_nr) = cmd->argument; + REG_MSC_CMDAT(ctl->msc_nr) = cmdat; - /* TODO: calculate a suitable lower value for the lockup timeout. - * - * The SD spec defines timings based on the number of blocks transferred, - * see sec. 4.6.2 "Read, write, and erase timeout conditions". This should - * reduce the long delays which happen if errors occur. - * - * Also need to check if registers MSC_RDTO / MSC_RESTO are correctly set. - */ - timeout_register(&d->cmd_tmo, msc_req_timeout, 10*HZ, (intptr_t)d); + imask |= ctl->iflag_done; + REG_MSC_IFLAG(ctl->msc_nr) = imask; + REG_MSC_IMASK(ctl->msc_nr) &= ~imask; + + ctl->resp = resp; + ctl->err_code = SDMMC_STATUS_OK; + membarrier(); + + jz_writef(MSC_CTRL(ctl->msc_nr), START_OP(1)); + if (SDMMC_DATA_PRESENT(cmd->flags)) + jz_writef(MSC_DMAC(ctl->msc_nr), ENABLE(1)); + + semaphore_wait(&ctl->sem, TIMEOUT_BLOCK); + + if (SDMMC_DATA_PRESENT(cmd->flags) && + SDMMC_DATA_DIR(cmd->flags) == SDMMC_DATA_READ) + discard_dcache_range(buff_addr, buff_size); + + return ctl->err_code; } -void msc_async_abort(msc_drv* d, int status) +static void x1000_msc_finish_command(struct x1000_msc_controller *ctl) { + REG_MSC_IMASK(ctl->msc_nr) = 0xFFFFFFFFu; + REG_MSC_IFLAG(ctl->msc_nr) = 0xFFFFFFFFu; + jz_writef(MSC_DMAC(ctl->msc_nr), ENABLE(0)); + semaphore_release(&ctl->sem); +} + +void x1000_msc_abort_command(void *controller) +{ + struct x1000_msc_controller *ctl = controller; int irq = disable_irq_save(); - if(d->req_running) { - logf("msc%d: async abort status:%d", d->msc_nr, status); - msc_finish_request(d, status); - } + + /* + * Looks strange but this wait is always OK because it either + * acquires the semaphore of an idle controller or is a no-op + * if there is a command running. + * + * Finishing the command then releases the semaphore which is + * correct for both cases. + */ + semaphore_wait(&ctl->sem, TIMEOUT_NOBLOCK); + + ctl->err_code = SDMMC_STATUS_ERROR; + x1000_msc_finish_command(ctl); restore_irq(irq); } -int msc_async_wait(msc_drv* d, int timeout) +static void x1000_msc_read_response(struct x1000_msc_controller *ctl) { - if(semaphore_wait(&d->cmd_done, timeout) == OBJ_WAIT_TIMEDOUT) - return MSC_REQ_INCOMPLETE; + uint32_t res = REG_MSC_RES(ctl->msc_nr); + uint32_t dat; - return d->req->status; -} - -int msc_request(msc_drv* d, msc_req* r) -{ - msc_async_start(d, r); - return msc_async_wait(d, TIMEOUT_BLOCK); -} - -/* --------------------------------------------------------------------------- - * Command response handling - */ - -static void msc_read_response(msc_drv* d) -{ - unsigned res = REG_MSC_RES(d->msc_nr); - unsigned dat; - switch(d->req->resptype) { - case MSC_RESP_R1: - case MSC_RESP_R1B: - case MSC_RESP_R3: - case MSC_RESP_R6: - case MSC_RESP_R7: - dat = res << 24; - res = REG_MSC_RES(d->msc_nr); - dat |= res << 8; - res = REG_MSC_RES(d->msc_nr); - dat |= res & 0xff; - d->req->response[0] = dat; - break; - - case MSC_RESP_R2: - for(int i = 0; i < 4; ++i) { + if (ctl->resp_len == 4) + { + for (int i = 0; i < 4; ++i) + { dat = res << 24; - res = REG_MSC_RES(d->msc_nr); + res = REG_MSC_RES(ctl->msc_nr); dat |= res << 8; - res = REG_MSC_RES(d->msc_nr); + res = REG_MSC_RES(ctl->msc_nr); dat |= res >> 8; - d->req->response[i] = dat; - } - break; - - default: - return; - } -} - -static int msc_check_sd_response(msc_drv* d) -{ - if(d->req->resptype == MSC_RESP_R1 || - d->req->resptype == MSC_RESP_R1B) { - if(d->req->response[0] & SD_R1_CARD_ERROR) { - logf("msc%d: R1 card error: %08x", d->msc_nr, d->req->response[0]); - return MSC_REQ_CARD_ERR; + if (ctl->resp) + ctl->resp->data[i] = dat; } } + else if (ctl->resp_len == 1) + { + dat = res << 24; + res = REG_MSC_RES(ctl->msc_nr); + dat |= res << 8; + res = REG_MSC_RES(ctl->msc_nr); + dat |= res & 0xff; - return MSC_REQ_SUCCESS; -} - -static int msc_check_response(msc_drv* d) -{ - switch(d->config->msc_type) { - case MSC_TYPE_SD: - return msc_check_sd_response(d); - default: - /* TODO - implement msc_check_response for MMC and CE-ATA */ - return 0; + if (ctl->resp) + ctl->resp->data[0] = dat; } } -/* --------------------------------------------------------------------------- - * Interrupt handlers - */ - -static void msc_interrupt(msc_drv* d) +void x1000_msc_irq_handler(struct x1000_msc_controller *ctl) { - const unsigned tmo_bits = jz_orm(MSC_IFLAG, TIME_OUT_READ, TIME_OUT_RES); - const unsigned crc_bits = jz_orm(MSC_IFLAG, CRC_RES_ERROR, - CRC_READ_ERROR, CRC_WRITE_ERROR); - const unsigned err_bits = tmo_bits | crc_bits; + const uint32_t tmo_bits = jz_orm(MSC_IFLAG, TIME_OUT_READ, TIME_OUT_RES); + const uint32_t crc_bits = jz_orm(MSC_IFLAG, CRC_RES_ERROR, CRC_READ_ERROR, CRC_WRITE_ERROR); - unsigned iflag = REG_MSC_IFLAG(d->msc_nr) & ~REG_MSC_IMASK(d->msc_nr); - bool handled = false; + uint32_t iflag = REG_MSC_IFLAG(ctl->msc_nr); - /* In case card was removed */ - if(!msc_card_detect(d)) { - msc_finish_request(d, MSC_REQ_EXTRACTED); - return; + if (iflag & tmo_bits) + ctl->err_code = SDMMC_STATUS_TIMEOUT; + else if (iflag & crc_bits) + ctl->err_code = SDMMC_STATUS_INVALID_CRC; + + /* Read and clear command response */ + if (iflag & BM_MSC_IFLAG_END_CMD_RES) + { + REG_MSC_IMASK(ctl->msc_nr) |= BM_MSC_IFLAG_END_CMD_RES; + REG_MSC_IFLAG(ctl->msc_nr) = BM_MSC_IFLAG_END_CMD_RES; + x1000_msc_read_response(ctl); } - /* Check for errors */ - if(iflag & err_bits) { - int st; - if(iflag & crc_bits) - st = MSC_REQ_CRC_ERR; - else if(iflag & tmo_bits) - st = MSC_REQ_TIMEOUT; - else - st = MSC_REQ_ERROR; - - msc_finish_request(d, st); - return; - } - - /* Read and check the command response */ - if(iflag & BM_MSC_IFLAG_END_CMD_RES) { - msc_read_response(d); - int st = msc_check_response(d); - if(st == MSC_REQ_SUCCESS) { - jz_writef(MSC_IMASK(d->msc_nr), END_CMD_RES(1)); - jz_overwritef(MSC_IFLAG(d->msc_nr), END_CMD_RES(1)); - handled = true; - } else { - msc_finish_request(d, st); - return; - } - } - - /* Check if the "done" interrupt is signaled */ - if(iflag & d->iflag_done) { - /* Discard after DMA in case of hardware cache prefetching. - * Only needed for read operations. - */ - if((d->req->flags & MSC_RF_DATA) != 0 && - (d->req->flags & MSC_RF_WRITE) == 0) { - discard_dcache_range(d->req->data, - d->req->block_len * d->req->nr_blocks); - } - - msc_finish_request(d, MSC_REQ_SUCCESS); - return; - } - - if(!handled) { - panicf("msc%d: irq bug! iflag:%08x raw_iflag:%08lx imask:%08lx", - d->msc_nr, iflag, REG_MSC_IFLAG(d->msc_nr), REG_MSC_IMASK(d->msc_nr)); - } -} - -static int msc_cd_callback(struct timeout* tmo) -{ - msc_drv* d = (msc_drv*)tmo->data; - int now_present = msc_card_detect(d) ? 1 : 0; - - /* If the CD pin level changed during the timeout interval, then the - * signal is not yet stable and we need to wait longer. */ - if(now_present != d->card_present_last) { - d->card_present_last = now_present; - return DEBOUNCE_TIME; - } - - /* If there is a change, then broadcast the hotswap event */ - if(now_present != d->card_present) { - if(now_present) { - d->card_present = 1; - queue_broadcast(SYS_HOTSWAP_INSERTED, d->drive_nr); - } else { - msc_async_abort(d, MSC_REQ_EXTRACTED); - d->card_present = 0; - queue_broadcast(SYS_HOTSWAP_EXTRACTED, d->drive_nr); - } - } - - return 0; -} - -static void msc_cd_interrupt(msc_drv* d) -{ - /* Timer to debounce input */ - d->card_present_last = msc_card_detect(d) ? 1 : 0; - timeout_register(&d->cd_tmo, msc_cd_callback, DEBOUNCE_TIME, (intptr_t)d); - - /* Invert the IRQ */ - gpio_flip_edge_irq(d->config->cd_gpio); -} - -void MSC0(void) -{ - msc_interrupt(&msc_drivers[0]); -} - -void MSC1(void) -{ - msc_interrupt(&msc_drivers[1]); -} - -static void msc0_cd_interrupt(void) -{ - msc_cd_interrupt(&msc_drivers[0]); -} - -static void msc1_cd_interrupt(void) -{ - msc_cd_interrupt(&msc_drivers[1]); -} - -/* --------------------------------------------------------------------------- - * SD command helpers - */ - -int msc_cmd_exec(msc_drv* d, msc_req* r) -{ - int status = msc_request(d, r); - if(status == MSC_REQ_SUCCESS) - return status; - else if(status == MSC_REQ_LOCKUP || status == MSC_REQ_EXTRACTED) - d->driver_flags |= MSC_DF_ERRSTATE; - else if(r->flags & (MSC_RF_ERR_CMD12|MSC_RF_AUTO_CMD12)) { - /* After an error, the controller does not automatically issue CMD12, - * so we need to send it if it's needed, as required by the SD spec. - */ - msc_req nreq = {0}; - nreq.command = SD_STOP_TRANSMISSION; - nreq.resptype = MSC_RESP_R1B; - nreq.flags = MSC_RF_ABORT; - logf("msc%d: cmd%d error, sending cmd12", d->msc_nr, r->command); - if(msc_cmd_exec(d, &nreq)) - d->driver_flags |= MSC_DF_ERRSTATE; - } - - logf("msc%d: err:%d, cmd%d, arg:%x", d->msc_nr, status, - r->command, r->argument); - return status; -} - -int msc_app_cmd_exec(msc_drv* d, msc_req* r) -{ - msc_req areq = {0}; - areq.command = SD_APP_CMD; - areq.argument = d->cardinfo.rca; - areq.resptype = MSC_RESP_R1; - if(msc_cmd_exec(d, &areq)) - return areq.status; - - /* Verify that CMD55 was accepted */ - if((areq.response[0] & (1 << 5)) == 0) - return MSC_REQ_ERROR; - - return msc_cmd_exec(d, r); -} - -int msc_cmd_go_idle_state(msc_drv* d) -{ - msc_req req = {0}; - req.command = SD_GO_IDLE_STATE; - req.resptype = MSC_RESP_NONE; - req.flags = MSC_RF_INIT; - return msc_cmd_exec(d, &req); -} - -int msc_cmd_send_if_cond(msc_drv* d) -{ - msc_req req = {0}; - req.command = SD_SEND_IF_COND; - req.argument = 0x1aa; - req.resptype = MSC_RESP_R7; - - /* TODO - Check if SEND_IF_COND timeout is really an error - * IIRC, this can occur if the card isn't HCS (old cards < 2 GiB). - */ - if(msc_cmd_exec(d, &req)) - return req.status; - - /* Set HCS bit if the card responds correctly */ - if((req.response[0] & 0xff) == 0xaa) - d->driver_flags |= MSC_DF_HCS_CARD; - - return MSC_REQ_SUCCESS; -} - -int msc_cmd_app_op_cond(msc_drv* d) -{ - msc_req req = {0}; - req.command = SD_APP_OP_COND; - req.argument = 0x00300000; /* 3.4 - 3.6 V */ - req.resptype = MSC_RESP_R3; - if(d->driver_flags & MSC_DF_HCS_CARD) - req.argument |= (1 << 30); - - int timeout = 2 * HZ; - do { - if(msc_app_cmd_exec(d, &req)) - return req.status; - if(req.response[0] & (1 << 31)) - break; - sleep(1); - } while(--timeout > 0); - - if(timeout == 0) - return MSC_REQ_TIMEOUT; - - return MSC_REQ_SUCCESS; -} - -int msc_cmd_all_send_cid(msc_drv* d) -{ - msc_req req = {0}; - req.command = SD_ALL_SEND_CID; - req.resptype = MSC_RESP_R2; - if(msc_cmd_exec(d, &req)) - return req.status; - - for(int i = 0; i < 4; ++i) - d->cardinfo.cid[i] = req.response[i]; - - return MSC_REQ_SUCCESS; -} - -int msc_cmd_send_rca(msc_drv* d) -{ - msc_req req = {0}; - req.command = SD_SEND_RELATIVE_ADDR; - req.resptype = MSC_RESP_R6; - if(msc_cmd_exec(d, &req)) - return req.status; - - d->cardinfo.rca = req.response[0] & 0xffff0000; - return MSC_REQ_SUCCESS; -} - -int msc_cmd_send_csd(msc_drv* d) -{ - msc_req req = {0}; - req.command = SD_SEND_CSD; - req.argument = d->cardinfo.rca; - req.resptype = MSC_RESP_R2; - if(msc_cmd_exec(d, &req)) - return req.status; - - for(int i = 0; i < 4; ++i) - d->cardinfo.csd[i] = req.response[i]; - sd_parse_csd(&d->cardinfo); - - if(d->cardinfo.sd2plus) - d->driver_flags |= MSC_DF_V2_CARD; - - return 0; -} - -int msc_cmd_select_card(msc_drv* d) -{ - msc_req req = {0}; - req.command = SD_SELECT_CARD; - req.argument = d->cardinfo.rca; - req.resptype = MSC_RESP_R1B; - return msc_cmd_exec(d, &req); -} - -int msc_cmd_set_bus_width(msc_drv* d, int width) -{ - /* TODO - must we check bus width is supported in the cardinfo? */ - msc_req req = {0}; - req.command = SD_SET_BUS_WIDTH; - req.resptype = MSC_RESP_R1; - switch(width) { - case 1: req.argument = 0; break; - case 4: req.argument = 2; break; - default: return MSC_REQ_ERROR; - } - - if(msc_app_cmd_exec(d, &req)) - return req.status; - - msc_set_width(d, width); - return MSC_REQ_SUCCESS; -} - -int msc_cmd_set_clr_card_detect(msc_drv* d, int arg) -{ - msc_req req = {0}; - req.command = SD_SET_CLR_CARD_DETECT; - req.argument = arg; - req.resptype = MSC_RESP_R1; - return msc_app_cmd_exec(d, &req); -} - -int msc_cmd_switch_freq(msc_drv* d) -{ - /* If card doesn't support High Speed, we don't need to send a command */ - if((d->driver_flags & MSC_DF_V2_CARD) == 0) { - msc_set_speed(d, MSC_SPEED_FAST); - return MSC_REQ_SUCCESS; - } - - /* Try switching to High Speed (50 MHz) */ - char buffer[64] CACHEALIGN_ATTR; - msc_req req = {0}; - req.command = SD_SWITCH_FUNC; - req.argument = 0x80fffff1; - req.resptype = MSC_RESP_R1; - req.flags = MSC_RF_DATA; - req.data = &buffer[0]; - req.block_len = 64; - req.nr_blocks = 1; - if(msc_cmd_exec(d, &req)) - return req.status; - - msc_set_speed(d, MSC_SPEED_HIGH); - return MSC_REQ_SUCCESS; -} - -int msc_cmd_send_status(msc_drv* d) -{ - msc_req req = {0}; - req.command = SD_SEND_STATUS; - req.argument = d->cardinfo.rca; - req.resptype = MSC_RESP_R1; - return msc_cmd_exec(d, &req); -} - -int msc_cmd_set_block_len(msc_drv* d, unsigned len) -{ - msc_req req = {0}; - req.command = SD_SET_BLOCKLEN; - req.argument = len; - req.resptype = MSC_RESP_R1; - return msc_cmd_exec(d, &req); + if ((iflag & ctl->iflag_done) || ctl->err_code) + x1000_msc_finish_command(ctl); } diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.h b/firmware/target/mips/ingenic_x1000/msc-x1000.h index 60f15b749e..aaa7157ee3 100644 --- a/firmware/target/mips/ingenic_x1000/msc-x1000.h +++ b/firmware/target/mips/ingenic_x1000/msc-x1000.h @@ -17,168 +17,47 @@ * KIND, either express or implied. * ****************************************************************************/ - #ifndef __MSC_X1000_H__ #define __MSC_X1000_H__ -#include "kernel.h" -#include "sdmmc.h" -#include +#include "sdmmc_host.h" -/* Number of MSC controllers */ -#define MSC_COUNT 2 +/* Must be allocated on a cacheline boundary */ +struct x1000_msc_dma_desc +{ + uint32_t nda; + uint32_t mem; + uint32_t len; + uint32_t cmd; +}; -/* Media types */ -#define MSC_TYPE_SD 0 -#define MSC_TYPE_MMC 1 -#define MSC_TYPE_ATA 2 -#define MSC_TYPE_ANY 3 - -/* Clock modes */ -#define MSC_CLK_MANUAL 0 -#define MSC_CLK_AUTOMATIC 1 - -/* Clock status bits */ -#define MSC_CLKST_ENABLE (1 << 0) -#define MSC_CLKST_AUTO (1 << 1) - -/* Driver flags */ -#define MSC_DF_ERRSTATE (1 << 0) -#define MSC_DF_READY (1 << 1) -#define MSC_DF_HCS_CARD (1 << 2) -#define MSC_DF_V2_CARD (1 << 3) -#define MSC_DF_HAS_SBC (1 << 4) - -/* Request status codes */ -#define MSC_REQ_SUCCESS 0 -#define MSC_REQ_CRC_ERR 1 -#define MSC_REQ_CARD_ERR 2 -#define MSC_REQ_TIMEOUT 3 -#define MSC_REQ_EXTRACTED 4 -#define MSC_REQ_LOCKUP 5 -#define MSC_REQ_ERROR 6 -#define MSC_REQ_INCOMPLETE (-1) - -/* Response types */ -#define MSC_RESP_NONE 0 -#define MSC_RESP_BUSY (1 << 7) -#define MSC_RESP_R1 1 -#define MSC_RESP_R1B (MSC_RESP_R1|MSC_RESP_BUSY) -#define MSC_RESP_R2 2 -#define MSC_RESP_R3 3 -#define MSC_RESP_R6 6 -#define MSC_RESP_R7 7 - -/* Request flags */ -#define MSC_RF_INIT (1 << 0) -#define MSC_RF_ERR_CMD12 (1 << 1) -#define MSC_RF_AUTO_CMD12 (1 << 2) -#define MSC_RF_PROG (1 << 3) -#define MSC_RF_DATA (1 << 4) -#define MSC_RF_WRITE (1 << 5) -#define MSC_RF_ABORT (1 << 6) - -/* Clock speeds */ -#define MSC_SPEED_INIT 400000 -#define MSC_SPEED_FAST 25000000 -#define MSC_SPEED_HIGH 50000000 - -typedef struct msc_config { +struct x1000_msc_controller +{ int msc_nr; - int msc_type; - int bus_width; - const char* label; - int cd_gpio; - int cd_active_level; -} msc_config; + uint32_t src_clk_freq; + struct x1000_msc_dma_desc *dma_desc; -typedef struct msc_req { - /* Filled by caller */ - int command; - unsigned argument; - int resptype; - int flags; - void* data; - unsigned nr_blocks; - unsigned block_len; + uint32_t bus_clock; + uint32_t cmdat_def; + uint32_t iflag_done; + int resp_len; + int err_code; + struct sdmmc_host_response *resp; - /* Filled by driver */ - volatile unsigned response[4]; - volatile int status; -} msc_req; + struct semaphore sem; +}; -struct sd_dma_desc { - unsigned nda; - unsigned mem; - unsigned len; - unsigned cmd; -} __attribute__((aligned(16))); +void x1000_msc_init(struct x1000_msc_controller* ctl, + struct x1000_msc_dma_desc *dma_desc, + int msc_nr, uint32_t src_clk_freq); -typedef struct msc_drv { - int msc_nr; - int drive_nr; - const msc_config* config; - - int driver_flags; - int clk_status; - unsigned cmdat_def; - msc_req* req; - unsigned iflag_done; - - volatile int req_running; - volatile int card_present; /* Debounced status */ - volatile int card_present_last; /* Status when we last polled it */ - - struct mutex lock; - struct semaphore cmd_done; - struct timeout cmd_tmo; - struct timeout cd_tmo; - struct sd_dma_desc dma_desc; - - tCardInfo cardinfo; -} msc_drv; - -/* Driver initialization, etc */ -extern void msc_init(void); -extern msc_drv* msc_get(int type, int index); -extern msc_drv* msc_get_by_drive(int drive_nr); - -extern void msc_lock(msc_drv* d); -extern void msc_unlock(msc_drv* d); -extern void msc_full_reset(msc_drv* d); -extern bool msc_card_detect(msc_drv* d); - -extern void msc_led_trigger(void); - -/* Controller API */ -extern void msc_ctl_reset(msc_drv* d); -extern void msc_set_clock_mode(msc_drv* d, int mode); -extern void msc_enable_clock(msc_drv* d, bool enable); -extern void msc_set_speed(msc_drv* d, int rate); -extern void msc_set_width(msc_drv* d, int width); - -/* Request API */ -extern void msc_async_start(msc_drv* d, msc_req* r); -extern void msc_async_abort(msc_drv* d, int status); -extern int msc_async_wait(msc_drv* d, int timeout); -extern int msc_request(msc_drv* d, msc_req* r); - -/* Command helpers; note these are written with SD in mind - * and should be reviewed before using them for MMC / CE-ATA - */ -extern int msc_cmd_exec(msc_drv* d, msc_req* r); -extern int msc_app_cmd_exec(msc_drv* d, msc_req* r); -extern int msc_cmd_go_idle_state(msc_drv* d); -extern int msc_cmd_send_if_cond(msc_drv* d); -extern int msc_cmd_app_op_cond(msc_drv* d); -extern int msc_cmd_all_send_cid(msc_drv* d); -extern int msc_cmd_send_rca(msc_drv* d); -extern int msc_cmd_send_csd(msc_drv* d); -extern int msc_cmd_select_card(msc_drv* d); -extern int msc_cmd_set_bus_width(msc_drv* d, int width); -extern int msc_cmd_set_clr_card_detect(msc_drv* d, int arg); -extern int msc_cmd_switch_freq(msc_drv* d); -extern int msc_cmd_send_status(msc_drv* d); -extern int msc_cmd_set_block_len(msc_drv* d, unsigned len); +void x1000_msc_set_power_enabled(void *controller, bool enabled); +void x1000_msc_set_bus_width(void *controller, uint32_t width); +void x1000_msc_set_bus_clock(void *controller, uint32_t clock); +int x1000_msc_submit_command(void *controller, + const struct sdmmc_host_command *cmd, + struct sdmmc_host_response *resp); +void x1000_msc_abort_command(void *controller); +void x1000_msc_irq_handler(struct x1000_msc_controller *ctl); #endif /* __MSC_X1000_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/sd-x1000.c b/firmware/target/mips/ingenic_x1000/sd-x1000.c deleted file mode 100644 index e40db5833d..0000000000 --- a/firmware/target/mips/ingenic_x1000/sd-x1000.c +++ /dev/null @@ -1,281 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * - * Copyright (C) 2021 Aidan MacDonald - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#include "system.h" -#include "storage.h" -#include "sdmmc.h" -#include "sd.h" -#include "msc-x1000.h" -#include "gpio-x1000.h" -#include - -/* #define LOGF_ENABLE */ -#include "logf.h" - -static msc_drv* sd_to_msc[MSC_COUNT]; -static long _sd_last_disk_activity = 0; - -static void sd_probe_set_block_count(msc_drv* d) -{ - uint8_t scr[CACHEALIGN_SIZE] CACHEALIGN_ATTR; - msc_req req = {0}; - req.command = SD_SEND_SCR; - req.resptype = MSC_RESP_R1; - req.flags = MSC_RF_DATA; - req.data = scr; - req.nr_blocks = 1; - req.block_len = 8; - - if(msc_app_cmd_exec(d, &req) == MSC_REQ_SUCCESS) { - if(scr[3] & 0x02) - d->driver_flags |= MSC_DF_HAS_SBC; - else - d->driver_flags &= ~MSC_DF_HAS_SBC; - } -} - -static int sd_init_card(msc_drv* d) -{ - int s; - if(s = msc_cmd_go_idle_state(d)) return -100 - s; - if(s = msc_cmd_send_if_cond(d)) return -110 - s; - if(s = msc_cmd_app_op_cond(d)) return -120 - s; - if(s = msc_cmd_all_send_cid(d)) return -130 - s; - if(s = msc_cmd_send_rca(d)) return -140 - s; - if(s = msc_cmd_send_csd(d)) return -150 - s; - if(s = msc_cmd_select_card(d)) return -160 - s; - if(s = msc_cmd_set_clr_card_detect(d, 0)) return -170 - s; - if(s = msc_cmd_set_bus_width(d, 4)) return -180 - s; - if(s = msc_cmd_switch_freq(d)) return -190 - s; - sd_probe_set_block_count(d); - d->driver_flags |= MSC_DF_READY; - d->cardinfo.initialized = 1; - return 0; -} - -static int sd_transfer(msc_drv* d, bool write, - sector_t start, int count, void* buf) -{ - int status = -1; - - msc_lock(d); - if(!d->card_present) - goto _exit; - - /* Hopefully puts the driver into a working state */ - if(d->driver_flags & MSC_DF_ERRSTATE) { - logf("MSC%d: attempting to reset after ERRSTATE", d->msc_nr); - msc_full_reset(d); - } - - /* Init card if needed */ - if((d->driver_flags & MSC_DF_READY) == 0) { - if(status = sd_init_card(d)) { - logf("MSC%d: card init failed (code %d)", d->msc_nr, status); - d->driver_flags |= MSC_DF_ERRSTATE; - d->cardinfo.initialized = status; - goto _exit; - } - } - - /* Ensure parameters are within range */ - if(count < 1) - goto _exit; - if(start + count > d->cardinfo.numblocks) - goto _exit; - - do { - /* We can only do 65536 blocks at a time */ - int xfer_count = count > 0xffff ? 0xffff : count; - - /* Set block length. I think this is only necessary for non-HCS cards. - * HCS cards always use 512 bytes so we shouldn't need it. - */ - if((d->driver_flags & MSC_DF_HCS_CARD) == 0) - if(status = msc_cmd_set_block_len(d, SD_BLOCK_SIZE)) - goto _exit; - - bool use_sbc = false; - if(xfer_count > 1 && (d->driver_flags & MSC_DF_HAS_SBC)) { - msc_req sbc_req = {0}; - sbc_req.command = 23; /* CMD23: SET_BLOCK_COUNT (SBC) */ - sbc_req.argument = xfer_count; - sbc_req.resptype = MSC_RESP_R1; - status = msc_cmd_exec(d, &sbc_req); - if(status == MSC_REQ_SUCCESS) { - use_sbc = true; - } else if(status == MSC_REQ_CARD_ERR && - (sbc_req.response[0] & SD_R1_ILLEGAL_COMMAND)) { - d->driver_flags &= ~MSC_DF_HAS_SBC; - } else { - goto _exit; - } - } - - msc_req req = {0}; - req.data = buf; - req.nr_blocks = xfer_count; - req.block_len = SD_BLOCK_SIZE; - req.resptype = MSC_RESP_R1; - req.flags = MSC_RF_DATA; - if(xfer_count > 1) { - if(use_sbc) - req.flags |= MSC_RF_ERR_CMD12; - else - req.flags |= MSC_RF_AUTO_CMD12; - } - if(write) { - req.command = xfer_count == 1 ? SD_WRITE_BLOCK - : SD_WRITE_MULTIPLE_BLOCK; - req.flags |= MSC_RF_PROG | MSC_RF_WRITE; - } else { - req.command = xfer_count == 1 ? SD_READ_SINGLE_BLOCK - : SD_READ_MULTIPLE_BLOCK; - } - - // XXX 64-bit - if(d->driver_flags & MSC_DF_V2_CARD) - req.argument = start; - else - req.argument = start * SD_BLOCK_SIZE; - - if(status = msc_cmd_exec(d, &req)) - goto _exit; - - /* TODO - properly handle reading the last block of the SD card - * This is likely to fail if we're reading near the end because - * the SD card will try to read past the last sector and then - * signal an error. So we need to ignore that error, but only if - * it was expected to occur. (See SD spec sec. 4.3.3, "Block Read") - */ - if(status = msc_cmd_send_status(d)) - goto _exit; - - /* Advance the buffer and adjust start/count */ - buf += xfer_count * SD_BLOCK_SIZE; - start += xfer_count; - count -= xfer_count; - } while(count > 0); - - _exit: - msc_unlock(d); - return status; -} - -int sd_read_sectors(IF_MD(int drive,) sector_t start, - int count, void* buf) -{ - return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], false, - start, count, buf); -} - -int sd_write_sectors(IF_MD(int drive,) sector_t start, - int count, const void* buf) -{ - return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], true, - start, count, (void*)buf); -} - -tCardInfo* card_get_info_target(int card_nr) -{ - /* Defensive measures */ - if(card_nr < 0 || card_nr >= MSC_COUNT || sd_to_msc[card_nr] == NULL) { - static tCardInfo null_info = { 0 }; - return &null_info; - } - - return &sd_to_msc[card_nr]->cardinfo; -} - -int sd_event(long id, intptr_t data) -{ - if(id == SYS_HOTSWAP_EXTRACTED) { - msc_drv* d = msc_get_by_drive(data); - if(d) - msc_full_reset(d); - return 0; - } else { - return storage_event_default_handler(id, data, _sd_last_disk_activity, - STORAGE_SD); - } -} - -long sd_last_disk_activity(void) -{ - return _sd_last_disk_activity; -} - -bool sd_present(IF_MD_NONVOID(int drive)) -{ -#ifndef HAVE_MULTIDRIVE - int drive = 0; -#endif - /* Seems that volume_properties() in firmware/common/disk.c may pass - * drive = -1 when the SD card is not inserted, so just return false. - */ - if(drive < 0) - return false; - - return sd_to_msc[IF_MD_DRV(drive)]->card_present; -} - -bool sd_removable(IF_MD_NONVOID(int drive)) -{ -#ifndef HAVE_MULTIDRIVE - int drive = 0; -#endif - - /* Same reason as sd_present() */ - if(drive < 0) - return false; - - return sd_to_msc[IF_MD_DRV(drive)]->config->cd_gpio != GPIO_NONE; -} - -#ifndef CONFIG_STORAGE_MULTI -static -#endif -int sd_num_drives(int first_drive) -{ - int n = 0; - for(; n < MSC_COUNT; ++n) { - msc_drv* d = msc_get(MSC_TYPE_SD, n); - if(d == NULL) - break; - - d->drive_nr = first_drive + n; - sd_to_msc[n] = d; - } - - for(int i = n; i < MSC_COUNT; ++i) - sd_to_msc[i] = NULL; - - return n; -} - -int sd_init(void) -{ - msc_init(); -#ifndef CONFIG_STORAGE_MULTI - sd_num_drives(0); -#endif - - return 0; -} diff --git a/firmware/target/mips/ingenic_x1000/sdmmc-x1000-common.c b/firmware/target/mips/ingenic_x1000/sdmmc-x1000-common.c new file mode 100644 index 0000000000..ac00a68369 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/sdmmc-x1000-common.c @@ -0,0 +1,116 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2026 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "sdmmc_host.h" +#include "sdmmc_poll.h" +#include "msc-x1000.h" +#include "gpio-x1000.h" +#include "irq-x1000.h" +#include "clk-x1000.h" + +/* All current X1000 targets use MSC0 */ +#define MSC_NUM 0 + +/* And all targets use an active low card dtect GPIO */ +#define MSC_CD_GPIO GPIO_MSC0_CD +#define MSC_CD_ACTIVE_LEVEL 0 + +/* Q1 uses MPLL, other targets are using SCLK_A */ +#if defined(SHANLING_Q1) +# define MSC_SOURCE_CLOCK X1000_CLK_MPLL +#else +# define MSC_SOURCE_CLOCK X1000_CLK_SCLK_A +#endif + +/* 300ms poll interval */ +#define SDCARD_POLL_TICKS (300 * HZ / 1000) + +static struct sdmmc_host sdmmc; +static struct sdmmc_poll sdmmc_poll; +static struct x1000_msc_controller msc_ctl; +static struct x1000_msc_dma_desc msc_dma_desc; + +static const struct sdmmc_controller_ops sdmmc_ops = { + .set_power_enabled = x1000_msc_set_power_enabled, + .set_bus_width = x1000_msc_set_bus_width, + .set_bus_clock = x1000_msc_set_bus_clock, + .submit_command = x1000_msc_submit_command, + .abort_command = x1000_msc_abort_command, +}; + +static const struct sdmmc_host_config sdmmc_config INITDATA_ATTR = { + .type = STORAGE_SD, + .bus_voltages = SDMMC_BUS_VOLTAGE_3V2_3V3 | + SDMMC_BUS_VOLTAGE_3V3_3V4, + .bus_widths = SDMMC_BUS_WIDTH_1BIT | + SDMMC_BUS_WIDTH_4BIT, + .bus_clocks = SDMMC_BUS_CLOCK_400KHZ | + SDMMC_BUS_CLOCK_25MHZ | + SDMMC_BUS_CLOCK_50MHZ, + .max_nr_blocks = 65535, + .is_removable = true, +}; + +static bool is_sdcard_inserted(void) +{ + return gpio_get_level(MSC_CD_GPIO) == MSC_CD_ACTIVE_LEVEL; +} + +static void sdcard_insert_irq(void) +{ + sdmmc_poll_event(&sdmmc_poll); + gpio_flip_edge_irq(MSC_CD_GPIO); +} + +void sdmmc_host_target_init(void) +{ + /* Configure clock source */ + jz_writef(CPM_MSC0CDR, CE(1), + CLKSRC(MSC_SOURCE_CLOCK == X1000_CLK_MPLL ? 1 : 0)); + while (jz_readf(CPM_MSC0CDR, BUSY)); + jz_writef(CPM_MSC0CDR, CE(0)); + + /* Initialize controller */ + x1000_msc_init(&msc_ctl, &msc_dma_desc, MSC_NUM, clk_get(MSC_SOURCE_CLOCK)); + system_enable_irq(MSC_NUM == 0 ? IRQ_MSC0 : IRQ_MSC1); + + /* Initialize SD/MMC host driver */ + sdmmc_host_init(&sdmmc, &sdmmc_config, &sdmmc_ops, &msc_ctl); + sdmmc_host_init_medium_present(&sdmmc, is_sdcard_inserted()); + + /* Setup card detect handling */ + sdmmc_poll_init(&sdmmc_poll, &sdmmc, is_sdcard_inserted); + + system_set_irq_handler(GPIO_TO_IRQ(MSC_CD_GPIO), sdcard_insert_irq); + gpio_set_function(MSC_CD_GPIO, GPIOF_IRQ_EDGE(1)); + gpio_flip_edge_irq(MSC_CD_GPIO); + gpio_enable_irq(MSC_CD_GPIO); +} + +#if MSC_NUM == 0 +void MSC0(void) +{ + x1000_msc_irq_handler(&msc_ctl); +} +#else +void MSC1(void) +{ + x1000_msc_irq_handler(&msc_ctl); +} +#endif