From b889bf765cbcc61f8579387f51a9d8d3bf2d8829 Mon Sep 17 00:00:00 2001 From: SamulKyull Date: Sun, 23 Feb 2025 16:18:01 +0800 Subject: [PATCH] [board] bringup t536 sdc0 --- board/tlt536-evm/board.c | 70 +++++++++++++ board/tlt536-evm/hello_world/main.c | 140 ++++++++++++++++++++++--- src/drivers/chips/sun55iw6/sys-sdhci.c | 113 ++++++++++++++++++++ 3 files changed, 310 insertions(+), 13 deletions(-) diff --git a/board/tlt536-evm/board.c b/board/tlt536-evm/board.c index 3738d5c5..d9006669 100644 --- a/board/tlt536-evm/board.c +++ b/board/tlt536-evm/board.c @@ -44,6 +44,76 @@ sunxi_serial_t uart_dbg = { }, }; +sunxi_sdhci_t sdhci0 = { + .name = "sdhci0", + .id = MMC_CONTROLLER_0, + .reg_base = SUNXI_SMHC0_BASE, + .sdhci_mmc_type = MMC_TYPE_SD, + .max_clk = 50000000, + .width = SMHC_WIDTH_4BIT, + .dma_des_addr = SDRAM_BASE + 0x30080000, + .pinctrl = { + .gpio_clk = {GPIO_PIN(GPIO_PORTF, 2), GPIO_PERIPH_MUX2}, + .gpio_cmd = {GPIO_PIN(GPIO_PORTF, 3), GPIO_PERIPH_MUX2}, + .gpio_d0 = {GPIO_PIN(GPIO_PORTF, 1), GPIO_PERIPH_MUX2}, + .gpio_d1 = {GPIO_PIN(GPIO_PORTF, 0), GPIO_PERIPH_MUX2}, + .gpio_d2 = {GPIO_PIN(GPIO_PORTF, 5), GPIO_PERIPH_MUX2}, + .gpio_d3 = {GPIO_PIN(GPIO_PORTF, 4), GPIO_PERIPH_MUX2}, + .gpio_cd = {GPIO_PIN(GPIO_PORTF, 6), GPIO_INPUT}, + .cd_level = GPIO_LEVEL_LOW, + }, + .clk_ctrl = { + .gate_reg_base = SUNXI_CCU_BASE + SMHC0_BGR_REG, + .gate_reg_offset = SDHCI_DEFAULT_CLK_GATE_OFFSET(0), + .rst_reg_base = SUNXI_CCU_BASE + SMHC0_BGR_REG, + .rst_reg_offset = SDHCI_DEFAULT_CLK_RST_OFFSET(0), + }, + .sdhci_clk = { + .reg_base = SUNXI_CCU_BASE + SMHC0_CLK_REG, + .reg_factor_n_offset = SDHCI_DEFAULT_CLK_FACTOR_N_OFFSET, + .reg_factor_m_offset = SDHCI_DEFAULT_CLK_FACTOR_M_OFFSET, + .clk_sel = 0x1, + .parent_clk = 300000000, + }, +}; + +sunxi_sdhci_t sdhci2 = { + .name = "sdhci2", + .id = MMC_CONTROLLER_2, + .reg_base = SUNXI_SMHC2_BASE, + .sdhci_mmc_type = MMC_TYPE_EMMC, + .max_clk = 5200000, + .width = SMHC_WIDTH_8BIT, + .dma_des_addr = SDRAM_BASE + 0x30080000, + .pinctrl = { + .gpio_clk = {GPIO_PIN(GPIO_PORTC, 5), GPIO_PERIPH_MUX3}, + .gpio_cmd = {GPIO_PIN(GPIO_PORTC, 6), GPIO_PERIPH_MUX3}, + .gpio_d0 = {GPIO_PIN(GPIO_PORTC, 10), GPIO_PERIPH_MUX3}, + .gpio_d1 = {GPIO_PIN(GPIO_PORTC, 13), GPIO_PERIPH_MUX3}, + .gpio_d2 = {GPIO_PIN(GPIO_PORTC, 15), GPIO_PERIPH_MUX3}, + .gpio_d3 = {GPIO_PIN(GPIO_PORTC, 8), GPIO_PERIPH_MUX3}, + .gpio_d4 = {GPIO_PIN(GPIO_PORTC, 9), GPIO_PERIPH_MUX3}, + .gpio_d5 = {GPIO_PIN(GPIO_PORTC, 11), GPIO_PERIPH_MUX3}, + .gpio_d6 = {GPIO_PIN(GPIO_PORTC, 14), GPIO_PERIPH_MUX3}, + .gpio_d7 = {GPIO_PIN(GPIO_PORTC, 16), GPIO_PERIPH_MUX3}, + .gpio_ds = {GPIO_PIN(GPIO_PORTC, 0), GPIO_PERIPH_MUX3}, + .gpio_rst = {GPIO_PIN(GPIO_PORTC, 1), GPIO_PERIPH_MUX3}, + }, + .clk_ctrl = { + .gate_reg_base = SUNXI_CCU_BASE + SMHC2_BGR_REG, + .gate_reg_offset = SDHCI_DEFAULT_CLK_GATE_OFFSET(2), + .rst_reg_base = SUNXI_CCU_BASE + SMHC2_BGR_REG, + .rst_reg_offset = SDHCI_DEFAULT_CLK_RST_OFFSET(2), + }, + .sdhci_clk = { + .reg_base = SUNXI_CCU_BASE + SMHC2_CLK_REG, + .reg_factor_n_offset = SDHCI_DEFAULT_CLK_FACTOR_N_OFFSET, + .reg_factor_m_offset = SDHCI_DEFAULT_CLK_FACTOR_M_OFFSET, + .clk_sel = 0x1, + .parent_clk = 800000000, + }, +}; + sunxi_i2c_t i2c_pmu = { .base = SUNXI_RTWI_BASE, .id = SUNXI_R_I2C0, diff --git a/board/tlt536-evm/hello_world/main.c b/board/tlt536-evm/hello_world/main.c index cd474c70..5324815e 100644 --- a/board/tlt536-evm/hello_world/main.c +++ b/board/tlt536-evm/hello_world/main.c @@ -7,33 +7,133 @@ #include #include +#include -#include - -#include +#include +#include +#include #include +#include #include #include - -#include -#include -#include +#include +#include +#include extern sunxi_serial_t uart_dbg; extern sunxi_i2c_t i2c_pmu; +extern sunxi_sdhci_t sdhci0; +extern sunxi_sdhci_t sdhci2; + +#define CONFIG_SDMMC_SPEED_TEST_SIZE 1024// (unit: 512B sectors) + +#define CHUNK_SIZE 0x20000 + +static int fatfs_loadimage_size(char *filename, BYTE *dest, uint32_t *file_size) { + FIL file; + UINT byte_to_read = CHUNK_SIZE; + UINT byte_read; + UINT total_read = 0; + FRESULT fret; + int ret = 1; + uint32_t start, time; + + fret = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ); + if (fret != FR_OK) { + printk_warning("FATFS: open, filename: [%s]: error %d\n", filename, fret); + ret = -1; + goto open_fail; + } + + start = time_ms(); + + do { + byte_read = 0; + fret = f_read(&file, (void *) (dest), byte_to_read, &byte_read); + dest += byte_to_read; + total_read += byte_read; + } while (byte_read >= byte_to_read && fret == FR_OK); + + time = time_ms() - start + 1; + + if (fret != FR_OK) { + printk_error("FATFS: read: error %d\n", fret); + ret = -1; + goto read_fail; + } + ret = 0; + *file_size = total_read; + +read_fail: + fret = f_close(&file); + + printk_info("FATFS: read in %ums at %.2fMB/S\n", time, + (f32) (total_read / time) / 1024.0f); + +open_fail: + return ret; +} -msh_declare_command(helloworld); +static int fatfs_loadimage(char *filename, BYTE *dest) { + return fatfs_loadimage_size(filename, dest, NULL); +} + +msh_declare_command(reload); +msh_define_help(reload, "rescan TF Card and reload DTB, Kernel zImage", "Usage: reload\n"); +int cmd_reload(int argc, const char **argv) { + if (sdmmc_init(&card0, &sdhci0) != 0) { + printk_error("SMHC: init failed\n"); + return 0; + } + return 0; +} -msh_define_help(helloworld, "display helloworld", "Usage: helloworld\n"); -int cmd_helloworld(int argc, const char **argv) { - printk(LOG_LEVEL_MUTE, "Hello World!\n"); - asm volatile("svc #0"); +msh_declare_command(read); +msh_define_help(read, "test", "Usage: read\n"); +int cmd_read(int argc, const char **argv) { + uint32_t start; + uint32_t test_time; + + printk_debug("Clear Buffer data\n"); + memset((void *) SDRAM_BASE, 0x00, 0x2000); + dump_hex(SDRAM_BASE, 0x100); + + printk_debug("Read data to buffer data\n"); + + start = time_ms(); + sdmmc_blk_read(&card0, (uint8_t *) (SDRAM_BASE), 0, 1024); + test_time = time_ms() - start; + printk_debug("SDMMC: speedtest %uKB in %ums at %uKB/S\n", + (CONFIG_SDMMC_SPEED_TEST_SIZE * 512) / 1024, test_time, + (CONFIG_SDMMC_SPEED_TEST_SIZE * 512) / test_time); + dump_hex(SDRAM_BASE, 0x100); + return 0; +} + +msh_declare_command(write); +msh_define_help(write, "test", "Usage: write\n"); +int cmd_write(int argc, const char **argv) { + uint32_t start; + uint32_t test_time; + + printk_debug("Set Buffer data\n"); + memset((void *) SDRAM_BASE, 0x00, 0x2000); + memcpy((void *) SDRAM_BASE, argv[1], strlen(argv[1])); + + start = time_ms(); + sdmmc_blk_write(&card0, (uint8_t *) (SDRAM_BASE), 0, 1024); + test_time = time_ms() - start; + printk_debug("SDMMC: speedtest %uKB in %ums at %uKB/S\n", + (CONFIG_SDMMC_SPEED_TEST_SIZE * 512) / 1024, test_time, + (CONFIG_SDMMC_SPEED_TEST_SIZE * 512) / test_time); return 0; } const msh_command_entry commands[] = { - msh_define_command(helloworld), + msh_define_command(reload), + msh_define_command(read), + msh_define_command(write), msh_command_end, }; @@ -60,6 +160,20 @@ int main(void) { printk_info("Hello World!\n"); + /* Initialize the SD host controller. */ + if (sunxi_sdhci_init(&sdhci0) != 0) { + printk_error("SMHC: %s controller init failed\n", sdhci0.name); + } else { + printk_info("SMHC: %s controller initialized\n", sdhci0.name); + } + + /* Initialize the SD card and check if initialization is successful. */ + if (sdmmc_init(&card0, &sdhci0) != 0) { + printk_warning("SMHC: init failed\n"); + } else { + printk_debug("Card OK!\n"); + } + syterkit_shell_attach(commands); abort(); diff --git a/src/drivers/chips/sun55iw6/sys-sdhci.c b/src/drivers/chips/sun55iw6/sys-sdhci.c index 3c6beb6a..1d88d075 100644 --- a/src/drivers/chips/sun55iw6/sys-sdhci.c +++ b/src/drivers/chips/sun55iw6/sys-sdhci.c @@ -16,3 +16,116 @@ #include #include + +/** + * @brief Set the SDHC controller's clock frequency. + * + * This function sets the clock frequency for the specified SDHC controller. + * + * @param sdhci Pointer to the SDHC controller structure. + * @param clk_hz Desired clock frequency in Hertz. + * @return Returns 0 on success, -1 on failure. + */ +int sunxi_sdhci_set_mclk(sunxi_sdhci_t *sdhci, uint32_t clk_hz) { + uint32_t reg_val = 0x0; + mmc_t *mmc = sdhci->mmc; + sunxi_sdhci_clk_t clk = sdhci->sdhci_clk; + + // Determine the clock source based on the requested frequency + if (clk_hz <= 4000000) { + clk.clk_sel = 0;// SCLK = 24000000 OSC CLK + } else { + clk.clk_sel = 2;// SCLK = AHB PLL + } + + if ((mmc->speed_mode == MMC_HSDDR52_DDR50) && (mmc->bus_width == SMHC_WIDTH_8BIT)) { + clk_hz /= 4; + } else { + clk_hz /= 2; + } + + // Set the clock divider values based on the requested frequency + switch (clk_hz) { + case 0 ... 400000: + clk.factor_n = 1; + clk.factor_m = 0xe; + break; + case 400001 ... 26000000: + clk.factor_n = (sdhci->id == 2) ? 2 : 1; + clk.factor_m = (sdhci->id == 2) ? 3 : 2; + break; + case 26000001 ... 52000000: + clk.factor_n = (sdhci->id == 2) ? 1 : 0; + clk.factor_m = 2; + break; + case 52000001 ... 100000000: + clk.clk_sel = 1;// PERI0_800M + clk.factor_n = 0; + clk.factor_m = 3; + break; + case 100000001 ... 150000000: + clk.clk_sel = 1;// PERI0_800M + clk.factor_n = 0; + clk.factor_m = 2; + break; + case 150000001 ... 200000000: + clk.clk_sel = 1;// PERI0_800M + clk.factor_n = 0; + clk.factor_m = 1; + break; + default: + clk.factor_n = (sdhci->id == 2) ? 1 : 0; + clk.factor_m = 2; + printk_debug("SMHC: requested frequency does not match: freq=%d, default to 52000000\n", clk_hz); + break; + } + + // Configure the clock register value + reg_val = (clk.clk_sel << 24) | (clk.factor_n << 8) | clk.factor_m; + writel(reg_val, clk.reg_base); + + return 0; +} + +/** + * @brief Get the current clock frequency of the SDHC controller. + * + * This function retrieves the current clock frequency of the specified SDHC controller. + * + * @param sdhci Pointer to the SDHC controller structure. + * @return Current clock frequency in Hertz. + */ +uint32_t sunxi_sdhci_get_mclk(sunxi_sdhci_t *sdhci) { + uint32_t clk_hz; + uint32_t reg_val = 0x0; + sunxi_sdhci_clk_t clk = sdhci->sdhci_clk; + + // Read the clock register value + reg_val = readl(clk.reg_base); + + // Extract the divider values and clock source from the register value + clk.factor_m = reg_val & 0xf; + clk.factor_n = (reg_val >> 8) & 0x3; + clk.clk_sel = (reg_val >> 24) & 0x3; + + // Calculate the current clock frequency based on the source and divider values + switch (clk.clk_sel) { + case 0: + clk_hz = 24000000; + break; + case 1: + case 3: + clk_hz = (sdhci->id == 2) ? 800000000 : 400000000; + break; + case 2: + case 4: + clk_hz = (sdhci->id == 2) ? 600000000 : 300000000; + break; + default: + printk_debug("SMHC: wrong clock source %u\n", clk.clk_sel); + break; + } + + // Calculate the actual clock frequency based on the divider values + return clk_hz / (clk.factor_n + 1) / (clk.factor_m + 1); +} \ No newline at end of file