From f40cae6cdc89103ca9432fe1c98aec30bd73cfbf Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Tue, 18 Aug 2026 15:36:41 +0100 Subject: [PATCH] x1000: rewrite SD driver using sdmmc_host Remove all SD protocol handling and all target specific code like GPIO/interrupt handling and clock parent setup. This can now be handled from sdmmc_host_target_init() for each target. Now only the clock frequency is managed by the MSC driver. To make this code easier to factor out later, it's confined to helper functions that do not access the driver state. One small change is that MSCxDIV output is now clamped to a minimum of 50MHz to avoid unnecessary frequency changes. The MSC_CLKRT divider can still divide 50MHz down to 400 KHz so there is no downside to this. Auto-CMD12 is now unused. Using it would make error handling more difficult for sdmmc_host since the controller does not expose response data for the auto-CMD12. Explicit CMD12 was not handled correctly in the old version of the driver because the busy signal was ignored for R1b responses if there was no associated data transfer. This is now fixed by waiting for the PRG_DONE interrupt instead of END_CMD_RES for non-data transfer R1b type commands. Since existing X1000 targets are all very similar they use a shared implementation in sdmmc-x1000-common.c for clock setup and card detection. New targets can either use this or create a separate file if they are different enough to warrant one. Change-Id: I35396637325d7c06a10151bb6aee64cabdc7b682 --- firmware/SOURCES | 6 +- firmware/export/config/erosqnative.h | 3 + firmware/export/config/fiiom3k.h | 3 + firmware/export/config/shanlingq1.h | 3 + .../target/mips/ingenic_x1000/msc-x1000.c | 1102 +++++------------ .../target/mips/ingenic_x1000/msc-x1000.h | 185 +-- firmware/target/mips/ingenic_x1000/sd-x1000.c | 281 ----- .../mips/ingenic_x1000/sdmmc-x1000-common.c | 116 ++ 8 files changed, 444 insertions(+), 1255 deletions(-) delete mode 100644 firmware/target/mips/ingenic_x1000/sd-x1000.c create mode 100644 firmware/target/mips/ingenic_x1000/sdmmc-x1000-common.c 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