diff --git a/board/100ask-t113s3/CMakeLists.txt b/board/100ask-t113s3/CMakeLists.txt new file mode 100644 index 00000000..27e2805d --- /dev/null +++ b/board/100ask-t113s3/CMakeLists.txt @@ -0,0 +1,7 @@ +set(APP_COMMON_SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/start.S + ${CMAKE_CURRENT_SOURCE_DIR}/board.c + ${CMAKE_CURRENT_SOURCE_DIR}/eabi_compat.c +) + +add_subdirectory(hello_world) diff --git a/board/100ask-t113s3/README.md b/board/100ask-t113s3/README.md new file mode 100644 index 00000000..fc121158 --- /dev/null +++ b/board/100ask-t113s3/README.md @@ -0,0 +1,29 @@ +# SyterKit Common + +## start.S + +This code snippet is an ARM assembly language program that includes initialization settings and exception handlers. Here's a breakdown of its functionalities: + +1. Initialization Settings: It sets registers and writes specific values to configure the processor's working mode, interrupt enable, etc. + +2. Set Vector Table: It writes the address of the vector table to the Vector Base Address Register, which is used for handling exceptions and interrupts. + +3. Enable NEON/VFP Unit: It configures the processor to enable the NEON (Advanced SIMD) and VFP (Floating-Point) units. + +4. Clear BSS Section: It zeroes out variables in the BSS section. + +5. Disable Interrupts: It disables FIQ and IRQ interrupts and switches the processor to SVC32 mode. + +6. Set Timer Frequency: It sets the timer frequency to 24M. + +7. Call the main Function: It jumps to the main function to execute the main logic. + +## eabi_compat.c + +This code snippet appears to be providing implementations for the functions `abort`, `raise`, and `__aeabi_unwind_cpp_pr0`. Here's a breakdown of their functionalities: + +1. `void abort(void)`: This function creates an infinite loop, causing the program to hang indefinitely. It is typically used to indicate a critical error or unrecoverable condition in a program. + +2. `int raise(int signum)`: This function is a placeholder and always returns 0. In standard C, this function is used to raise a signal and initiate the corresponding signal handler. However, in this implementation, it does nothing and simply returns 0. + +3. `void __aeabi_unwind_cpp_pr0(void)`: This is a dummy function that serves as a placeholder to avoid linker complaints. Its purpose is to satisfy the linker when using C++ exceptions and unwinding, but it does not contain any actual functionality. diff --git a/board/100ask-t113s3/board.c b/board/100ask-t113s3/board.c new file mode 100644 index 00000000..16b51175 --- /dev/null +++ b/board/100ask-t113s3/board.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +sunxi_serial_t uart_dbg = { + .base = 0x02500000, + .id = 0, + .gpio_tx = {GPIO_PIN(GPIO_PORTE, 2), GPIO_PERIPH_MUX5}, + .gpio_rx = {GPIO_PIN(GPIO_PORTE, 3), GPIO_PERIPH_MUX5}, +}; + +sunxi_spi_t sunxi_spi0 = { + .base = 0x04025000, + .id = 0, + .clk_rate = 75 * 1000 * 1000, + .gpio_cs = {GPIO_PIN(GPIO_PORTC, 1), GPIO_PERIPH_MUX4}, + .gpio_sck = {GPIO_PIN(GPIO_PORTC, 0), GPIO_PERIPH_MUX4}, + .gpio_mosi = {GPIO_PIN(GPIO_PORTC, 2), GPIO_PERIPH_MUX4}, + .gpio_miso = {GPIO_PIN(GPIO_PORTC, 3), GPIO_PERIPH_MUX4}, + .gpio_wp = {GPIO_PIN(GPIO_PORTC, 4), GPIO_PERIPH_MUX4}, + .gpio_hold = {GPIO_PIN(GPIO_PORTC, 5), GPIO_PERIPH_MUX4}, +}; + +sdhci_t sdhci0 = { + .name = "sdhci0", + .reg = (sdhci_reg_t *) 0x04020000, + .voltage = MMC_VDD_27_36, + .width = MMC_BUS_WIDTH_4, + .clock = MMC_CLK_50M, + .removable = 0, + .isspi = FALSE, + .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}, +}; + +void clean_syterkit_data(void) { + /* Disable MMU, data cache, instruction cache, interrupts */ + arm32_mmu_disable(); + printk(LOG_LEVEL_INFO, "disable mmu ok...\n"); + arm32_dcache_disable(); + printk(LOG_LEVEL_INFO, "disable dcache ok...\n"); + arm32_icache_disable(); + printk(LOG_LEVEL_INFO, "disable icache ok...\n"); + arm32_interrupt_disable(); + printk(LOG_LEVEL_INFO, "free interrupt ok...\n"); +} diff --git a/board/100ask-t113s3/eabi_compat.c b/board/100ask-t113s3/eabi_compat.c new file mode 100644 index 00000000..93303d89 --- /dev/null +++ b/board/100ask-t113s3/eabi_compat.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +void abort(void) { + while (1) + ; +} + +int raise(int signum) { + return 0; +} + +/* Dummy function to avoid linker complaints */ +void __aeabi_unwind_cpp_pr0(void) { +} \ No newline at end of file diff --git a/board/100ask-t113s3/hello_world/CMakeLists.txt b/board/100ask-t113s3/hello_world/CMakeLists.txt new file mode 100644 index 00000000..b5ce1ea5 --- /dev/null +++ b/board/100ask-t113s3/hello_world/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +add_syterkit_app(helloworld + main.c +) \ No newline at end of file diff --git a/board/100ask-t113s3/hello_world/main.c b/board/100ask-t113s3/hello_world/main.c new file mode 100644 index 00000000..b588126a --- /dev/null +++ b/board/100ask-t113s3/hello_world/main.c @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include + +#include + +extern sunxi_serial_t uart_dbg; + +int main(void) { + sunxi_serial_init(&uart_dbg); + + sunxi_clk_init(); + + printk(LOG_LEVEL_INFO, "Hello World!\n"); + + return 0; +} \ No newline at end of file diff --git a/board/100ask-t113s3/start.S b/board/100ask-t113s3/start.S new file mode 100644 index 00000000..b55ac933 --- /dev/null +++ b/board/100ask-t113s3/start.S @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#define ARMV7_USR_MODE 0x10 +#define ARMV7_FIQ_MODE 0x11 +#define ARMV7_IRQ_MODE 0x12 +#define ARMV7_SVC_MODE 0x13 +#define ARMV7_MON_MODE 0x16 +#define ARMV7_ABT_MODE 0x17 +#define ARMV7_UND_MODE 0x1b +#define ARMV7_SYSTEM_MODE 0x1f +#define ARMV7_MODE_MASK 0x1f +#define ARMV7_FIQ_MASK 0x40 +#define ARMV7_IRQ_MASK 0x80 + +.arm +.globl reset +.text + +reset: + /* Boot head information for BROM */ + .long 0xea000016 + .byte 'e', 'G', 'O', 'N', '.', 'B', 'T', '0' + .long 0x12345678 /* checksum */ + .long __spl_size /* spl size */ + .long 0x30 /* boot header size */ + .long 0x30303033 /* boot header version */ + .long 0x00020000 /* return value */ + .long 0x00028000 /* run address */ + .long 0x0 /* eGON version */ + .byte 0x00, 0x00, 0x00, 0x00 /* platform information - 8byte */ + .byte 0x34, 0x2e, 0x30, 0x00 + + /* + * The actual reset code + */ + mrs r0, cpsr + bic r0, r0, #ARMV7_MODE_MASK + orr r0, r0, #ARMV7_SVC_MODE + orr r0, r0, #(ARMV7_IRQ_MASK | ARMV7_FIQ_MASK) + bic r0, r0, #(1<<9) @set little-endian + msr cpsr_c, r0 + + /* Set vector base address register */ + + ldr r0, =_vector + mcr p15, 0, r0, c12, c0, 0 + mrc p15, 0, r0, c1, c0, 0 + bic r0, #(1 << 13) + mcr p15, 0, r0, c1, c0, 0 + + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #0x00002000 @ clear bits 13 (--V-) + bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) + orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB + bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache + mcr p15, 0, r0, c1, c0, 0 + + /* Enable neon/vfp unit */ + mrc p15, 0, r0, c1, c0, 2 + orr r0, r0, #(0xf << 20) + mcr p15, 0, r0, c1, c0, 2 + isb + mov r0, #0x40000000 + vmsr fpexc, r0 + + /* Set stack pointer */ + ldr sp, =__stack_srv_end + + bl clear_bss + + /* + * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode, + * except if in HYP mode already + */ + mrs r0, cpsr + and r1, r0, #0x1f @ mask mode bits + teq r1, #0x1a @ test for HYP mode + bicne r0, r0, #0x1f @ clear all mode bits + orrne r0, r0, #0x13 @ set SVC mode + orr r0, r0, #0xc0 @ disable FIQ and IRQ + msr cpsr,r0 + + @set cntfrq to 24M + ldr r0, =24000000 + mcr p15, 0, r0, c14, c0, 0 + + bl set_timer_count + + bl main + + clear_bss: + ldr r0, =_sbss + ldr r1, =_ebss + mov r2, #0 + + clbss_1: + stmia r0!, {r2} + cmp r0, r1 + blt clbss_1 + + mov pc, lr + +_vector: + b reset + ldr pc, _undefined_instruction + ldr pc, _software_interrupt + ldr pc, _prefetch_abort + ldr pc, _data_abort + ldr pc, _not_used + ldr pc, _irq + ldr pc, _fiq + +_undefined_instruction: + .word undefined_instruction +_software_interrupt: + .word software_interrupt +_prefetch_abort: + .word prefetch_abort +_data_abort: + .word data_abort +_not_used: + .word not_used +_irq: + .word irq +_fiq: + .word fiq + +.macro save_regs + str lr, [sp, #-4] + mrs lr, spsr_all + str lr, [sp, #-8] + str r1, [sp, #-12] + str r0, [sp, #-16] + mov r0, sp + cps #0x13 + ldr r1, [r0, #-4] + str r1, [sp, #-4]! + ldr r1, [r0, #-8] + str r1, [sp, #-(4 * 16)] + ldr r1, [r0, #-12] + ldr r0, [r0, #-16] + stmdb sp, {r0 - r14}^ + sub sp, sp, #(4 * 16) + ldr r4, [sp] + and r0, r4, #0x1f + cmp r0, #0x10 + beq 10f + cmp r0, #0x13 + beq 11f + b . +11: add r1, sp, #(4 * 17) + str r1, [sp, #(4 * 14)] + str lr, [sp, #(4 * 15)] +10: add r1, sp, #(4 * 17) + str r1, [sp, #-4]! + mov r0, sp +.endm + +.macro restore_regs + mov r12, sp + ldr sp, [r12], #4 + ldr r1, [r12], #4 + msr spsr_cxsf, r1 + and r0, r1, #0x1f + cmp r0, #0x10 + beq 20f + cmp r0, #0x13 + beq 21f + b . +20: ldr lr, [r12, #(4 * 15)] + ldmia r12, {r0 - r14}^ + movs pc, lr +21: ldm r12, {r0 - r15}^ + mov r0, r0 +.endm + + /* + * Exception handlers + */ + .align 5 +undefined_instruction: + sub lr, lr, #4 + save_regs + bl arm32_do_undefined_instruction + restore_regs + + .align 5 +software_interrupt: + sub lr, lr, #4 + save_regs + bl arm32_do_software_interrupt + restore_regs + + .align 5 +prefetch_abort: + sub lr, lr, #4 + save_regs + bl arm32_do_prefetch_abort + restore_regs + + .align 5 +data_abort: + sub lr, lr, #8 + save_regs + bl arm32_do_data_abort + restore_regs + + .align 5 +not_used: + b . + + .align 5 +irq: + sub lr, lr, #4 + save_regs + bl arm32_do_irq + restore_regs + + .align 5 +fiq: + sub lr, lr, #4 + save_regs + bl arm32_do_fiq + restore_regs diff --git a/board/CMakeLists.txt b/board/CMakeLists.txt index 97c2b6fd..5c83f708 100644 --- a/board/CMakeLists.txt +++ b/board/CMakeLists.txt @@ -4,4 +4,8 @@ endif() if(CONFIG_BOARD_YUZUKILIZARD) add_subdirectory(yuzukilizard) +endif() + +if(CONFIG_BOARD_100ASK-T113S3) + add_subdirectory(100ask-t113s3) endif() \ No newline at end of file diff --git a/cmake/board/100ask-t113s3.cmake b/cmake/board/100ask-t113s3.cmake new file mode 100644 index 00000000..d10e3406 --- /dev/null +++ b/cmake/board/100ask-t113s3.cmake @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: Apache-2.0 + +set(CONFIG_ARCH_ARM32 True) +set(CONFIG_CHIP_SUN8IW20 True) +set(CONFIG_BOARD_100ASK-T113S3 True) + +add_definitions(-DCONFIG_CHIP_SUN8IW20) + +# Options + +# By setting ENABLE_HARDFP to ON, it indicates that the project is configured +# to utilize hard floating-point operations when applicable. This can be beneficial +# in scenarios where performance gains from hardware acceleration are desired. +option(ENABLE_HARDFP "Enable hardware floating-point operations" ON) + +# Set the cross-compile toolchain +set(CROSS_COMPILE "arm-none-eabi-") +set(CROSS_COMPILE ${CROSS_COMPILE} CACHE STRING "CROSS_COMPILE Toolchain") + +# Set the C and C++ compilers +set(CMAKE_C_COMPILER "${CROSS_COMPILE}gcc") +set(CMAKE_CXX_COMPILER "${CROSS_COMPILE}g++") + +# Configure compiler flags based on ENABLE_HARDFP option +if(ENABLE_HARDFP) + set(CMAKE_COMMON_FLAGS "-nostdlib -g -ggdb -O3 -mcpu=cortex-a7 -mthumb-interwork -mthumb -mno-unaligned-access -mfpu=neon-vfpv4 -mfloat-abi=hard") +else() + set(CMAKE_COMMON_FLAGS "-nostdlib -g -ggdb -O3 -mcpu=cortex-a7 -mthumb-interwork -mthumb -mno-unaligned-access -mfpu=neon-vfpv4 -mfloat-abi=softfp") +endif() + +# Disable specific warning flags for C and C++ compilers +set(CMAKE_C_DISABLE_WARN_FLAGS "-Wno-int-to-pointer-cast -Wno-implicit-function-declaration -Wno-discarded-qualifiers") +set(CMAKE_CXX_DISABLE_WARN_FLAGS "-Wno-int-to-pointer-cast") diff --git a/include/drivers/reg/reg-ncat.h b/include/drivers/reg/reg-ncat.h index 11db4690..8f1612e6 100644 --- a/include/drivers/reg/reg-ncat.h +++ b/include/drivers/reg/reg-ncat.h @@ -4,9 +4,11 @@ #define __G_REG_NCAT_H__ #if defined(CONFIG_CHIP_SUN8IW21) -#include + #include +#elif defined(CONFIG_CHIP_SUN8IW20) + #include #else -#error "Unsupported chip" + #error "Unsupported chip" #endif #endif // __G_REG_NCAT_H__ \ No newline at end of file diff --git a/include/drivers/sun8iw20/reg/reg-ccu.h b/include/drivers/sun8iw20/reg/reg-ccu.h new file mode 100644 index 00000000..b0780f2d --- /dev/null +++ b/include/drivers/sun8iw20/reg/reg-ccu.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef __SUN8IW20_REG_CCU_H__ +#define __SUN8IW20_REG_CCU_H__ + +#include + +#define CCU_BASE (0x02001000) + +#define CCU_PLL_CPU_CTRL_REG (0x000) +#define CCU_PLL_DDR_CTRL_REG (0x010) +#define CCU_PLL_PERI0_CTRL_REG (0x020) +#define CCU_PLL_PERI1_CTRL_REG (0x028) +#define CCU_PLL_GPU_CTRL_REG (0x030) +#define CCU_PLL_VIDEO0_CTRL_REG (0x040) +#define CCU_PLL_VIDEO1_CTRL_REG (0x048) +#define CCU_PLL_VE_CTRL (0x058) +#define CCU_PLL_DE_CTRL (0x060) +#define CCU_PLL_HSIC_CTRL (0x070) +#define CCU_PLL_AUDIO0_CTRL_REG (0x078) +#define CCU_PLL_AUDIO1_CTRL_REG (0x080) +#define CCU_PLL_DDR_PAT0_CTRL_REG (0x110) +#define CCU_PLL_DDR_PAT1_CTRL_REG (0x114) +#define CCU_PLL_PERI0_PAT0_CTRL_REG (0x120) +#define CCU_PLL_PERI0_PAT1_CTRL_REG (0x124) +#define CCU_PLL_PERI1_PAT0_CTRL_REG (0x128) +#define CCU_PLL_PERI1_PAT1_CTRL_REG (0x12c) +#define CCU_PLL_GPU_PAT0_CTRL_REG (0x130) +#define CCU_PLL_GPU_PAT1_CTRL_REG (0x134) +#define CCU_PLL_VIDEO0_PAT0_CTRL_REG (0x140) +#define CCU_PLL_VIDEO0_PAT1_CTRL_REG (0x144) +#define CCU_PLL_VIDEO1_PAT0_CTRL_REG (0x148) +#define CCU_PLL_VIDEO1_PAT1_CTRL_REG (0x14c) +#define CCU_PLL_VE_PAT0_CTRL_REG (0x158) +#define CCU_PLL_VE_PAT1_CTRL_REG (0x15c) +#define CCU_PLL_DE_PAT0_CTRL_REG (0x160) +#define CCU_PLL_DE_PAT1_CTRL_REG (0x164) +#define CCU_PLL_HSIC_PAT0_CTRL_REG (0x170) +#define CCU_PLL_HSIC_PAT1_CTRL_REG (0x174) +#define CCU_PLL_AUDIO0_PAT0_CTRL_REG (0x178) +#define CCU_PLL_AUDIO0_PAT1_CTRL_REG (0x17c) +#define CCU_PLL_AUDIO1_PAT0_CTRL_REG (0x180) +#define CCU_PLL_AUDIO1_PAT1_CTRL_REG (0x184) +#define CCU_PLL_CPU_BIAS_REG (0x300) +#define CCU_PLL_DDR_BIAS_REG (0x310) +#define CCU_PLL_PERI0_BIAS_REG (0x320) +#define CCU_PLL_PERI1_BIAS_REG (0x328) +#define CCU_PLL_GPU_BIAS_REG (0x330) +#define CCU_PLL_VIDEO0_BIAS_REG (0x340) +#define CCU_PLL_VIDEO1_BIAS_REG (0x348) +#define CCU_PLL_VE_BIAS_REG (0x358) +#define CCU_PLL_DE_BIAS_REG (0x360) +#define CCU_PLL_HSIC_BIAS_REG (0x370) +#define CCU_PLL_AUDIO0_BIAS_REG (0x378) +#define CCU_PLL_AUDIO1_BIAS_REG (0x380) +#define CCU_PLL_CPU_TUN_REG (0x400) +#define CCU_CPU_AXI_CFG_REG (0x500) +#define CCU_CPU_GATING_REG (0x504) +#define CCU_PSI_CLK_REG (0x510) +#define CCU_AHB3_CLK_REG (0x51c) +#define CCU_APB0_CLK_REG (0x520) +#define CCU_APB1_CLK_REG (0x524) +#define CCU_MBUS_CLK_REG (0x540) +#define CCU_DMA_BGR_REG (0x70c) +#define CCU_DRAM_CLK_REG (0x800) +#define CCU_MBUS_MAT_CLK_GATING_REG (0x804) +#define CCU_DRAM_BGR_REG (0x80c) +#define CCU_SMHC0_CLK_REG (0x830) +#define CCU_SMHC_BGR_REG (0x84c) +#define CCU_UART_BGR_REG (0x90c) +#define CCU_TWI_BGR_REG (0x91c) +#define CCU_SCR_BGR_REG (0x93C) +#define CCU_SPI0_CLK_REG (0x940) +#define CCU_SPI_BGR_REG (0x96c) +#define CCU_RISCV_CLK_REG (0xd00) +#define CCU_RISCV_GATING_REG (0xd04) +#define CCU_RISCV_CFG_BGR_REG (0xd0c) + +/* MMC clock bit field */ +#define CCU_MMC_CTRL_M(x) ((x) -1) +#define CCU_MMC_CTRL_N(x) ((x) << 8) +#define CCU_MMC_CTRL_OSCM24 (0x0 << 24) +#define CCU_MMC_CTRL_PLL6X1 (0x1 << 24) +#define CCU_MMC_CTRL_PLL6X2 (0x2 << 24) +#define CCU_MMC_CTRL_PLL_PERIPH1X CCU_MMC_CTRL_PLL6X1 +#define CCU_MMC_CTRL_PLL_PERIPH2X CCU_MMC_CTRL_PLL6X2 +#define CCU_MMC_CTRL_ENABLE (0x1 << 31) +/* if doesn't have these delays */ +#define CCU_MMC_CTRL_OCLK_DLY(a) ((void) (a), 0) +#define CCU_MMC_CTRL_SCLK_DLY(a) ((void) (a), 0) + +#define CCU_MMC_BGR_SMHC0_GATE (1 << 0) +#define CCU_MMC_BGR_SMHC1_GATE (1 << 1) +#define CCU_MMC_BGR_SMHC2_GATE (1 << 2) + +#define CCU_MMC_BGR_SMHC0_RST (1 << 16) +#define CCU_MMC_BGR_SMHC1_RST (1 << 17) +#define CCU_MMC_BGR_SMHC2_RST (1 << 18) + + +#endif// __SUN8IW20_REG_CCU_H__ diff --git a/include/drivers/sun8iw20/reg/reg-dma.h b/include/drivers/sun8iw20/reg/reg-dma.h new file mode 100644 index 00000000..26a23ba6 --- /dev/null +++ b/include/drivers/sun8iw20/reg/reg-dma.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef __SUN8IW20_REG_DMA_H__ +#define __SUN8IW20_REG_DMA_H__ + +#include + +#define SUNXI_DMA_CHANNEL_BASE (SUNXI_DMA_BASE + 0x100) +#define DMA_AUTO_GATE_REG (SUNXI_DMA_BASE + 0x28) + +#define SUNXI_DMA_CHANNEL_SIZE (0x40) +#define SUNXI_DMA_LINK_NULL (0xfffff800) + +#define DMAC_DMATYPE_NORMAL 0 +#define DMAC_CFG_TYPE_DRAM (1) +#define DMAC_CFG_TYPE_SRAM (0) + +#define DMAC_CFG_TYPE_SPI0 (22) +#define DMAC_CFG_TYPE_SHMC0 (20) +#define DMAC_CFG_SRC_TYPE_NAND (5) + +/* DMA base config */ +#define DMAC_CFG_CONTINUOUS_ENABLE (0x01) +#define DMAC_CFG_CONTINUOUS_DISABLE (0x00) + +/* ----------DMA dest config-------------------- */ +/* DMA dest width config */ +#define DMAC_CFG_DEST_DATA_WIDTH_8BIT (0x00) +#define DMAC_CFG_DEST_DATA_WIDTH_16BIT (0x01) +#define DMAC_CFG_DEST_DATA_WIDTH_32BIT (0x02) +#define DMAC_CFG_DEST_DATA_WIDTH_64BIT (0x03) + +/* DMA dest bust config */ +#define DMAC_CFG_DEST_1_BURST (0x00) +#define DMAC_CFG_DEST_4_BURST (0x01) +#define DMAC_CFG_DEST_8_BURST (0x02) +#define DMAC_CFG_DEST_16_BURST (0x03) + +#define DMAC_CFG_DEST_ADDR_TYPE_LINEAR_MODE (0x00) +#define DMAC_CFG_DEST_ADDR_TYPE_IO_MODE (0x01) + +/* ----------DMA src config -------------------*/ +#define DMAC_CFG_SRC_DATA_WIDTH_8BIT (0x00) +#define DMAC_CFG_SRC_DATA_WIDTH_16BIT (0x01) +#define DMAC_CFG_SRC_DATA_WIDTH_32BIT (0x02) +#define DMAC_CFG_SRC_DATA_WIDTH_64BIT (0x03) + +#define DMAC_CFG_SRC_1_BURST (0x00) +#define DMAC_CFG_SRC_4_BURST (0x01) +#define DMAC_CFG_SRC_8_BURST (0x02) +#define DMAC_CFG_SRC_16_BURST (0x03) + +#define DMAC_CFG_SRC_ADDR_TYPE_LINEAR_MODE (0x00) +#define DMAC_CFG_SRC_ADDR_TYPE_IO_MODE (0x01) + +/*dma int config*/ +#define DMA_PKG_HALF_INT (1 << 0) +#define DMA_PKG_END_INT (1 << 1) +#define DMA_QUEUE_END_INT (1 << 2) + +#endif// __SUN8IW20_REG_DMA_H__ \ No newline at end of file diff --git a/include/drivers/sun8iw20/reg/reg-dram.h b/include/drivers/sun8iw20/reg/reg-dram.h new file mode 100644 index 00000000..fac95eb8 --- /dev/null +++ b/include/drivers/sun8iw20/reg/reg-dram.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __SUN8IW20_REG_DRAM_H__ +#define __SUN8IW20_REG_DRAM_H__ + +#define MCTL_COM_BASE (0x3102000) +#define MCTL_COM_WORK_MODE0 (0x00) +#define MCTL_COM_WORK_MODE1 (0x04) +#define MCTL_COM_DBGCR (0x08) +#define MCTL_COM_TMR (0x0c) +#define MCTL_COM_CCCR (0x14) +#define MCTL_COM_MAER0 (0x20) +#define MCTL_COM_MAER1 (0x24) +#define MCTL_COM_MAER2 (0x28) +#define MCTL_COM_REMAP0 (0x500) +#define MCTL_COM_REMAP1 (0x504) +#define MCTL_COM_REMAP2 (0x508) +#define MCTL_COM_REMAP3 (0x50c) + +#define MCTL_PHY_BASE (0x3103000) +#define MCTL_PHY_PIR (0x00) +#define MCTL_PHY_PWRCTL (0x04) +#define MCTL_PHY_MRCTRL0 (0x08) +#define MCTL_PHY_CLKEN (0x0c) +#define MCTL_PHY_PGSR0 (0x10) +#define MCTL_PHY_PGSR1 (0x14) +#define MCTL_PHY_STATR (0x18) +#define MCTL_PHY_LP3MR11 (0x2c) +#define MCTL_PHY_DRAM_MR0 (0x30) +#define MCTL_PHY_DRAM_MR1 (0x34) +#define MCTL_PHY_DRAM_MR2 (0x38) +#define MCTL_PHY_DRAM_MR3 (0x3c) +#define MCTL_PHY_PTR0 (0x44) +#define MCTL_PHY_PTR2 (0x4c) +#define MCTL_PHY_PTR3 (0x50) +#define MCTL_PHY_PTR4 (0x54) +#define MCTL_PHY_DRAMTMG0 (0x58) +#define MCTL_PHY_DRAMTMG1 (0x5c) +#define MCTL_PHY_DRAMTMG2 (0x60) +#define MCTL_PHY_DRAMTMG3 (0x64) +#define MCTL_PHY_DRAMTMG4 (0x68) +#define MCTL_PHY_DRAMTMG5 (0x6c) +#define MCTL_PHY_DRAMTMG6 (0x70) +#define MCTL_PHY_DRAMTMG7 (0x74) +#define MCTL_PHY_DRAMTMG8 (0x78) +#define MCTL_PHY_ODTCFG (0x7c) +#define MCTL_PHY_PITMG0 (0x80) +#define MCTL_PHY_PITMG1 (0x84) +#define MCTL_PHY_LPTPR (0x88) +#define MCTL_PHY_RFSHCTL0 (0x8c) +#define MCTL_PHY_RFSHTMG (0x90) +#define MCTL_PHY_RFSHCTL1 (0x94) +#define MCTL_PHY_PWRTMG (0x98) +#define MCTL_PHY_ASRC (0x9c) +#define MCTL_PHY_ASRTC (0xa0) +#define MCTL_PHY_VTFCR (0xb8) +#define MCTL_PHY_DQSGMR (0xbc) +#define MCTL_PHY_DTCR (0xc0) +#define MCTL_PHY_DTAR0 (0xc4) +#define MCTL_PHY_PGCR0 (0x100) +#define MCTL_PHY_PGCR1 (0x104) +#define MCTL_PHY_PGCR2 (0x108) +#define MCTL_PHY_PGCR3 (0x10c) +#define MCTL_PHY_IOVCR0 (0x110) +#define MCTL_PHY_IOVCR1 (0x114) +#define MCTL_PHY_DXCCR (0x11c) +#define MCTL_PHY_ODTMAP (0x120) +#define MCTL_PHY_ZQCTL0 (0x124) +#define MCTL_PHY_ZQCTL1 (0x128) +#define MCTL_PHY_ZQCR (0x140) +#define MCTL_PHY_ZQSR (0x144) +#define MCTL_PHY_ZQDR0 (0x148) +#define MCTL_PHY_ZQDR1 (0x14c) +#define MCTL_PHY_ZQDR2 (0x150) +#define MCTL_PHY_SCHED (0x1c0) +#define MCTL_PHY_PERFHPR0 (0x1c4) +#define MCTL_PHY_PERFHPR1 (0x1c8) +#define MCTL_PHY_PERFLPR0 (0x1cc) +#define MCTL_PHY_PERFLPR1 (0x1d0) +#define MCTL_PHY_PERFWR0 (0x1d4) +#define MCTL_PHY_PERFWR1 (0x1d8) +#define MCTL_PHY_ACMDLR (0x200) +#define MCTL_PHY_ACLDLR (0x204) +#define MCTL_PHY_ACIOCR0 (0x208) +#define MCTL_PHY_ACIOCR1(x) (0x210 + 0x4 * x) +#define MCTL_PHY_DXnMDLR(x) (0x300 + 0x80 * x) +#define MCTL_PHY_DXnLDLR0(x) (0x304 + 0x80 * x) +#define MCTL_PHY_DXnLDLR1(x) (0x308 + 0x80 * x) +#define MCTL_PHY_DXnLDLR2(x) (0x30c + 0x80 * x) +#define MCTL_PHY_DXIOCR (0x310) +#define MCTL_PHY_DATX0IOCR(x) (0x310 + 0x4 * x) +#define MCTL_PHY_DATX1IOCR(x) (0x390 + 0x4 * x) +#define MCTL_PHY_DATX2IOCR(x) (0x410 + 0x4 * x) +#define MCTL_PHY_DATX3IOCR(x) (0x490 + 0x4 * x) +#define MCTL_PHY_DXnSDLR6(x) (0x33c + 0x80 * x) +#define MCTL_PHY_DXnGTR(x) (0x340 + 0x80 * x) +#define MCTL_PHY_DXnGCR0(x) (0x344 + 0x80 * x) +#define MCTL_PHY_DXnGSR0(x) (0x348 + 0x80 * x) + +#define SYS_CONTROL_REG_BASE (0x3000000) +#define LDO_CTAL_REG (0x150) +#define ZQ_CAL_CTRL_REG (0x160) +#define ZQ_RES_CTRL_REG (0x168) +#define ZQ_RES_STATUS_REG (0x16c) + +#define SYS_SID_BASE (0x3006000) +#define SYS_EFUSE_REG (0x228) +#define SYS_LDOB_SID (0x21c) + +#define SUNXI_R_CPUCFG_BASE (0x7000400) +#define SUNXI_R_CPUCFG_SUP_STAN_FLAG (0x1d4) + +#define R_PRCM_BASE (0x7010000) +#define VDD_SYS_PWROFF_GATING_REG (0x250) +#define ANALOG_PWROFF_GATING_REG (0x254) + +#endif /* __SUN8IW20_REG_DRAM_H__ */ \ No newline at end of file diff --git a/include/drivers/sun8iw20/reg/reg-ncat.h b/include/drivers/sun8iw20/reg/reg-ncat.h new file mode 100644 index 00000000..d587bcdf --- /dev/null +++ b/include/drivers/sun8iw20/reg/reg-ncat.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __SUN8IW20_REG_NCAT_H__ +#define __SUN8IW20_REG_NCAT_H__ + +/*CPUX*/ +#define SUNXI_CPUXCFG_BASE (0x08100000) + +/*sys ctrl*/ +#define SUNXI_TIMER_BASE (0x02050000) +#define SUNXI_CCM_BASE (0x02001000) +#define SUNXI_PIO_BASE (0x02000000) +#define SUNXI_SPC_BASE (0x02000800) +#define SUNXI_SYSCRL_BASE (0x03000000) +#define SUNXI_DMA_BASE (0x03002000) +#define SUNXI_SID_BASE (0x03006000) +#define SUNXI_SID_SRAM_BASE (0x03006200) + +#define SUNXI_WDOG_BASE (0x020500A0) + +#define SUNXI_CE_BASE (0x03040000) +#define SUNXI_SS_BASE SUNXI_CE_BASE + +#define SUNXI_SMC_BASE (0x03007000) + +/*storage*/ +#define SUNXI_SMHC0_BASE (0x04020000) +#define SUNXI_SMHC1_BASE (0x04021000) + +/*noraml*/ +#define SUNXI_UART0_BASE (0x02500000) +#define SUNXI_UART1_BASE (0x02500400) +#define SUNXI_UART2_BASE (0x02500800) +#define SUNXI_UART3_BASE (0x02500C00) + +#define SUNXI_TWI0_BASE (0x02502000) +#define SUNXI_TWI1_BASE (0x02502400) + +#define SUNXI_SPI0_BASE (0x04025000) +#define SUNXI_SPI1_BASE (0x04026000) +#define SUNXI_SPIF_BASE (0x04f00000) + +/*physical key*/ +#define SUNXI_GPADC_BASE (0x02009000) +#define SUNXI_LRADC_BASE (0x02009800) +#define SUNXI_KEYADC_BASE SUNXI_LRADC_BASE + +/*cpus*/ +#define SUNXI_RTC_BASE (0x07090000) +#define SUNXI_AUDIO_CODEC (0x02030000) +#define SUNXI_CPUS_CFG_BASE (0x07000400) +#define SUNXI_RCPUCFG_BASE (SUNXI_CPUS_CFG_BASE) +#define SUNXI_RPRCM_BASE (0x07010000) +#define SUNXI_RPWM_BASE (0x07020c00) +#define SUNXI_RPIO_BASE (0x07022000) +#define SUNXI_R_PIO_BASE SUNXI_RPIO_BASE +#define SUNXI_RTWI_BASE (0x07020800) +#define SUNXI_RRSB_BASE (0x07083000) +#define SUNXI_RSB_BASE SUNXI_RRSB_BASE +#define SUNXI_RTWI_BRG_REG (SUNXI_RPRCM_BASE + 0x019c) +#define SUNXI_RTWI0_RST_BIT (16) +#define SUNXI_RTWI0_GATING_BIT (0) +#define SUNXI_RST_BIT (16) +#define SUNXI_GATING_BIT (0) + +#define SUNXI_RTC_DATA_BASE (SUNXI_RTC_BASE + 0x100) +#define AUDIO_CODEC_BIAS_REG (SUNXI_AUDIO_CODEC + 0x320) +#define AUDIO_POWER_REG (SUNXI_AUDIO_CODEC + 0x348) +#define SUNXI_VER_REG (SUNXI_SYSCRL_BASE + 0x24) + +#define RES_CAL_CTRL_REG (SUNXI_RPRCM_BASE + 0X310) +#define ANA_PWR_RST_REG (SUNXI_RPRCM_BASE + 0X254) + +#define VDD_ADDA_OFF_GATING (9) +#define CAL_ANA_EN (1) +#define CAL_EN (0) + +#define RVBARADDR0_L (SUNXI_CPUXCFG_BASE + 0x40) +#define RVBARADDR0_H (SUNXI_CPUXCFG_BASE + 0x44) + +#define SRAM_CONTRL_REG0 (SUNXI_SYSCRL_BASE + 0x0) +#define SRAM_CONTRL_REG1 (SUNXI_SYSCRL_BASE + 0x4) + + +/* rtc check power off */ +#define FORCE_DETECTER_OUTPUT (1 << 7) +#define VCCIO_THRESHOLD_VOLTAGE_2_5 (0 << 4) +#define VCCIO_THRESHOLD_VOLTAGE_2_6 (1 << 4) +#define VCCIO_THRESHOLD_VOLTAGE_2_7 (2 << 4) +#define VCCIO_THRESHOLD_VOLTAGE_2_8 (3 << 4) +#define VCCIO_THRESHOLD_VOLTAGE_2_9 (4 << 4) +#define VCCIO_THRESHOLD_VOLTAGE_3_0 (5 << 4) +#define VCCIO_DET_BYPASS_EN (1 << 0) + +#endif // __SUN8IW20_REG_NCAT_H__ \ No newline at end of file diff --git a/include/drivers/sun8iw20/reg/reg-rproc.h b/include/drivers/sun8iw20/reg/reg-rproc.h new file mode 100644 index 00000000..eb0fcc0e --- /dev/null +++ b/include/drivers/sun8iw20/reg/reg-rproc.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef __REG_RPROC_H__ +#define __REG_RPROC_H__ + +#define RISCV_CFG_BASE (0x06010000)// Base address for RISC-V configuration +#define RISCV_STA_ADD_REG \ + (RISCV_CFG_BASE + 0x0204)// Register address for RISC-V start address + +#endif// __REG_RPROC_H__ \ No newline at end of file diff --git a/include/drivers/sun8iw20/sys-clk.h b/include/drivers/sun8iw20/sys-clk.h new file mode 100644 index 00000000..373a904c --- /dev/null +++ b/include/drivers/sun8iw20/sys-clk.h @@ -0,0 +1,12 @@ +#ifndef __SUN8IW20_CLK_H__ +#define __SUN8IW20_CLK_H__ + +#include "reg/reg-ccu.h" + +void sunxi_clk_init(void); + +void sunxi_clk_reset(void); + +void sunxi_clk_dump(void); + +#endif// __SUN8IW20_CLK_H__ \ No newline at end of file diff --git a/include/drivers/sun8iw20/sys-dram.h b/include/drivers/sun8iw20/sys-dram.h new file mode 100644 index 00000000..bce69de9 --- /dev/null +++ b/include/drivers/sun8iw20/sys-dram.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __SUN8IW20_SYS_DRAM_H__ +#define __SUN8IW20_SYS_DRAM_H__ + +#include +#include +#include +#include +#include +#include + +#include "reg/reg-dram.h" + +#define SDRAM_BASE (0x40000000) + +enum sunxi_dram_type { + SUNXI_DRAM_TYPE_DDR2 = 2, + SUNXI_DRAM_TYPE_DDR3 = 3, + SUNXI_DRAM_TYPE_LPDDR2 = 6, + SUNXI_DRAM_TYPE_LPDDR3 = 7, +}; + +typedef struct __DRAM_PARA { + // normal configuration + uint32_t dram_clk; + uint32_t dram_type;// dram_type DDR2: 2 DDR3: 3 LPDDR2: 6 LPDDR3: 7 DDR3L: 31 + uint32_t dram_zq; // do not need + uint32_t dram_odt_en; + + // control configuration + uint32_t dram_para1; + uint32_t dram_para2; + + // timing configuration + uint32_t dram_mr0; + uint32_t dram_mr1; + uint32_t dram_mr2; + uint32_t dram_mr3; + uint32_t dram_tpr0;// DRAMTMG0 + uint32_t dram_tpr1;// DRAMTMG1 + uint32_t dram_tpr2;// DRAMTMG2 + uint32_t dram_tpr3;// DRAMTMG3 + uint32_t dram_tpr4;// DRAMTMG4 + uint32_t dram_tpr5;// DRAMTMG5 + uint32_t dram_tpr6;// DRAMTMG8 + // reserved for future use + uint32_t dram_tpr7; + uint32_t dram_tpr8; + uint32_t dram_tpr9; + uint32_t dram_tpr10; + uint32_t dram_tpr11; + uint32_t dram_tpr12; + uint32_t dram_tpr13; +} dram_para_t; + +int init_DRAM(int type, dram_para_t *para); + +uint64_t sunxi_dram_init(void); + +#endif // __SUN8IW20_SYS_DRAM_H__ diff --git a/include/drivers/sun8iw20/sys-rproc.h b/include/drivers/sun8iw20/sys-rproc.h new file mode 100644 index 00000000..da23ea50 --- /dev/null +++ b/include/drivers/sun8iw20/sys-rproc.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef __SUN8IW20_SYS_RPROC_H__ +#define __SUN8IW20_SYS_RPROC_H__ + +#include "reg/reg-rproc.h" + +void sunxi_e907_clock_init(uint32_t addr); + +void sunxi_e907_clock_reset(void); + +#endif// __SUN8IW20_SYS_RPROC_H__ \ No newline at end of file diff --git a/include/drivers/sun8iw20/sys-sid.h b/include/drivers/sun8iw20/sys-sid.h new file mode 100644 index 00000000..4b296cad --- /dev/null +++ b/include/drivers/sun8iw20/sys-sid.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef __SUN8IW20_SYS_SID_H__ +#define __SUN8IW20_SYS_SID_H__ + +#include +#include +#include +#include +#include +#include + +#include "log.h" + +#include + +static const struct sid_section_t { + char *name; + uint32_t offset; + uint32_t size_bits; +} sids[] = { + {"chipid", 0x0000, 128}, + {"brom-conf-try", 0x0010, 32}, + {"thermal-sensor", 0x0014, 64}, + {"ft-zone", 0x001c, 128}, + {"tvout", 0x002c, 32}, + {"tvout-gamma", 0x0030, 64}, + {"oem-program", 0x0038, 64}, + {"write-protect", 0x0040, 32}, + {"read-protect", 0x0044, 32}, + {"reserved1", 0x0048, 64}, + {"huk", 0x0050, 192}, + {"reserved2", 0x0068, 64}, + {"rotpk", 0x0070, 256}, + {"ssk", 0x0090, 256}, + {"rssk", 0x00b0, 128}, + {"hdcp-hash", 0x00c0, 128}, + {"nv1", 0x00d0, 32}, + {"nv2", 0x00d4, 32}, + {"reserved3", 0x00d8, 96}, + {"oem-program-secure", 0x00e4, 224}, +}; + +enum { + SID_PRCTL = SUNXI_SID_BASE + 0x040, + SID_PRKEY = SUNXI_SID_BASE + 0x050, + SID_RDKEY = SUNXI_SID_BASE + 0x060, + EFUSE_HV_SWITCH = SUNXI_RTC_BASE + 0x204, +}; + +uint32_t efuse_read(uint32_t offset); + +void efuse_write(uint32_t offset, uint32_t value); + +#endif// __SUN8IW20_SYS_SID_H__ \ No newline at end of file diff --git a/include/drivers/sys-clk.h b/include/drivers/sys-clk.h index f624813b..4e81dc56 100644 --- a/include/drivers/sys-clk.h +++ b/include/drivers/sys-clk.h @@ -2,9 +2,11 @@ #define __SYS_CLK_H__ #if defined(CONFIG_CHIP_SUN8IW21) -#include + #include +#elif defined(CONFIG_CHIP_SUN8IW20) + #include #else -#error "Unsupported chip" + #error "Unsupported chip" #endif #endif // __SYS_CLK_H__ \ No newline at end of file diff --git a/include/drivers/sys-dram.h b/include/drivers/sys-dram.h index 8a2f53da..2e5b9bec 100644 --- a/include/drivers/sys-dram.h +++ b/include/drivers/sys-dram.h @@ -4,9 +4,11 @@ #define __SYS_DRAM_H__ #if defined(CONFIG_CHIP_SUN8IW21) -#include + #include +#elif defined(CONFIG_CHIP_SUN8IW20) + #include #else -#error "Unsupported chip" + #error "Unsupported chip" #endif #endif // __SYS_DRAM_H__ diff --git a/include/drivers/sys-sid.h b/include/drivers/sys-sid.h index 74a48778..debe9294 100644 --- a/include/drivers/sys-sid.h +++ b/include/drivers/sys-sid.h @@ -4,9 +4,11 @@ #define _SYS_SID_H_ #if defined(CONFIG_CHIP_SUN8IW21) -#include + #include +#elif defined(CONFIG_CHIP_SUN8IW20) + #include #else -#error "Unsupported chip" + #error "Unsupported chip" #endif #endif// _SYS_SID_H_ \ No newline at end of file diff --git a/src/drivers/sun8iw20/CMakeLists.txt b/src/drivers/sun8iw20/CMakeLists.txt index af38aa76..299731b2 100644 --- a/src/drivers/sun8iw20/CMakeLists.txt +++ b/src/drivers/sun8iw20/CMakeLists.txt @@ -1,2 +1,6 @@ add_library(chip_drivers-obj OBJECT + sys-clk.c + sys-sid.c + sys-wdt.c + sys-dram.c ) \ No newline at end of file diff --git a/src/drivers/sun8iw20/sys-clk.c b/src/drivers/sun8iw20/sys-clk.c new file mode 100644 index 00000000..c6b6289b --- /dev/null +++ b/src/drivers/sun8iw20/sys-clk.c @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +void set_pll_cpux_axi(void) { + uint32_t val; + + /* AXI: Select cpu clock src to PLL_PERI(1x) */ + write32(CCU_BASE + CCU_CPU_AXI_CFG_REG, (4 << 24) | (1 << 0)); + sdelay(10); + + /* Disable pll gating */ + val = read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG); + val &= ~(1 << 27); + write32(CCU_BASE + CCU_PLL_CPU_CTRL_REG, val); + + /* Enable pll ldo */ + val = read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG); + val |= (1 << 30); + write32(CCU_BASE + CCU_PLL_CPU_CTRL_REG, val); + sdelay(5); + + /* Set clk to 1008mhz (default) or CONFIG_CPU_FREQ */ + /* PLL_CPUX = 24 MHz*N/P */ + val = read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG); + val &= ~((0x3 << 16) | (0xff << 8) | (0x3 << 0)); + val |= (41 << 8); + write32(CCU_BASE + CCU_PLL_CPU_CTRL_REG, val); + + /* Lock enable */ + val = read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG); + val |= (1 << 29); + write32(CCU_BASE + CCU_PLL_CPU_CTRL_REG, val); + + /* Enable pll */ + val = read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG); + val |= (1 << 31); + write32(CCU_BASE + CCU_PLL_CPU_CTRL_REG, val); + + /* Wait pll stable */ + while (!(read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG) & (0x1 << 28))) + ; + sdelay(20); + + /* Enable pll gating */ + val = read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG); + val |= (1 << 27); + write32(CCU_BASE + CCU_PLL_CPU_CTRL_REG, val); + + /* Lock disable */ + val = read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG); + val &= ~(1 << 29); + write32(CCU_BASE + CCU_PLL_CPU_CTRL_REG, val); + sdelay(1); + + /* AXI: set and change cpu clk src to PLL_CPUX, PLL_CPUX:AXI0 = 1200MHz:600MHz */ + val = read32(CCU_BASE + CCU_CPU_AXI_CFG_REG); + val &= ~(0x07 << 24 | 0x3 << 16 | 0x3 << 8 | 0xf << 0);// Clear + val |= (0x03 << 24 | 0x0 << 16 | 0x1 << 8 | 0x1 << 0); // CLK_SEL=PLL_CPU/P, DIVP=0, DIV2=1, DIV1=1 + write32(CCU_BASE + CCU_CPU_AXI_CFG_REG, val); + sdelay(1); +} + +static void set_pll_periph0(void) { + uint32_t val; + + /* Periph0 has been enabled */ + if (read32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG) & (1 << 31)) + return; + + /* Set default val */ + write32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG, 0x63 << 8); + + /* Lock enable */ + val = read32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG); + val |= (1 << 29); + write32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG, val); + + /* Enabe pll 600m(1x) 1200m(2x) */ + val = read32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG); + val |= (1 << 31); + write32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG, val); + + /* Wait pll stable */ + while (!(read32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG) & (0x1 << 28))) + ; + sdelay(20); + + /* Lock disable */ + val = read32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG); + val &= ~(1 << 29); + write32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG, val); +} + +static void set_ahb(void) { + write32(CCU_BASE + CCU_PSI_CLK_REG, (2 << 0) | (0 << 8) | (0x03 << 24)); + sdelay(1); +} + +static void set_apb(void) { + write32(CCU_BASE + CCU_APB0_CLK_REG, (2 << 0) | (1 << 8) | (0x03 << 24)); + sdelay(1); +} + +static void set_dma(void) { + /* Dma reset */ + write32(CCU_BASE + CCU_DMA_BGR_REG, read32(CCU_BASE + CCU_DMA_BGR_REG) | (1 << 16)); + sdelay(20); + /* Enable gating clock for dma */ + write32(CCU_BASE + CCU_DMA_BGR_REG, read32(CCU_BASE + CCU_DMA_BGR_REG) | (1 << 0)); +} + +static void set_mbus(void) { + uint32_t val; + + /* Reset mbus domain */ + val = read32(CCU_BASE + CCU_MBUS_CLK_REG); + val |= (0x1 << 30); + write32(CCU_BASE + CCU_MBUS_CLK_REG, val); + sdelay(1); + + /* Enable mbus master clock gating */ + write32(CCU_BASE + CCU_MBUS_MAT_CLK_GATING_REG, 0x00000d87); +} + +static void set_module(virtual_addr_t addr) { + uint32_t val; + + if (!(read32(addr) & (1 << 31))) { + val = read32(addr); + write32(addr, val | (1 << 31) | (1 << 30)); + + /* Lock enable */ + val = read32(addr); + val |= (1 << 29); + write32(addr, val); + + /* Wait pll stable */ + while (!(read32(addr) & (0x1 << 28))) + ; + sdelay(20); + + /* Lock disable */ + val = read32(addr); + val &= ~(1 << 29); + write32(addr, val); + } +} + +void sunxi_clk_init(void) { + set_pll_cpux_axi(); + set_pll_periph0(); + set_ahb(); + set_apb(); + set_dma(); + set_mbus(); + set_module(CCU_BASE + CCU_PLL_PERI0_CTRL_REG); + set_module(CCU_BASE + CCU_PLL_VIDEO0_CTRL_REG); + set_module(CCU_BASE + CCU_PLL_VIDEO1_CTRL_REG); + set_module(CCU_BASE + CCU_PLL_VE_CTRL); + set_module(CCU_BASE + CCU_PLL_AUDIO0_CTRL_REG); + set_module(CCU_BASE + CCU_PLL_AUDIO1_CTRL_REG); +} + +uint32_t sunxi_clk_get_peri1x_rate() { + uint32_t reg32; + uint8_t plln, pllm, p0; + + /* PLL PERIx */ + reg32 = read32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG); + if (reg32 & (1 << 31)) { + plln = ((reg32 >> 8) & 0xff) + 1; + pllm = (reg32 & 0x01) + 1; + p0 = ((reg32 >> 16) & 0x03) + 1; + + return ((((24 * plln) / (pllm * p0)) >> 1) * 1000 * 1000); + } + + return 0; +} + +void sunxi_clk_dump() { + uint32_t reg32; + uint32_t cpu_clk_src; + uint32_t plln, pllm; + uint8_t p0; + uint8_t p1; + const char *clock_str; + + /* PLL CPU */ + reg32 = read32(CCU_BASE + CCU_CPU_AXI_CFG_REG); + cpu_clk_src = (reg32 >> 24) & 0x7; + + switch (cpu_clk_src) { + case 0x0: + clock_str = "OSC24M"; + break; + + case 0x1: + clock_str = "CLK32"; + break; + + case 0x2: + clock_str = "CLK16M_RC"; + break; + + case 0x3: + clock_str = "PLL_CPU"; + break; + + case 0x4: + clock_str = "PLL_PERI(1X)"; + break; + + case 0x5: + clock_str = "PLL_PERI(2X)"; + break; + + case 0x6: + clock_str = "PLL_PERI(800M)"; + break; + + default: + clock_str = "ERROR"; + } + + reg32 = read32(CCU_BASE + CCU_PLL_CPU_CTRL_REG); + p0 = (reg32 >> 16) & 0x03; + if (p0 == 0) { + p1 = 1; + } else if (p0 == 1) { + p1 = 2; + } else if (p0 == 2) { + p1 = 4; + } else { + p1 = 1; + } + + printk(LOG_LEVEL_DEBUG, "CLK: CPU PLL=%s FREQ=%" PRIu32 "MHz\r\n", clock_str, ((((reg32 >> 8) & 0xff) + 1) * 24 / p1)); + + /* PLL PERIx */ + reg32 = read32(CCU_BASE + CCU_PLL_PERI0_CTRL_REG); + if (reg32 & (1 << 31)) { + plln = ((reg32 >> 8) & 0xff) + 1; + pllm = (reg32 & 0x01) + 1; + p0 = ((reg32 >> 16) & 0x03) + 1; + p1 = ((reg32 >> 20) & 0x03) + 1; + + printk(LOG_LEVEL_DEBUG, "CLK: PLL_peri (2X)=%" PRIu32 "MHz, (1X)=%" PRIu32 "MHz, (800M)=%" PRIu32 "MHz\r\n", (24 * plln) / (pllm * p0), + (24 * plln) / (pllm * p0) >> 1, (24 * plln) / (pllm * p1)); + } else { + printk(LOG_LEVEL_DEBUG, "CLK: PLL_peri disabled\r\n"); + } + + /* PLL DDR */ + reg32 = read32(CCU_BASE + CCU_PLL_DDR_CTRL_REG); + if (reg32 & (1 << 31)) { + plln = ((reg32 >> 8) & 0xff) + 1; + + pllm = (reg32 & 0x01) + 1; + p1 = ((reg32 >> 1) & 0x1) + 1; + p0 = (reg32 & 0x01) + 1; + + printk(LOG_LEVEL_DEBUG, "CLK: PLL_ddr=%" PRIu32 "MHz\r\n", (24 * plln) / (p0 * p1)); + + } else { + printk(LOG_LEVEL_DEBUG, "CLK: PLL_ddr disabled\r\n"); + } +} \ No newline at end of file diff --git a/src/drivers/sun8iw20/sys-dram.c b/src/drivers/sun8iw20/sys-dram.c new file mode 100644 index 00000000..ad33ec5d --- /dev/null +++ b/src/drivers/sun8iw20/sys-dram.c @@ -0,0 +1,1422 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#define DIV_ROUND_UP(a, b) (((a) + (b) -1) / (b)) + +#define CONFIG_SYS_SDRAM_BASE SDRAM_BASE + +#ifndef SUNXI_SID_BASE +#define SUNXI_SID_BASE 0x3006200 +#endif + +static int ns_to_t(dram_para_t *para, int nanoseconds) +{ + const unsigned int ctrl_freq = para->dram_clk / 2; + + return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000); +} + +void abort(void) +{ + while (1) + ; +} + +static void sid_read_ldoB_cal(dram_para_t *para) +{ + uint32_t reg; + + reg = (readl(SYS_SID_BASE + SYS_LDOB_SID) & 0xff00) >> 8; + + if (reg == 0) + return; + + switch (para->dram_type) { + case SUNXI_DRAM_TYPE_DDR2: + break; + case SUNXI_DRAM_TYPE_DDR3: + if (reg > 0x20) + reg -= 0x16; + break; + default: + reg = 0; + break; + } + + clrsetbits_le32((SYS_CONTROL_REG_BASE + LDO_CTAL_REG), 0xff00, reg << 8); +} + +static void dram_voltage_set(dram_para_t *para) +{ + int vol; + + switch (para->dram_type) { + case SUNXI_DRAM_TYPE_DDR2: + vol = 47; + break; + case SUNXI_DRAM_TYPE_DDR3: + vol = 25; + break; + default: + vol = 0; + break; + } + + clrsetbits_le32((SYS_CONTROL_REG_BASE + LDO_CTAL_REG), 0x20ff00, vol << 8); + + udelay(1); + + sid_read_ldoB_cal(para); +} + +static void dram_enable_all_master(void) +{ + writel(~0, (MCTL_COM_BASE + MCTL_COM_MAER0)); + writel(0xff, (MCTL_COM_BASE + MCTL_COM_MAER1)); + writel(0xffff, (MCTL_COM_BASE + MCTL_COM_MAER2)); + udelay(10); +} + +static void dram_disable_all_master(void) +{ + writel(1, (MCTL_COM_BASE + MCTL_COM_MAER0)); + writel(0, (MCTL_COM_BASE + MCTL_COM_MAER1)); + writel(0, (MCTL_COM_BASE + MCTL_COM_MAER2)); + udelay(10); +} + +static void eye_delay_compensation(dram_para_t *para) // s1 +{ + uint32_t delay, i = 0; + + // DATn0IOCR, n = 0...7 + delay = (para->dram_tpr11 & 0xf) << 9; + delay |= (para->dram_tpr12 & 0xf) << 1; + + for (i = 0; i < 9; i++) + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DATX0IOCR(i)), delay); + + // DATn1IOCR, n = 0...7 + delay = (para->dram_tpr11 & 0xf0) << 5; + delay |= (para->dram_tpr12 & 0xf0) >> 3; + for (i = 0; i < 9; i++) + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DATX1IOCR(i)), delay); + + // PGCR0: assert AC loopback FIFO reset + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR0), 0x04000000); + + // DQS0 read and write delay + delay = (para->dram_tpr11 & 0xf0000) >> 7; + delay |= (para->dram_tpr12 & 0xf0000) >> 15; + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DATX0IOCR(9)), delay); // DQS0 P + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DATX0IOCR(10)), delay); // DQS0 N + + // DQS1 read and write delay + delay = (para->dram_tpr11 & 0xf00000) >> 11; + delay |= (para->dram_tpr12 & 0xf00000) >> 19; + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DATX1IOCR(9)), delay); // DQS1 P + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DATX1IOCR(10)), delay); // DQS1 N + + // DQS0 enable bit delay + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXnSDLR6(0)), (para->dram_tpr11 & 0xf0000) << 9); + + // DQS1 enable bit delay + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXnSDLR6(1)), (para->dram_tpr11 & 0xf00000) << 5); + + // PGCR0: release AC loopback FIFO reset + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR0), (1 << 26)); + + udelay(1); + + delay = (para->dram_tpr10 & 0xf0) << 4; + + // Set RAS CAS and CA delay + for (i = 6; i < 27; ++i) + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_ACIOCR1(i)), delay); + + // Set CK CS delay + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_ACIOCR1(2)), (para->dram_tpr10 & 0x0f) << 8); + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_ACIOCR1(3)), (para->dram_tpr10 & 0x0f) << 8); + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_ACIOCR1(28)), (para->dram_tpr10 & 0xf00) >> 4); +} + +/* + * Main purpose of the auto_set_timing routine seems to be to calculate all + * timing settings for the specific type of sdram used. Read together with + * an sdram datasheet for context on the various variables. + */ +static void mctl_set_timing_params(dram_para_t *para) +{ + /* DRAM_TPR0 */ + u8 tccd = 2; + u8 tfaw; + u8 trrd; + u8 trcd; + u8 trc; + + /* DRAM_TPR1 */ + u8 txp; + u8 twtr; + u8 trtp = 4; + u8 twr; + u8 trp; + u8 tras; + + /* DRAM_TPR2 */ + u16 trefi; + u16 trfc; + + u8 tcksrx; + u8 tckesr; + u8 trd2wr; + u8 twr2rd; + u8 trasmax; + u8 twtp; + u8 tcke; + u8 tmod; + u8 tmrd; + u8 tmrw; + + u8 tcl; + u8 tcwl; + u8 t_rdata_en; + u8 wr_latency; + + u32 mr0; + u32 mr1; + u32 mr2; + u32 mr3; + + u32 tdinit0; + u32 tdinit1; + u32 tdinit2; + u32 tdinit3; + + switch (para->dram_type) { + case SUNXI_DRAM_TYPE_DDR2: + /* DRAM_TPR0 */ + tfaw = ns_to_t(para, 50); + trrd = ns_to_t(para, 10); + trcd = ns_to_t(para, 20); + trc = ns_to_t(para, 65); + + /* DRAM_TPR1 */ + txp = 2; + twtr = ns_to_t(para, 8); + twr = ns_to_t(para, 15); + trp = ns_to_t(para, 15); + tras = ns_to_t(para, 45); + + /* DRAM_TRP2 */ + trfc = ns_to_t(para, 328); + trefi = ns_to_t(para, 7800) / 32; + + trasmax = para->dram_clk / 30; + if (para->dram_clk < 409) { + t_rdata_en = 1; + tcl = 3; + mr0 = 0x06a3; + } else { + t_rdata_en = 2; + tcl = 4; + mr0 = 0x0e73; + } + tmrd = 2; + twtp = twr + 5; + tcksrx = 5; + tckesr = 4; + trd2wr = 4; + tcke = 3; + tmod = 12; + wr_latency = 1; + tmrw = 0; + twr2rd = twtr + 5; + tcwl = 0; + + mr1 = para->dram_mr1; + mr2 = 0; + mr3 = 0; + + tdinit0 = 200 * para->dram_clk + 1; + tdinit1 = 100 * para->dram_clk / 1000 + 1; + tdinit2 = 200 * para->dram_clk + 1; + tdinit3 = 1 * para->dram_clk + 1; + + break; + case SUNXI_DRAM_TYPE_DDR3: + trfc = ns_to_t(para, 350); + trefi = ns_to_t(para, 7800) / 32 + 1; // XXX + + twtr = ns_to_t(para, 8) + 2; // + 2 ? XXX + /* Only used by trd2wr calculation, which gets discard below */ + // twr = max(ns_to_t(para, 15), 2); + trrd = max(ns_to_t(para, 10), 2); + txp = max(ns_to_t(para, 10), 2); + + if (para->dram_clk <= 800) { + tfaw = ns_to_t(para, 50); + trcd = ns_to_t(para, 15); + trp = ns_to_t(para, 15); + trc = ns_to_t(para, 53); + tras = ns_to_t(para, 38); + + mr0 = 0x1c70; + mr2 = 0x18; + tcl = 6; + wr_latency = 2; + tcwl = 4; + t_rdata_en = 4; + } else { + tfaw = ns_to_t(para, 35); + trcd = ns_to_t(para, 14); + trp = ns_to_t(para, 14); + trc = ns_to_t(para, 48); + tras = ns_to_t(para, 34); + + mr0 = 0x1e14; + mr2 = 0x20; + tcl = 7; + wr_latency = 3; + tcwl = 5; + t_rdata_en = 5; + } + + trasmax = para->dram_clk / 30; + twtp = tcwl + 2 + twtr; // WL+BL/2+tWTR + /* Gets overwritten below */ + // trd2wr = tcwl + 2 + twr; // WL+BL/2+tWR + twr2rd = tcwl + twtr; // WL+tWTR + + tdinit0 = 500 * para->dram_clk + 1; // 500 us + tdinit1 = 360 * para->dram_clk / 1000 + 1; // 360 ns + tdinit2 = 200 * para->dram_clk + 1; // 200 us + tdinit3 = 1 * para->dram_clk + 1; // 1 us + + mr1 = para->dram_mr1; + mr3 = 0; + tcke = 3; + tcksrx = 5; + tckesr = 4; + if (((para->dram_tpr13 & 0xc) == 0x04) || para->dram_clk < 912) + trd2wr = 5; + else + trd2wr = 6; + + tmod = 12; + tmrd = 4; + tmrw = 0; + + break; + case SUNXI_DRAM_TYPE_LPDDR2: + tfaw = max(ns_to_t(para, 50), 4); + trrd = max(ns_to_t(para, 10), 1); + trcd = max(ns_to_t(para, 24), 2); + trc = ns_to_t(para, 70); + txp = ns_to_t(para, 8); + if (txp < 2) { + txp++; + twtr = 2; + } else { + twtr = txp; + } + twr = max(ns_to_t(para, 15), 2); + trp = ns_to_t(para, 17); + tras = ns_to_t(para, 42); + trefi = ns_to_t(para, 3900) / 32; + trfc = ns_to_t(para, 210); + + trasmax = para->dram_clk / 60; + mr3 = para->dram_mr3; + twtp = twr + 5; + mr2 = 6; + mr1 = 5; + tcksrx = 5; + tckesr = 5; + trd2wr = 10; + tcke = 2; + tmod = 5; + tmrd = 5; + tmrw = 3; + tcl = 4; + wr_latency = 1; + t_rdata_en = 1; + + tdinit0 = 200 * para->dram_clk + 1; + tdinit1 = 100 * para->dram_clk / 1000 + 1; + tdinit2 = 11 * para->dram_clk + 1; + tdinit3 = 1 * para->dram_clk + 1; + twr2rd = twtr + 5; + tcwl = 2; + mr1 = 195; + mr0 = 0; + + break; + case SUNXI_DRAM_TYPE_LPDDR3: + tfaw = max(ns_to_t(para, 50), 4); + trrd = max(ns_to_t(para, 10), 1); + trcd = max(ns_to_t(para, 24), 2); + trc = ns_to_t(para, 70); + twtr = max(ns_to_t(para, 8), 2); + twr = max(ns_to_t(para, 15), 2); + trp = ns_to_t(para, 17); + tras = ns_to_t(para, 42); + trefi = ns_to_t(para, 3900) / 32; + trfc = ns_to_t(para, 210); + txp = twtr; + + trasmax = para->dram_clk / 60; + if (para->dram_clk < 800) { + tcwl = 4; + wr_latency = 3; + t_rdata_en = 6; + mr2 = 12; + } else { + tcwl = 3; + tcke = 6; + wr_latency = 2; + t_rdata_en = 5; + mr2 = 10; + } + twtp = tcwl + 5; + tcl = 7; + mr3 = para->dram_mr3; + tcksrx = 5; + tckesr = 5; + trd2wr = 13; + tcke = 3; + tmod = 12; + tdinit0 = 400 * para->dram_clk + 1; + tdinit1 = 500 * para->dram_clk / 1000 + 1; + tdinit2 = 11 * para->dram_clk + 1; + tdinit3 = 1 * para->dram_clk + 1; + tmrd = 5; + tmrw = 5; + twr2rd = tcwl + twtr + 5; + mr1 = 195; + mr0 = 0; + + break; + default: + trfc = 128; + trp = 6; + trefi = 98; + txp = 10; + twr = 8; + twtr = 3; + tras = 14; + tfaw = 16; + trc = 20; + trcd = 6; + trrd = 3; + + twr2rd = 8; // 48(sp) + tcksrx = 4; // t1 + tckesr = 3; // t4 + trd2wr = 4; // t6 + trasmax = 27; // t3 + twtp = 12; // s6 + tcke = 2; // s8 + tmod = 6; // t0 + tmrd = 2; // t5 + tmrw = 0; // a1 + tcwl = 3; // a5 + tcl = 3; // a0 + wr_latency = 1; // a7 + t_rdata_en = 1; // a4 + mr3 = 0; // s0 + mr2 = 0; // t2 + mr1 = 0; // s1 + mr0 = 0; // a3 + tdinit3 = 0; // 40(sp) + tdinit2 = 0; // 32(sp) + tdinit1 = 0; // 24(sp) + tdinit0 = 0; // 16(sp) + + break; + } + + /* Set mode registers */ + writel(mr0, (MCTL_PHY_BASE + MCTL_PHY_DRAM_MR0)); + writel(mr1, (MCTL_PHY_BASE + MCTL_PHY_DRAM_MR1)); + writel(mr2, (MCTL_PHY_BASE + MCTL_PHY_DRAM_MR2)); + writel(mr3, (MCTL_PHY_BASE + MCTL_PHY_DRAM_MR3)); + /* TODO: dram_odt_en is either 0x0 or 0x1, so right shift looks weird */ + writel((para->dram_odt_en >> 4) & 0x3, (MCTL_PHY_BASE + MCTL_PHY_LP3MR11)); + + /* Set dram timing DRAMTMG0 - DRAMTMG5 */ + writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0), (MCTL_PHY_BASE + MCTL_PHY_DRAMTMG0)); + writel((txp << 16) | (trtp << 8) | (trc << 0), (MCTL_PHY_BASE + MCTL_PHY_DRAMTMG1)); + writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0), (MCTL_PHY_BASE + MCTL_PHY_DRAMTMG2)); + writel((tmrw << 16) | (tmrd << 12) | (tmod << 0), (MCTL_PHY_BASE + MCTL_PHY_DRAMTMG3)); + writel((trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0), (MCTL_PHY_BASE + MCTL_PHY_DRAMTMG4)); + writel((tcksrx << 24) | (tcksrx << 16) | (tckesr << 8) | (tcke << 0), (MCTL_PHY_BASE + MCTL_PHY_DRAMTMG5)); + + /* Set dual rank timing */ + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DRAMTMG8), 0xf000ffff, (para->dram_clk < 800) ? 0xf0006610 : 0xf0007610); + + /* Set phy interface time PITMG0, PTR3, PTR4 */ + writel((0x2 << 24) | (t_rdata_en << 16) | (1 << 8) | (wr_latency << 0), (MCTL_PHY_BASE + MCTL_PHY_PITMG0)); + writel(((tdinit0 << 0) | (tdinit1 << 20)), (MCTL_PHY_BASE + MCTL_PHY_PTR3)); + writel(((tdinit2 << 0) | (tdinit3 << 20)), (MCTL_PHY_BASE + MCTL_PHY_PTR4)); + + /* Set refresh timing and mode */ + writel((trefi << 16) | (trfc << 0), (MCTL_PHY_BASE + MCTL_PHY_RFSHTMG)); + writel((trefi << 15) & 0x0fff0000, (MCTL_PHY_BASE + MCTL_PHY_RFSHCTL1)); +} + +// Purpose of this routine seems to be to initialize the PLL driving +// the MBUS and sdram. +// +static int ccu_set_pll_ddr_clk(int index, dram_para_t *para) +{ + unsigned int val, clk, n; + + if (para->dram_tpr13 & (1 << 6)) + clk = para->dram_tpr9; + else + clk = para->dram_clk; + + // set VCO clock divider + n = (clk * 2) / 24; + + val = readl((CCU_BASE + CCU_PLL_DDR_CTRL_REG)); + val &= 0xfff800fc; // clear dividers + val |= (n - 1) << 8; // set PLL division + val |= 0xc0000000; // enable PLL and LDO + val &= 0xdfffffff; + writel(val | 0x20000000, (CCU_BASE + CCU_PLL_DDR_CTRL_REG)); + + // wait for PLL to lock + while ((readl((CCU_BASE + CCU_PLL_DDR_CTRL_REG)) & 0x10000000) == 0) { + ; + } + + udelay(20); + + // enable PLL output + val = readl(CCU_BASE); + val |= 0x08000000; + writel(val, CCU_BASE); + + // turn clock gate on + val = readl((CCU_BASE + CCU_DRAM_CLK_REG)); + val &= 0xfcfffcfc; // select DDR clk source, n=1, m=1 + val |= 0x80000000; // turn clock on + writel(val, (CCU_BASE + CCU_DRAM_CLK_REG)); + + return n * 24; +} + +// Main purpose of sys_init seems to be to initalise the clocks for +// the sdram controller. +// +static void mctl_sys_init(dram_para_t *para) +{ + // assert MBUS reset + clrbits_le32((CCU_BASE + CCU_MBUS_CLK_REG), (1 << 30)); + + // turn off sdram clock gate, assert sdram reset + clrbits_le32((CCU_BASE + CCU_DRAM_BGR_REG), 0x10001); + clrsetbits_le32((CCU_BASE + CCU_DRAM_CLK_REG), (1 << 31) | (1 << 30), (1 << 27)); + udelay(10); + + // set ddr pll clock + para->dram_clk = ccu_set_pll_ddr_clk(0, para) / 2; + udelay(100); + dram_disable_all_master(); + + // release sdram reset + setbits_le32((CCU_BASE + CCU_DRAM_BGR_REG), (1 << 16)); + + // release MBUS reset + setbits_le32((CCU_BASE + CCU_MBUS_CLK_REG), (1 << 30)); + setbits_le32((CCU_BASE + CCU_DRAM_CLK_REG), (1 << 30)); + + udelay(5); + + // turn on sdram clock gate + setbits_le32((CCU_BASE + CCU_DRAM_BGR_REG), (1 << 0)); + + // turn dram clock gate on, trigger sdr clock update + setbits_le32((CCU_BASE + CCU_DRAM_CLK_REG), (1 << 31) | (1 << 27)); + udelay(5); + + // mCTL clock enable + writel(0x8000, (MCTL_PHY_BASE + MCTL_PHY_CLKEN)); + udelay(10); +} + +// The main purpose of this routine seems to be to copy an address configuration +// from the dram_para1 and dram_para2 fields to the PHY configuration registers +// (MCTL_COM_WORK_MODE0, MCTL_COM_WORK_MODE1). +// +static void mctl_com_init(dram_para_t *para) +{ + uint32_t val, width; + unsigned long ptr; + int i; + + // Setting controller wait time + clrsetbits_le32((MCTL_COM_BASE + MCTL_COM_DBGCR), 0x3f00, 0x2000); + + // set SDRAM type and word width + val = readl((MCTL_COM_BASE + MCTL_COM_WORK_MODE0)) & ~0x00fff000; + val |= (para->dram_type & 0x7) << 16; // DRAM type + val |= (~para->dram_para2 & 0x1) << 12; // DQ width + val |= (1 << 22); // ?? + if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 || para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) { + val |= (1 << 19); // type 6 and 7 must use 1T + } else { + if (para->dram_tpr13 & (1 << 5)) + val |= (1 << 19); + } + writel(val, (MCTL_COM_BASE + MCTL_COM_WORK_MODE0)); + + // init rank / bank / row for single/dual or two different ranks + if ((para->dram_para2 & (1 << 8)) && ((para->dram_para2 & 0xf000) != 0x1000)) + width = 32; + else + width = 16; + + ptr = (MCTL_COM_BASE + MCTL_COM_WORK_MODE0); + for (i = 0; i < width; i += 16) { + val = readl(ptr) & 0xfffff000; + + val |= (para->dram_para2 >> 12) & 0x3; // rank + val |= ((para->dram_para1 >> (i + 12)) << 2) & 0x4; // bank - 2 + val |= (((para->dram_para1 >> (i + 4)) - 1) << 4) & 0xff; // row - 1 + + // convert from page size to column addr width - 3 + switch ((para->dram_para1 >> i) & 0xf) { + case 8: + val |= 0xa00; + break; + case 4: + val |= 0x900; + break; + case 2: + val |= 0x800; + break; + case 1: + val |= 0x700; + break; + default: + val |= 0x600; + break; + } + writel(val, ptr); + ptr += 4; + } + + // set ODTMAP based on number of ranks in use + val = (readl((MCTL_COM_BASE + MCTL_COM_WORK_MODE0)) & 0x1) ? 0x303 : 0x201; + writel(val, (MCTL_PHY_BASE + MCTL_PHY_ODTMAP)); + + // set mctl reg 3c4 to zero when using half DQ + if (para->dram_para2 & (1 << 0)) + writel(0, (MCTL_PHY_BASE + MCTL_PHY_DXnGCR0(1))); + + // set dram address mapping from dram_tpr4 param + if (para->dram_tpr4) { + setbits_le32((MCTL_COM_BASE + MCTL_COM_WORK_MODE0), (para->dram_tpr4 & 0x3) << 25); + setbits_le32((MCTL_COM_BASE + MCTL_COM_WORK_MODE1), (para->dram_tpr4 & 0x7fc) << 10); + } +} + +static const uint8_t ac_remapping_tables[][22] = { + [0] = {0}, + /* FPGA Verify DDR REMAP */ + [1] = {0x1, 0x9, 0x3, 0x7, 0x8, 0x12, 0x4, 0xD, 0x5, 0x6, 0xA, + 0x2, 0xE, 0xC, 0x0, 0x0, 0x15, 0x11, 0x14, 0x13, 0xB, 0x16}, // Generic DDR3 Type1 + [2] = {0x4, 0x9, 0x3, 0x7, 0x8, 0x12, 0x1, 0xD, 0x2, 0x6, 0xA, + 0x5, 0xE, 0xC, 0x0, 0x0, 0x15, 0x11, 0x14, 0x13, 0xB, 0x16}, // Generic DDR3 Type C + [3] = {0x1, 0x7, 0x8, 0xC, 0xA, 0x12, 0x4, 0xD, 0x5, 0x6, 0x3, + 0x2, 0x9, 0x0, 0x0, 0x0, 0x15, 0x11, 0x14, 0x13, 0xB, 0x16}, // Generic DDR3 Type 8 + [4] = {0x4, 0xC, 0xA, 0x7, 0x8, 0x12, 0x1, 0xD, 0x2, 0x6, 0x3, + 0x5, 0x9, 0x0, 0x0, 0x0, 0x15, 0x11, 0x14, 0x13, 0xB, 0x16}, // Generic DDR3 Type 9 + [5] = {0xD, 0x2, 0x7, 0x9, 0xC, 0x13, 0x5, 0x1, 0x6, 0x3, 0x4, + 0x8, 0xA, 0x0, 0x0, 0x0, 0x15, 0x16, 0x12, 0x11, 0xB, 0x14}, // Generic DDR3 Type bf + /* ASIC Chip */ + [6] = {0x3, 0xA, 0x7, 0xD, 0x9, 0xB, 0x1, 0x2, 0x4, 0x6, 0x8, + 0x5, 0xC, 0x0, 0x0, 0x0, 0x14, 0x12, 0x0, 0x15, 0x16, 0x11}, // DDR2 + [7] = {0x3, 0x2, 0x4, 0x7, 0x9, 0x1, 0x11, 0xC, 0x12, 0xE, 0xD, + 0x8, 0xF, 0x6, 0xA, 0x5, 0x13, 0x16, 0x10, 0x15, 0x14, 0xB}, // DDR3 D1-H + [8] = {0x2, 0x13, 0x8, 0x6, 0xE, 0x5, 0x14, 0xA, 0x3, 0x12, 0xD, + 0xB, 0x7, 0xF, 0x9, 0x1, 0x16, 0x15, 0x11, 0xC, 0x4, 0x10}, // DDR3 H133 + [9] = {0x1, 0x2, 0xD, 0x8, 0xF, 0xC, 0x13, 0xA, 0x3, 0x15, 0x6, + 0x11, 0x9, 0xE, 0x5, 0x10, 0x14, 0x16, 0xB, 0x7, 0x4, 0x12}, // DDR2 H133 +}; + +/* + * This routine chooses one of several remapping tables for 22 lines. + * It is unclear which lines are being remapped. It seems to pick + * table cfg7 for the Nezha board. + */ +static void mctl_phy_ac_remapping(dram_para_t *para) +{ + const uint8_t *cfg; + uint32_t fuse, val, chipid; + + /* + * It is unclear whether the LPDDRx types don't need any remapping, + * or whether the original code just didn't provide tables. + */ + if (para->dram_type != SUNXI_DRAM_TYPE_DDR2 && para->dram_type != SUNXI_DRAM_TYPE_DDR3) + return; + + fuse = (readl(SUNXI_SID_BASE + 0x28) & 0xf00) >> 8; + chipid = (readl(SUNXI_SID_BASE) & 0xffff); + + debug("DDR efuse: 0x%" PRIx32 "\r\n", fuse); + debug("chip id efuse: 0x%" PRIx32 "\r\n", chipid); + + if (para->dram_type == SUNXI_DRAM_TYPE_DDR2) { + if (fuse == 15) + return; + if (fuse == 10) // Support D1s + cfg = ac_remapping_tables[0]; + cfg = ac_remapping_tables[6]; + } else { + if (para->dram_tpr13 & 0xc0000) { + cfg = ac_remapping_tables[7]; + } else { + switch (fuse) { + case 8: + cfg = ac_remapping_tables[2]; + break; + case 9: + cfg = ac_remapping_tables[3]; + break; + case 10: + if(chipid == 0x6800){ // 0x6800 is T113-S4 no remap + cfg = ac_remapping_tables[0]; + } else { + cfg = ac_remapping_tables[5]; + } + break; + case 11: + cfg = ac_remapping_tables[4]; + break; + default: + case 12: + cfg = ac_remapping_tables[1]; + break; + case 13: + case 14: + cfg = ac_remapping_tables[0]; + break; + } + } + } + + val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) | (cfg[1] << 10) | (cfg[0] << 5); + writel(val, (MCTL_COM_BASE + MCTL_COM_REMAP0)); + + val = (cfg[10] << 25) | (cfg[9] << 20) | (cfg[8] << 15) | (cfg[7] << 10) | (cfg[6] << 5) | cfg[5]; + writel(val, (MCTL_COM_BASE + MCTL_COM_REMAP1)); + + val = (cfg[15] << 20) | (cfg[14] << 15) | (cfg[13] << 10) | (cfg[12] << 5) | cfg[11]; + writel(val, (MCTL_COM_BASE + MCTL_COM_REMAP2)); + + val = (cfg[21] << 25) | (cfg[20] << 20) | (cfg[19] << 15) | (cfg[18] << 10) | (cfg[17] << 5) | cfg[16]; + writel(val, (MCTL_COM_BASE + MCTL_COM_REMAP3)); + + val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) | (cfg[1] << 10) | (cfg[0] << 5) | 1; + writel(val, (MCTL_COM_BASE + MCTL_COM_REMAP0)); +} + +// Init the controller channel. The key part is placing commands in the main +// command register (PIR, 0x3103000) and checking command status (PGSR0, 0x3103010). +// +static unsigned int mctl_channel_init(unsigned int ch_index, dram_para_t *para) +{ + unsigned int val, dqs_gating_mode; + + dqs_gating_mode = (para->dram_tpr13 & 0xc) >> 2; + + // set DDR clock to half of CPU clock + clrsetbits_le32((MCTL_COM_BASE + MCTL_COM_TMR), 0xfff, (para->dram_clk / 2) - 1); + + // MRCTRL0 nibble 3 undocumented + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR2), 0xf00, 0x300); + + if (para->dram_odt_en) + val = 0; + else + val = (1 << 5); + + // DX0GCR0 + if (para->dram_clk > 672) + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXnGCR0(0)), 0xf63e, val); + else + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXnGCR0(0)), 0xf03e, val); + + // DX1GCR0 + if (para->dram_clk > 672) { + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXnGCR0(0)), 0x400); + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXnGCR0(1)), 0xf63e, val); + } else { + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXnGCR0(1)), 0xf03e, val); + } + + // (MCTL_PHY_BASE+MCTL_PHY_ACIOCR0) undocumented + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_ACIOCR0), (1 << 1)); + + eye_delay_compensation(para); + + // set PLL SSCG ? + val = readl((MCTL_PHY_BASE + MCTL_PHY_PGCR2)); + if (dqs_gating_mode == 1) { + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR2), 0xc0, 0); + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_DQSGMR), 0x107); + } else if (dqs_gating_mode == 2) { + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR2), 0xc0, 0x80); + + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DQSGMR), 0x107, (((para->dram_tpr13 >> 16) & 0x1f) - 2) | 0x100); + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXCCR), (1 << 31), (1 << 27)); + } else { + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR2), 0x40); + udelay(10); + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR2), 0xc0); + } + + if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 || para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) { + if (dqs_gating_mode == 1) + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXCCR), 0x080000c0, 0x80000000); + else + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXCCR), 0x77000000, 0x22000000); + } + + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DTCR), 0x0fffffff, + (para->dram_para2 & (1 << 12)) ? 0x03000001 : 0x01000007); + + if (readl((SUNXI_R_CPUCFG_BASE + SUNXI_R_CPUCFG_SUP_STAN_FLAG)) & (1 << 16)) { + clrbits_le32((R_PRCM_BASE + VDD_SYS_PWROFF_GATING_REG), 0x2); + udelay(10); + } + + // Set ZQ config + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_ZQCR), 0x3ffffff, (para->dram_zq & 0x00ffffff) | (1 << 25)); + + // Initialise DRAM controller + if (dqs_gating_mode == 1) { + // writel(0x52, (MCTL_PHY_BASE+MCTL_PHY_PIR)); // prep PHY reset + PLL init + // + z-cal + writel(0x53, (MCTL_PHY_BASE + MCTL_PHY_PIR)); // Go + + while ((readl((MCTL_PHY_BASE + MCTL_PHY_PGSR0)) & 0x1) == 0) { + } // wait for IDONE + udelay(10); + + // 0x520 = prep DQS gating + DRAM init + d-cal + if (para->dram_type == SUNXI_DRAM_TYPE_DDR3) + writel(0x5a0, (MCTL_PHY_BASE + MCTL_PHY_PIR)); // + DRAM reset + else + writel(0x520, (MCTL_PHY_BASE + MCTL_PHY_PIR)); + } else { + if ((readl((SUNXI_R_CPUCFG_BASE + SUNXI_R_CPUCFG_SUP_STAN_FLAG)) & (1 << 16)) == 0) { + // prep DRAM init + PHY reset + d-cal + PLL init + z-cal + if (para->dram_type == SUNXI_DRAM_TYPE_DDR3) + writel(0x1f2, (MCTL_PHY_BASE + MCTL_PHY_PIR)); // + DRAM reset + else + writel(0x172, (MCTL_PHY_BASE + MCTL_PHY_PIR)); + } else { + // prep PHY reset + d-cal + z-cal + writel(0x62, (MCTL_PHY_BASE + MCTL_PHY_PIR)); + } + } + + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_PIR), 0x1); // GO + + udelay(10); + while ((readl((MCTL_PHY_BASE + MCTL_PHY_PGSR0)) & 0x1) == 0) { + } // wait for IDONE + + if (readl((SUNXI_R_CPUCFG_BASE + SUNXI_R_CPUCFG_SUP_STAN_FLAG)) & (1 << 16)) { + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR3), 0x06000000, 0x04000000); + udelay(10); + + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_PWRCTL), 0x1); + + while ((readl((MCTL_PHY_BASE + MCTL_PHY_STATR)) & 0x7) != 0x3) { + } + + clrbits_le32((R_PRCM_BASE + VDD_SYS_PWROFF_GATING_REG), 0x1); + udelay(10); + + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_PWRCTL), 0x1); + + while ((readl((MCTL_PHY_BASE + MCTL_PHY_STATR)) & 0x7) != 0x1) { + } + + udelay(15); + + if (dqs_gating_mode == 1) { + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR2), 0xc0); + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR3), 0x06000000, 0x02000000); + udelay(1); + writel(0x401, (MCTL_PHY_BASE + MCTL_PHY_PIR)); + + while ((readl((MCTL_PHY_BASE + MCTL_PHY_PGSR0)) & 0x1) == 0) { + } + } + } + + // Check for training error + if (readl((MCTL_PHY_BASE + MCTL_PHY_PGSR0)) & (1 << 20)) { + printk(LOG_LEVEL_ERROR, "ZQ calibration error, check external 240 ohm resistor\r\n"); + return 0; + } + + // STATR = Zynq STAT? Wait for status 'normal'? + while ((readl((MCTL_PHY_BASE + MCTL_PHY_STATR)) & 0x1) == 0) { + } + + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_RFSHCTL0), (1 << 31)); + udelay(10); + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_RFSHCTL0), (1 << 31)); + udelay(10); + setbits_le32((MCTL_COM_BASE + MCTL_COM_CCCR), (1 << 31)); + udelay(10); + + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR3), 0x06000000); + + if (dqs_gating_mode == 1) + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_DXCCR), 0xc0, 0x40); + + return 1; +} + +static unsigned int calculate_rank_size(uint32_t regval) +{ + unsigned int bits; + + bits = (regval >> 8) & 0xf; /* page size - 3 */ + bits += (regval >> 4) & 0xf; /* row width - 1 */ + bits += (regval >> 2) & 0x3; /* bank count - 2 */ + bits -= 14; /* 1MB = 20 bits, minus above 6 = 14 */ + + return 1U << bits; +} + +/* + * The below routine reads the dram config registers and extracts + * the number of address bits in each rank available. It then calculates + * total memory size in MB. + */ +static unsigned int DRAMC_get_dram_size(void) +{ + uint32_t val; + unsigned int size; + + val = readl((MCTL_COM_BASE + MCTL_COM_WORK_MODE0)); /* MCTL_COM_WORK_MODE0 */ + size = calculate_rank_size(val); + + if ((val & 0x3) == 0) /* single rank? */ + return size; + + val = readl((MCTL_COM_BASE + MCTL_COM_WORK_MODE1)); /* MCTL_WORK_MODE1 */ + if ((val & 0x3) == 0) /* two identical ranks? */ + return size * 2; + + /* add sizes of both ranks */ + return size + calculate_rank_size(val); +} + +/* + * The below routine reads the command status register to extract + * DQ width and rank count. This follows the DQS training command in + * channel_init. If error bit 22 is reset, we have two ranks and full DQ. + * If there was an error, figure out whether it was half DQ, single rank, + * or both. Set bit 12 and 0 in dram_para2 with the results. + */ +static int dqs_gate_detect(dram_para_t *para) +{ + uint32_t dx0 = 0, dx1 = 0; + + if ((readl(MCTL_PHY_BASE + MCTL_PHY_PGSR0) & BIT(22)) == 0) { + para->dram_para2 = (para->dram_para2 & ~0xf) | BIT(12); + debug("dual rank and full DQ\r\n"); + + return 1; + } + + dx0 = (readl(MCTL_PHY_BASE + MCTL_PHY_DXnGSR0(0)) & 0x3000000) >> 24; + if (dx0 == 0) { + para->dram_para2 = (para->dram_para2 & ~0xf) | 0x1001; + debug("dual rank and half DQ\r\n"); + + return 1; + } + + if (dx0 == 2) { + dx1 = (readl(MCTL_PHY_BASE + MCTL_PHY_DXnGSR0(1)) & 0x3000000) >> 24; + if (dx1 == 2) { + para->dram_para2 = para->dram_para2 & ~0xf00f; + debug("single rank and full DQ\r\n"); + } else { + para->dram_para2 = (para->dram_para2 & ~0xf00f) | BIT(0); + debug("single rank and half DQ\r\n"); + } + + return 1; + } + + if ((para->dram_tpr13 & BIT(29)) == 0) + return 0; + + debug("DX0 state: %" PRIu32 "\r\n", dx0); + debug("DX1 state: %" PRIu32 "\r\n", dx1); + + return 0; +} + +static int dramc_simple_wr_test(unsigned int mem_mb, int len) +{ + unsigned int offs = (mem_mb / 2) << 18; // half of memory size + unsigned int patt1 = 0x01234567; + unsigned int patt2 = 0xfedcba98; + unsigned int *addr, v1, v2, i; + + addr = (unsigned int *)CONFIG_SYS_SDRAM_BASE; + for (i = 0; i != len; i++, addr++) { + writel(patt1 + i, (unsigned long)addr); + writel(patt2 + i, (unsigned long)(addr + offs)); + } + + addr = (unsigned int *)CONFIG_SYS_SDRAM_BASE; + for (i = 0; i != len; i++) { + v1 = readl((unsigned long)(addr + i)); + v2 = patt1 + i; + if (v1 != v2) { + printk(LOG_LEVEL_ERROR, "DRAM: simple test FAIL\r\n"); + printk(LOG_LEVEL_ERROR, "%x != %x at address %p\r\n", v1, v2, addr + i); + return 1; + } + v1 = readl((unsigned long)(addr + offs + i)); + v2 = patt2 + i; + if (v1 != v2) { + printk(LOG_LEVEL_ERROR, "DRAM: simple test FAIL\r\n"); + printk(LOG_LEVEL_ERROR, "%x != %x at address %p\r\n", v1, v2, addr + offs + i); + return 1; + } + } + + debug("DRAM: simple test OK\r\n"); + return 0; +} + +// Set the Vref mode for the controller +// +static void mctl_vrefzq_init(dram_para_t *para) +{ + if (para->dram_tpr13 & (1 << 17)) + return; + + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_IOVCR0), 0x7f7f7f7f, para->dram_tpr5); + + // IOCVR1 + if ((para->dram_tpr13 & (1 << 16)) == 0) + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_IOVCR1), 0x7f, para->dram_tpr6 & 0x7f); +} + +// Perform an init of the controller. This is actually done 3 times. The first +// time to establish the number of ranks and DQ width. The second time to +// establish the actual ram size. The third time is final one, with the final +// settings. +// +static int mctl_core_init(dram_para_t *para) +{ + mctl_sys_init(para); + + mctl_vrefzq_init(para); + + mctl_com_init(para); + + mctl_phy_ac_remapping(para); + + mctl_set_timing_params(para); + + return mctl_channel_init(0, para); +} + +/* + * This routine sizes a DRAM device by cycling through address lines and + * figuring out if they are connected to a real address line, or if the + * address is a mirror. + * First the column and bank bit allocations are set to low values (2 and 9 + * address lines). Then a maximum allocation (16 lines) is set for rows and + * this is tested. + * Next the BA2 line is checked. This seems to be placed above the column, + * BA0-1 and row addresses. Finally, the column address is allocated 13 lines + * and these are tested. The results are placed in dram_para1 and dram_para2. + */ +static int auto_scan_dram_size(dram_para_t *para) +{ + uint32_t i = 0, j = 0, current_rank = 0; + uint32_t rank_count = 1, addr_line = 0; + uint32_t reg_val = 0, ret = 0, cnt = 0; + unsigned long mc_work_mode; + uint32_t rank1_addr = CONFIG_SYS_SDRAM_BASE; + + // init core + if (mctl_core_init(para) == 0) { + debug("DRAM initial error : 0!\r\n"); + return 0; + } + + // Set rank_count to 2 + if ((((para->dram_para2 >> 12) & 0xf) == 0x1)) + rank_count = 2; + + for (current_rank = 0; current_rank < rank_count; current_rank++) { + mc_work_mode = ((MCTL_COM_BASE + MCTL_COM_WORK_MODE0) + 4 * current_rank); + + /* Set 16 Row 4Bank 512BPage for Rank 1 */ + if (current_rank == 1) { + clrsetbits_le32((MCTL_COM_BASE + MCTL_COM_WORK_MODE0), 0xf0c, 0x6f0); + clrsetbits_le32((MCTL_COM_BASE + MCTL_COM_WORK_MODE1), 0xf0c, 0x6f0); + /* update Rank 1 addr */ + rank1_addr = CONFIG_SYS_SDRAM_BASE + (0x1 << 27); + } + + /* write test pattern */ + for (i = 0; i < 64; i++) { + writel((i % 2) ? (CONFIG_SYS_SDRAM_BASE + 4 * i) : (~(CONFIG_SYS_SDRAM_BASE + 4 * i)), + CONFIG_SYS_SDRAM_BASE + 4 * i); + } + /* set row mode */ + clrsetbits_le32(mc_work_mode, 0xf0c, 0x6f0); + udelay(2); + + for (i = 11; i < 17; i++) { + ret = CONFIG_SYS_SDRAM_BASE + (1 << (i + 2 + 9)); /* row-bank-column */ + cnt = 0; + for (j = 0; j < 64; j++) { + reg_val = (j % 2) ? (rank1_addr + 4 * j) : (~(rank1_addr + 4 * j)); + if (reg_val == readl(ret + j * 4)) { + cnt++; + } else + break; + } + if (cnt == 64) { + break; + } + } + if (i >= 16) + i = 16; + addr_line += i; + + debug("rank %" PRIu32 " row = %" PRIu32 " \r\n", current_rank, i); + + /* Store rows in para 1 */ + para->dram_para1 &= ~(0xffU << (16 * current_rank + 4)); + para->dram_para1 |= (i << (16 * current_rank + 4)); + debug("para->dram_para1 = 0x%x\r\n", para->dram_para1); + + /* Set bank mode for current rank */ + if (current_rank == 1) { /* Set bank mode for rank0 */ + clrsetbits_le32((MCTL_COM_BASE + MCTL_COM_WORK_MODE0), 0xffc, 0x6a4); + } + + /* Set bank mode for current rank */ + clrsetbits_le32(mc_work_mode, 0xffc, 0x6a4); + udelay(1); + + for (i = 0; i < 1; i++) { + ret = CONFIG_SYS_SDRAM_BASE + (0x1U << (i + 2 + 9)); + cnt = 0; + for (j = 0; j < 64; j++) { + reg_val = (j % 2) ? (rank1_addr + 4 * j) : (~(rank1_addr + 4 * j)); + if (reg_val == readl(ret + j * 4)) { + cnt++; + } else + break; + } + if (cnt == 64) { + break; + } + } + + addr_line += i + 2; + debug("rank %" PRIu32 " bank = %" PRIu32 " \r\n", current_rank, (4 + i * 4)); + + /* Store bank in para 1 */ + para->dram_para1 &= ~(0xfU << (16 * current_rank + 12)); + para->dram_para1 |= (i << (16 * current_rank + 12)); + debug("para->dram_para1 = 0x%x\r\n", para->dram_para1); + + /* Set page mode for rank0 */ + if (current_rank == 1) { + clrsetbits_le32(mc_work_mode, 0xffc, 0xaa0); + } + + /* Set page mode for current rank */ + clrsetbits_le32(mc_work_mode, 0xffc, 0xaa0); + udelay(2); + + /* Scan per address line, until address wraps (i.e. see shadow) */ + for (i = 9; i <= 13; i++) { + ret = CONFIG_SYS_SDRAM_BASE + (0x1U << i); // column 40000000+(9~13) + cnt = 0; + for (j = 0; j < 64; j++) { + reg_val = (j % 2) ? (CONFIG_SYS_SDRAM_BASE + 4 * j) : (~(CONFIG_SYS_SDRAM_BASE + 4 * j)); + if (reg_val == readl(ret + j * 4)) { + cnt++; + } else { + break; + } + } + if (cnt == 64) { + break; + } + } + + if (i >= 13) { + i = 13; + } + + /* add page size */ + addr_line += i; + + if (i == 9) { + i = 0; + } else { + i = (0x1U << (i - 10)); + } + + debug("rank %" PRIu32 " page size = %" PRIu32 " KB \r\n", current_rank, i); + + /* Store page in para 1 */ + para->dram_para1 &= ~(0xfU << (16 * current_rank)); + para->dram_para1 |= (i << (16 * current_rank)); + debug("para->dram_para1 = 0x%x\r\n", para->dram_para1); + } + + /* check dual rank config */ + if (rank_count == 2) { + para->dram_para2 &= 0xfffff0ff; + if ((para->dram_para1 & 0xffff) == (para->dram_para1 >> 16)) { + debug("rank1 config same as rank0\r\n"); + } else { + para->dram_para2 |= 0x1 << 8; + debug("rank1 config different from rank0\r\n"); + } + } + return 1; +} + +/* + * This routine sets up parameters with dqs_gating_mode equal to 1 and two + * ranks enabled. It then configures the core and tests for 1 or 2 ranks and + * full or half DQ width. It then resets the parameters to the original values. + * dram_para2 is updated with the rank and width findings. + */ +static int auto_scan_dram_rank_width(dram_para_t *para) +{ + unsigned int s1 = para->dram_tpr13; + unsigned int s2 = para->dram_para1; + + para->dram_para1 = 0x00b000b0; + para->dram_para2 = (para->dram_para2 & ~0xf) | (1 << 12); + + /* set DQS probe mode */ + para->dram_tpr13 = (para->dram_tpr13 & ~0x8) | (1 << 2) | (1 << 0); + + mctl_core_init(para); + + if (readl((MCTL_PHY_BASE + MCTL_PHY_PGSR0)) & (1 << 20)) + return 0; + + if (dqs_gate_detect(para) == 0) + return 0; + + para->dram_tpr13 = s1; + para->dram_para1 = s2; + + return 1; +} + +/* + * This routine determines the SDRAM topology. It first establishes the number + * of ranks and the DQ width. Then it scans the SDRAM address lines to establish + * the size of each rank. It then updates dram_tpr13 to reflect that the sizes + * are now known: a re-init will not repeat the autoscan. + */ +static int auto_scan_dram_config(dram_para_t *para) +{ + if (((para->dram_tpr13 & BIT(14)) == 0) && (auto_scan_dram_rank_width(para) == 0)) { + printk(LOG_LEVEL_ERROR, "ERROR: auto scan dram rank & width failed\r\n"); + return 0; + } + + if (((para->dram_tpr13 & BIT(0)) == 0) && (auto_scan_dram_size(para) == 0)) { + printk(LOG_LEVEL_ERROR, "ERROR: auto scan dram size failed\r\n"); + return 0; + } + + if ((para->dram_tpr13 & BIT(15)) == 0) + para->dram_tpr13 |= BIT(14) | BIT(13) | BIT(1) | BIT(0); + + return 1; +} + +int init_DRAM(int type, dram_para_t *para) +{ + u32 rc, mem_size_mb; + + debug("DRAM BOOT DRIVE INFO: %s\r\n", "V0.24"); + debug("DRAM CLK = %d MHz\r\n", para->dram_clk); + debug("DRAM Type = %d (2:DDR2,3:DDR3)\r\n", para->dram_type); + if ((para->dram_odt_en & 0x1) == 0) + debug("DRAMC read ODT off\r\n"); + else + debug("DRAMC ZQ value: 0x%x\r\n", para->dram_zq); + + /* Test ZQ status */ + if (para->dram_tpr13 & (1 << 16)) { + debug("DRAM only have internal ZQ\r\n"); + setbits_le32((SYS_CONTROL_REG_BASE + ZQ_CAL_CTRL_REG), (1 << 8)); + writel(0, (SYS_CONTROL_REG_BASE + ZQ_RES_CTRL_REG)); + udelay(10); + } else { + clrbits_le32((SYS_CONTROL_REG_BASE + ZQ_CAL_CTRL_REG), 0x3); + writel(para->dram_tpr13 & (1 << 16), (R_PRCM_BASE + ANALOG_PWROFF_GATING_REG)); + udelay(10); + clrsetbits_le32((SYS_CONTROL_REG_BASE + ZQ_CAL_CTRL_REG), 0x108, (1 << 1)); + udelay(10); + setbits_le32((SYS_CONTROL_REG_BASE + ZQ_CAL_CTRL_REG), (1 << 0)); + udelay(20); + debug("ZQ value = 0x%" PRIx32 "\r\n", readl((SYS_CONTROL_REG_BASE + ZQ_RES_STATUS_REG))); + } + + dram_voltage_set(para); + + /* Set SDRAM controller auto config */ + if ((para->dram_tpr13 & (1 << 0)) == 0) { + if (auto_scan_dram_config(para) == 0) { + printk(LOG_LEVEL_ERROR, "auto_scan_dram_config() FAILED\r\n"); + return 0; + } + } + + /* report ODT */ + rc = para->dram_mr1; + if ((rc & 0x44) == 0) + debug("DRAM ODT off\r\n"); + else + debug("DRAM ODT value: 0x%" PRIx32 "\r\n", rc); + + /* Init core, final run */ + if (mctl_core_init(para) == 0) { + debug("DRAM initialisation error: 1\r\n"); + return 0; + } + + /* Get SDRAM size + * You can set dram_para2 to force set the dram size + * TODO: who ever puts a negative number in the top half? + */ + rc = para->dram_para2; + if (rc & (1 << 31)) { + rc = (rc >> 16) & ~(1 << 15); + } else { + rc = DRAMC_get_dram_size(); + debug("DRAM: size = %" PRIu32 "MB\r\n", rc); + para->dram_para2 = (para->dram_para2 & 0xffffU) | rc << 16; + } + mem_size_mb = rc; + + /* Enable hardware auto refresh */ + if (para->dram_tpr13 & (1 << 30)) { + rc = para->dram_tpr8; + if (rc == 0) + rc = 0x10000200; + writel(rc, (MCTL_PHY_BASE + MCTL_PHY_ASRTC)); + writel(0x40a, (MCTL_PHY_BASE + MCTL_PHY_ASRC)); + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_PWRCTL), (1 << 0)); + debug("Enable Auto SR\r\n"); + } else { + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_ASRTC), 0xffff); + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_PWRCTL), 0x1); + } + + /* Set HDR/DDR dynamic */ + if (para->dram_tpr13 & (1 << 9)) { + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR0), 0xf000, 0x5000); + } else { + if (para->dram_type != SUNXI_DRAM_TYPE_LPDDR2) + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR0), 0xf000); + } + + /* Disable ZQ calibration */ + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_ZQCR), (1 << 31)); + + /* Set VTF feature */ + if (para->dram_tpr13 & (1 << 8)) + writel(readl((MCTL_PHY_BASE + MCTL_PHY_VTFCR)) | 0x300, (MCTL_PHY_BASE + MCTL_PHY_VTFCR)); + + /* Set PAD Hold */ + if (para->dram_tpr13 & (1 << 16)) + clrbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR2), (1 << 13)); + else + setbits_le32((MCTL_PHY_BASE + MCTL_PHY_PGCR2), (1 << 13)); + + /* Set LPDDR3 ODT delay */ + if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) + clrsetbits_le32((MCTL_PHY_BASE + MCTL_PHY_ODTCFG), 0xf0000, 0x1000); + + dram_enable_all_master(); + if (para->dram_tpr13 & (1 << 28)) { + if ((readl((SUNXI_R_CPUCFG_BASE + SUNXI_R_CPUCFG_SUP_STAN_FLAG)) & (1 << 16)) || + dramc_simple_wr_test(mem_size_mb, 4096)) + return 0; + } + + return mem_size_mb; +} + +uint64_t sunxi_dram_init(void) +{ + dram_para_t para = { + .dram_clk = 792, + .dram_type = 3, + .dram_zq = 0x7b7bfb, + .dram_odt_en = 0x00, + .dram_para1 = 0x000010d2, + .dram_para2 = 0, + .dram_mr0 = 0x1c70, + .dram_mr1 = 0x42, + .dram_mr2 = 0x18, + .dram_mr3 = 0, + .dram_tpr0 = 0x004a2195, + .dram_tpr1 = 0x02423190, + .dram_tpr2 = 0x0008b061, + .dram_tpr3 = 0xb4787896, // unused + .dram_tpr4 = 0, + .dram_tpr5 = 0x48484848, + .dram_tpr6 = 0x00000048, + .dram_tpr7 = 0x1620121e, // unused + .dram_tpr8 = 0, + .dram_tpr9 = 0, // clock? + .dram_tpr10 = 0, + .dram_tpr11 = 0x00340000, + .dram_tpr12 = 0x00000046, + .dram_tpr13 = 0x34000100, + }; + + return init_DRAM(0, ¶); +}; \ No newline at end of file diff --git a/src/drivers/sun8iw20/sys-sid.c b/src/drivers/sun8iw20/sys-sid.c new file mode 100644 index 00000000..519d87eb --- /dev/null +++ b/src/drivers/sun8iw20/sys-sid.c @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "sys-sid.h" + +uint32_t efuse_read(uint32_t offset) { + uint32_t val; + + val = read32(SID_PRCTL); + val &= ~((0x1ff << 16) | 0x3); + val |= offset << 16; + write32(SID_PRCTL, val); + val &= ~((0xff << 8) | 0x3); + val |= (0xac << 8) | 0x2; + write32(SID_PRCTL, val); + while (read32(SID_PRCTL) & 0x2) + ; + val &= ~((0x1ff << 16) | (0xff << 8) | 0x3); + write32(SID_PRCTL, val); + val = read32(SID_RDKEY); + + return val; +} + +void efuse_write(uint32_t offset, uint32_t value) { + uint32_t val; + + write32(EFUSE_HV_SWITCH, 0x1); + write32(SID_PRKEY, value); + val = read32(SID_PRCTL); + val &= ~((0x1ff << 16) | 0x3); + val |= offset << 16; + write32(SID_PRCTL, val); + val &= ~((0xff << 8) | 0x3); + val |= (0xac << 8) | 0x1; + write32(SID_PRCTL, val); + while (read32(SID_PRCTL) & 0x1) + ; + val &= ~((0x1ff << 16) | (0xff << 8) | 0x3); + write32(SID_PRCTL, val); + write32(EFUSE_HV_SWITCH, 0x0); +} + +void dump_efuse(void) { + uint32_t buffer[2048 / 4]; + + for (int n = 0; n < ARRAY_SIZE(sids); n++) { + uint32_t count = sids[n].size_bits / 32; + for (int i = 0; i < count; i++) + buffer[i] = efuse_read(sids[n].offset + i * 4); + + printk(LOG_LEVEL_MUTE, "%s:(0x%04x %d-bits)", sids[n].name, + sids[n].offset, sids[n].size_bits); + for (int i = 0; i < count; i++) { + if (i >= 0 && ((i % 8) == 0)) + printk(LOG_LEVEL_MUTE, "\n%-4s", ""); + printk(LOG_LEVEL_MUTE, "%08x ", buffer[i]); + } + printk(LOG_LEVEL_MUTE, "\n"); + } +} \ No newline at end of file diff --git a/src/drivers/sun8iw20/sys-wdt.c b/src/drivers/sun8iw20/sys-wdt.c new file mode 100644 index 00000000..4bce8b1f --- /dev/null +++ b/src/drivers/sun8iw20/sys-wdt.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include +#include +#include + +#include + +void sys_reset() { + write32(0x020500a0 + 0x08, (0x16aa << 16) | (0x1 << 0)); +} \ No newline at end of file