diff --git a/Documentation/platforms/risc-v/k210/index.rst b/Documentation/platforms/risc-v/k210/index.rst index f769049b810..7dfc61b80cc 100644 --- a/Documentation/platforms/risc-v/k210/index.rst +++ b/Documentation/platforms/risc-v/k210/index.rst @@ -2,6 +2,26 @@ Kendryte K210 ============= +System Controller (sysctl) +========================== + +The K210 System Controller (sysctl) driver provides essential clock and reset +control functionality for the K210 SoC. It is built unconditionally for all +K210 boards. + +Clock Frequency Configuration +----------------------------- + +The driver supports querying clock frequencies for: + +* PLL frequencies (PLL0, PLL1, PLL2) +* CPU clock frequency +* APB bus frequencies (APB0, APB1, APB2) +* Individual peripheral clock frequencies + +CPU frequency can be configured at build time using the ``K210_CPU_FREQ`` +Kconfig option (default: 400 MHz, range: 40-600 MHz). + Supported Boards ================ diff --git a/arch/risc-v/src/k210/CMakeLists.txt b/arch/risc-v/src/k210/CMakeLists.txt index f81ad8fc8e6..4df61aab560 100644 --- a/arch/risc-v/src/k210/CMakeLists.txt +++ b/arch/risc-v/src/k210/CMakeLists.txt @@ -26,6 +26,8 @@ list(APPEND SRCS k210_timerisr.c k210_allocateheap.c k210_clockconfig.c) list(APPEND SRCS k210_lowputc.c k210_serial.c k210_fpioa.c k210_gpiohs.c) +list(APPEND SRCS k210_sysctl.c) + if(CONFIG_BUILD_PROTECTED) list(APPEND SRCS k210_userspace.c) endif() diff --git a/arch/risc-v/src/k210/Kconfig b/arch/risc-v/src/k210/Kconfig index 9fb905a9305..4416e294c20 100644 --- a/arch/risc-v/src/k210/Kconfig +++ b/arch/risc-v/src/k210/Kconfig @@ -27,6 +27,16 @@ config K210_UART0 endmenu +config K210_CPU_FREQ + int "K210 CPU target frequency (Hz)" + default 400000000 + range 40000000 600000000 + depends on ARCH_CHIP_K210 + ---help--- + Set the K210 CPU target frequency in Hz. + Default is 400000000 (400 MHz). + Valid range: 40000000-600000000 Hz (40-600 MHz). + menu "K210 Others" config K210_WITH_QEMU diff --git a/arch/risc-v/src/k210/Make.defs b/arch/risc-v/src/k210/Make.defs index cb302ecc904..42d2d705140 100644 --- a/arch/risc-v/src/k210/Make.defs +++ b/arch/risc-v/src/k210/Make.defs @@ -30,7 +30,7 @@ HEAD_ASRC = k210_head.S CHIP_CSRCS = k210_allocateheap.c k210_clockconfig.c CHIP_CSRCS += k210_irq.c k210_irq_dispatch.c CHIP_CSRCS += k210_lowputc.c k210_serial.c k210_fpioa.c -CHIP_CSRCS += k210_start.c k210_timerisr.c k210_gpiohs.c +CHIP_CSRCS += k210_start.c k210_timerisr.c k210_gpiohs.c k210_sysctl.c ifeq ($(CONFIG_BUILD_PROTECTED),y) CHIP_CSRCS += k210_userspace.c diff --git a/arch/risc-v/src/k210/hardware/k210_sysctl.h b/arch/risc-v/src/k210/hardware/k210_sysctl.h index b26e10bfe9a..ac91bf96ef4 100644 --- a/arch/risc-v/src/k210/hardware/k210_sysctl.h +++ b/arch/risc-v/src/k210/hardware/k210_sysctl.h @@ -33,13 +33,303 @@ * Pre-processor Definitions ****************************************************************************/ -#define K210_SYSCTL_PLL0 (K210_SYSCTL_BASE + 0x08) +/* Sysctl Register Offsets */ + +#define K210_SYSCTL_GIT_ID (K210_SYSCTL_BASE + 0x00) /* Git short commit id */ +#define K210_SYSCTL_CLK_FREQ (K210_SYSCTL_BASE + 0x04) /* System clock base frequency */ +#define K210_SYSCTL_PLL0 (K210_SYSCTL_BASE + 0x08) /* PLL0 controller */ +#define K210_SYSCTL_PLL1 (K210_SYSCTL_BASE + 0x0c) /* PLL1 controller */ +#define K210_SYSCTL_PLL2 (K210_SYSCTL_BASE + 0x10) /* PLL2 controller */ + +/* 0x14: Reserved */ + +#define K210_SYSCTL_PLL_LOCK (K210_SYSCTL_BASE + 0x18) /* PLL lock tester */ +#define K210_SYSCTL_ROM_ERROR (K210_SYSCTL_BASE + 0x1c) /* AXI ROM detector */ +#define K210_SYSCTL_CLKSEL0 (K210_SYSCTL_BASE + 0x20) /* Clock select controller 0 */ +#define K210_SYSCTL_CLKSEL1 (K210_SYSCTL_BASE + 0x24) /* Clock select controller 1 */ +#define K210_SYSCTL_CLK_EN_CENT (K210_SYSCTL_BASE + 0x28) /* Central clock enable */ +#define K210_SYSCTL_CLK_EN_PERI (K210_SYSCTL_BASE + 0x2c) /* Peripheral clock enable */ +#define K210_SYSCTL_SOFT_RESET (K210_SYSCTL_BASE + 0x30) /* Soft reset control */ +#define K210_SYSCTL_PERI_RESET (K210_SYSCTL_BASE + 0x34) /* Peripheral reset controller */ +#define K210_SYSCTL_CLK_TH0 (K210_SYSCTL_BASE + 0x38) /* Clock threshold controller 0 */ +#define K210_SYSCTL_CLK_TH1 (K210_SYSCTL_BASE + 0x3c) /* Clock threshold controller 1 */ +#define K210_SYSCTL_CLK_TH2 (K210_SYSCTL_BASE + 0x40) /* Clock threshold controller 2 */ +#define K210_SYSCTL_CLK_TH3 (K210_SYSCTL_BASE + 0x44) /* Clock threshold controller 3 */ +#define K210_SYSCTL_CLK_TH4 (K210_SYSCTL_BASE + 0x48) /* Clock threshold controller 4 */ +#define K210_SYSCTL_CLK_TH5 (K210_SYSCTL_BASE + 0x4c) /* Clock threshold controller 5 */ +#define K210_SYSCTL_CLK_TH6 (K210_SYSCTL_BASE + 0x50) /* Clock threshold controller 6 */ +#define K210_SYSCTL_MISC (K210_SYSCTL_BASE + 0x54) /* Miscellaneous controller */ +#define K210_SYSCTL_PERI (K210_SYSCTL_BASE + 0x58) /* Peripheral controller */ +#define K210_SYSCTL_SPI_SLEEP (K210_SYSCTL_BASE + 0x5c) /* SPI sleep controller */ +#define K210_SYSCTL_RESET_STATUS (K210_SYSCTL_BASE + 0x60) /* Reset source status */ +#define K210_SYSCTL_DMA_SEL0 (K210_SYSCTL_BASE + 0x64) /* DMA handshake selector 0 */ +#define K210_SYSCTL_DMA_SEL1 (K210_SYSCTL_BASE + 0x68) /* DMA handshake selector 1 */ +#define K210_SYSCTL_POWER_SEL (K210_SYSCTL_BASE + 0x6c) /* IO Power Mode Select */ + +/* PLL bit field extraction macros (legacy, kept for compatibility) */ #define PLL_CLK_R(n) (n & 0x00000f) #define PLL_CLK_F(n) ((n & 0x0003f0) >> 4) #define PLL_CLK_OD(n) ((n & 0x003c00) >> 10) -#define K210_SYSCTL_CLKSEL0 (K210_SYSCTL_BASE + 0x20) +/* PLL register bit field definitions */ + +#define PLL_CLKR_SHIFT (0) /* Reference clock divider */ +#define PLL_CLKR_MASK (0xf << PLL_CLKR_SHIFT) +#define PLL_CLKF_SHIFT (4) /* Feedback divider */ +#define PLL_CLKF_MASK (0x3f << PLL_CLKF_SHIFT) +#define PLL_CLKOD_SHIFT (10) /* Output divider */ +#define PLL_CLKOD_MASK (0xf << PLL_CLKOD_SHIFT) +#define PLL_BWADJ_SHIFT (14) /* Bandwidth adjust */ +#define PLL_BWADJ_MASK (0x3f << PLL_BWADJ_SHIFT) +#define PLL_RESET_SHIFT (20) /* PLL reset */ +#define PLL_RESET_MASK (1 << PLL_RESET_SHIFT) +#define PLL_PWRD_SHIFT (21) /* Power down */ +#define PLL_PWRD_MASK (1 << PLL_PWRD_SHIFT) +#define PLL_BYPASS_SHIFT (23) /* Bypass */ +#define PLL_BYPASS_MASK (1 << PLL_BYPASS_SHIFT) +#define PLL_OUT_EN_SHIFT (25) /* Output enable */ +#define PLL_OUT_EN_MASK (1 << PLL_OUT_EN_SHIFT) + +/* CLK_EN_CENT register bit definitions (Central clock enable) */ + +#define CLK_EN_CENT_CPU_SHIFT (0) /* CPU clock enable */ +#define CLK_EN_CENT_CPU_MASK (1 << CLK_EN_CENT_CPU_SHIFT) +#define CLK_EN_CENT_SRAM0_SHIFT (1) /* SRAM0 clock enable */ +#define CLK_EN_CENT_SRAM0_MASK (1 << CLK_EN_CENT_SRAM0_SHIFT) +#define CLK_EN_CENT_SRAM1_SHIFT (2) /* SRAM1 clock enable */ +#define CLK_EN_CENT_SRAM1_MASK (1 << CLK_EN_CENT_SRAM1_SHIFT) +#define CLK_EN_CENT_APB0_SHIFT (3) /* APB0 bus clock enable */ +#define CLK_EN_CENT_APB0_MASK (1 << CLK_EN_CENT_APB0_SHIFT) +#define CLK_EN_CENT_APB1_SHIFT (4) /* APB1 bus clock enable */ +#define CLK_EN_CENT_APB1_MASK (1 << CLK_EN_CENT_APB1_SHIFT) +#define CLK_EN_CENT_APB2_SHIFT (5) /* APB2 bus clock enable */ +#define CLK_EN_CENT_APB2_MASK (1 << CLK_EN_CENT_APB2_SHIFT) + +/* CLK_EN_PERI register bit definitions (Peripheral clock enable) */ + +#define CLK_EN_PERI_ROM_SHIFT (0) /* ROM clock enable */ +#define CLK_EN_PERI_ROM_MASK (1 << CLK_EN_PERI_ROM_SHIFT) +#define CLK_EN_PERI_DMA_SHIFT (1) /* DMA clock enable */ +#define CLK_EN_PERI_DMA_MASK (1 << CLK_EN_PERI_DMA_SHIFT) +#define CLK_EN_PERI_AI_SHIFT (2) /* AI accelerator clock enable */ +#define CLK_EN_PERI_AI_MASK (1 << CLK_EN_PERI_AI_SHIFT) +#define CLK_EN_PERI_DVP_SHIFT (3) /* DVP camera interface clock enable */ +#define CLK_EN_PERI_DVP_MASK (1 << CLK_EN_PERI_DVP_SHIFT) +#define CLK_EN_PERI_FFT_SHIFT (4) /* FFT accelerator clock enable */ +#define CLK_EN_PERI_FFT_MASK (1 << CLK_EN_PERI_FFT_SHIFT) +#define CLK_EN_PERI_GPIO_SHIFT (5) /* GPIO clock enable */ +#define CLK_EN_PERI_GPIO_MASK (1 << CLK_EN_PERI_GPIO_SHIFT) +#define CLK_EN_PERI_SPI0_SHIFT (6) /* SPI0 clock enable */ +#define CLK_EN_PERI_SPI0_MASK (1 << CLK_EN_PERI_SPI0_SHIFT) +#define CLK_EN_PERI_SPI1_SHIFT (7) /* SPI1 clock enable */ +#define CLK_EN_PERI_SPI1_MASK (1 << CLK_EN_PERI_SPI1_SHIFT) +#define CLK_EN_PERI_SPI2_SHIFT (8) /* SPI2 clock enable */ +#define CLK_EN_PERI_SPI2_MASK (1 << CLK_EN_PERI_SPI2_SHIFT) +#define CLK_EN_PERI_SPI3_SHIFT (9) /* SPI3 clock enable */ +#define CLK_EN_PERI_SPI3_MASK (1 << CLK_EN_PERI_SPI3_SHIFT) +#define CLK_EN_PERI_I2S0_SHIFT (10) /* I2S0 clock enable */ +#define CLK_EN_PERI_I2S0_MASK (1 << CLK_EN_PERI_I2S0_SHIFT) +#define CLK_EN_PERI_I2S1_SHIFT (11) /* I2S1 clock enable */ +#define CLK_EN_PERI_I2S1_MASK (1 << CLK_EN_PERI_I2S1_SHIFT) +#define CLK_EN_PERI_I2S2_SHIFT (12) /* I2S2 clock enable */ +#define CLK_EN_PERI_I2S2_MASK (1 << CLK_EN_PERI_I2S2_SHIFT) +#define CLK_EN_PERI_I2C0_SHIFT (13) /* I2C0 clock enable */ +#define CLK_EN_PERI_I2C0_MASK (1 << CLK_EN_PERI_I2C0_SHIFT) +#define CLK_EN_PERI_I2C1_SHIFT (14) /* I2C1 clock enable */ +#define CLK_EN_PERI_I2C1_MASK (1 << CLK_EN_PERI_I2C1_SHIFT) +#define CLK_EN_PERI_I2C2_SHIFT (15) /* I2C2 clock enable */ +#define CLK_EN_PERI_I2C2_MASK (1 << CLK_EN_PERI_I2C2_SHIFT) +#define CLK_EN_PERI_UART1_SHIFT (16) /* UART1 clock enable */ +#define CLK_EN_PERI_UART1_MASK (1 << CLK_EN_PERI_UART1_SHIFT) +#define CLK_EN_PERI_UART2_SHIFT (17) /* UART2 clock enable */ +#define CLK_EN_PERI_UART2_MASK (1 << CLK_EN_PERI_UART2_SHIFT) +#define CLK_EN_PERI_UART3_SHIFT (18) /* UART3 clock enable */ +#define CLK_EN_PERI_UART3_MASK (1 << CLK_EN_PERI_UART3_SHIFT) +#define CLK_EN_PERI_AES_SHIFT (19) /* AES accelerator clock enable */ +#define CLK_EN_PERI_AES_MASK (1 << CLK_EN_PERI_AES_SHIFT) +#define CLK_EN_PERI_FPIOA_SHIFT (20) /* FPIOA (GPIO multiplexer) clock enable */ +#define CLK_EN_PERI_FPIOA_MASK (1 << CLK_EN_PERI_FPIOA_SHIFT) +#define CLK_EN_PERI_TIMER0_SHIFT (21) /* TIMER0 clock enable */ +#define CLK_EN_PERI_TIMER0_MASK (1 << CLK_EN_PERI_TIMER0_SHIFT) +#define CLK_EN_PERI_TIMER1_SHIFT (22) /* TIMER1 clock enable */ +#define CLK_EN_PERI_TIMER1_MASK (1 << CLK_EN_PERI_TIMER1_SHIFT) +#define CLK_EN_PERI_TIMER2_SHIFT (23) /* TIMER2 clock enable */ +#define CLK_EN_PERI_TIMER2_MASK (1 << CLK_EN_PERI_TIMER2_SHIFT) +#define CLK_EN_PERI_WDT0_SHIFT (24) /* Watchdog timer 0 clock enable */ +#define CLK_EN_PERI_WDT0_MASK (1 << CLK_EN_PERI_WDT0_SHIFT) +#define CLK_EN_PERI_WDT1_SHIFT (25) /* Watchdog timer 1 clock enable */ +#define CLK_EN_PERI_WDT1_MASK (1 << CLK_EN_PERI_WDT1_SHIFT) +#define CLK_EN_PERI_SHA_SHIFT (26) /* SHA accelerator clock enable */ +#define CLK_EN_PERI_SHA_MASK (1 << CLK_EN_PERI_SHA_SHIFT) +#define CLK_EN_PERI_OTP_SHIFT (27) /* OTP (One-Time Programmable) clock enable */ +#define CLK_EN_PERI_OTP_MASK (1 << CLK_EN_PERI_OTP_SHIFT) +#define CLK_EN_PERI_RTC_SHIFT (29) /* RTC clock enable */ +#define CLK_EN_PERI_RTC_MASK (1 << CLK_EN_PERI_RTC_SHIFT) + +/* PERI_RESET register bit definitions (Peripheral reset control) */ + +#define PERI_RESET_ROM_SHIFT (0) /* ROM reset */ +#define PERI_RESET_ROM_MASK (1 << PERI_RESET_ROM_SHIFT) +#define PERI_RESET_DMA_SHIFT (1) /* DMA reset */ +#define PERI_RESET_DMA_MASK (1 << PERI_RESET_DMA_SHIFT) +#define PERI_RESET_AI_SHIFT (2) /* AI accelerator reset */ +#define PERI_RESET_AI_MASK (1 << PERI_RESET_AI_SHIFT) +#define PERI_RESET_DVP_SHIFT (3) /* DVP camera interface reset */ +#define PERI_RESET_DVP_MASK (1 << PERI_RESET_DVP_SHIFT) +#define PERI_RESET_FFT_SHIFT (4) /* FFT accelerator reset */ +#define PERI_RESET_FFT_MASK (1 << PERI_RESET_FFT_SHIFT) +#define PERI_RESET_GPIO_SHIFT (5) /* GPIO reset */ +#define PERI_RESET_GPIO_MASK (1 << PERI_RESET_GPIO_SHIFT) +#define PERI_RESET_SPI0_SHIFT (6) /* SPI0 reset */ +#define PERI_RESET_SPI0_MASK (1 << PERI_RESET_SPI0_SHIFT) +#define PERI_RESET_SPI1_SHIFT (7) /* SPI1 reset */ +#define PERI_RESET_SPI1_MASK (1 << PERI_RESET_SPI1_SHIFT) +#define PERI_RESET_SPI2_SHIFT (8) /* SPI2 reset */ +#define PERI_RESET_SPI2_MASK (1 << PERI_RESET_SPI2_SHIFT) +#define PERI_RESET_SPI3_SHIFT (9) /* SPI3 reset */ +#define PERI_RESET_SPI3_MASK (1 << PERI_RESET_SPI3_SHIFT) +#define PERI_RESET_I2S0_SHIFT (10) /* I2S0 reset */ +#define PERI_RESET_I2S0_MASK (1 << PERI_RESET_I2S0_SHIFT) +#define PERI_RESET_I2S1_SHIFT (11) /* I2S1 reset */ +#define PERI_RESET_I2S1_MASK (1 << PERI_RESET_I2S1_SHIFT) +#define PERI_RESET_I2S2_SHIFT (12) /* I2S2 reset */ +#define PERI_RESET_I2S2_MASK (1 << PERI_RESET_I2S2_SHIFT) +#define PERI_RESET_I2C0_SHIFT (13) /* I2C0 reset */ +#define PERI_RESET_I2C0_MASK (1 << PERI_RESET_I2C0_SHIFT) +#define PERI_RESET_I2C1_SHIFT (14) /* I2C1 reset */ +#define PERI_RESET_I2C1_MASK (1 << PERI_RESET_I2C1_SHIFT) +#define PERI_RESET_I2C2_SHIFT (15) /* I2C2 reset */ +#define PERI_RESET_I2C2_MASK (1 << PERI_RESET_I2C2_SHIFT) +#define PERI_RESET_UART1_SHIFT (16) /* UART1 reset */ +#define PERI_RESET_UART1_MASK (1 << PERI_RESET_UART1_SHIFT) +#define PERI_RESET_UART2_SHIFT (17) /* UART2 reset */ +#define PERI_RESET_UART2_MASK (1 << PERI_RESET_UART2_SHIFT) +#define PERI_RESET_UART3_SHIFT (18) /* UART3 reset */ +#define PERI_RESET_UART3_MASK (1 << PERI_RESET_UART3_SHIFT) +#define PERI_RESET_AES_SHIFT (19) /* AES accelerator reset */ +#define PERI_RESET_AES_MASK (1 << PERI_RESET_AES_SHIFT) +#define PERI_RESET_FPIOA_SHIFT (20) /* FPIOA (GPIO multiplexer) reset */ +#define PERI_RESET_FPIOA_MASK (1 << PERI_RESET_FPIOA_SHIFT) +#define PERI_RESET_TIMER0_SHIFT (21) /* TIMER0 reset */ +#define PERI_RESET_TIMER0_MASK (1 << PERI_RESET_TIMER0_SHIFT) +#define PERI_RESET_TIMER1_SHIFT (22) /* TIMER1 reset */ +#define PERI_RESET_TIMER1_MASK (1 << PERI_RESET_TIMER1_SHIFT) +#define PERI_RESET_TIMER2_SHIFT (23) /* TIMER2 reset */ +#define PERI_RESET_TIMER2_MASK (1 << PERI_RESET_TIMER2_SHIFT) +#define PERI_RESET_WDT0_SHIFT (24) /* Watchdog timer 0 reset */ +#define PERI_RESET_WDT0_MASK (1 << PERI_RESET_WDT0_SHIFT) +#define PERI_RESET_WDT1_SHIFT (25) /* Watchdog timer 1 reset */ +#define PERI_RESET_WDT1_MASK (1 << PERI_RESET_WDT1_SHIFT) +#define PERI_RESET_SHA_SHIFT (26) /* SHA accelerator reset */ +#define PERI_RESET_SHA_MASK (1 << PERI_RESET_SHA_SHIFT) +#define PERI_RESET_RTC_SHIFT (29) /* RTC reset */ +#define PERI_RESET_RTC_MASK (1 << PERI_RESET_RTC_SHIFT) + +/* RESET_STATUS register bit definitions (Reset source status) */ + +#define RESET_STATUS_CLR_SHIFT (0) /* Reset status clear */ +#define RESET_STATUS_CLR_MASK (1 << RESET_STATUS_CLR_SHIFT) +#define RESET_STATUS_PIN_SHIFT (1) /* Pin reset status */ +#define RESET_STATUS_PIN_MASK (1 << RESET_STATUS_PIN_SHIFT) +#define RESET_STATUS_WDT0_SHIFT (2) /* Watchdog timer 0 reset status */ +#define RESET_STATUS_WDT0_MASK (1 << RESET_STATUS_WDT0_SHIFT) +#define RESET_STATUS_WDT1_SHIFT (3) /* Watchdog timer 1 reset status */ +#define RESET_STATUS_WDT1_MASK (1 << RESET_STATUS_WDT1_SHIFT) +#define RESET_STATUS_SOFT_SHIFT (4) /* Soft reset status */ +#define RESET_STATUS_SOFT_MASK (1 << RESET_STATUS_SOFT_SHIFT) + +/* CLKSEL0 register bit definitions (Clock select controller 0) */ + +#define CLKSEL0_ACLK_SEL_SHIFT (0) +#define CLKSEL0_ACLK_SEL_MASK (1 << CLKSEL0_ACLK_SEL_SHIFT) +#define CLKSEL0_ACLK_DIV_SHIFT (1) +#define CLKSEL0_ACLK_DIV_MASK (3 << CLKSEL0_ACLK_DIV_SHIFT) +#define CLKSEL0_APB0_DIV_SHIFT (3) +#define CLKSEL0_APB0_DIV_MASK (7 << CLKSEL0_APB0_DIV_SHIFT) +#define CLKSEL0_APB1_DIV_SHIFT (6) +#define CLKSEL0_APB1_DIV_MASK (7 << CLKSEL0_APB1_DIV_SHIFT) +#define CLKSEL0_APB2_DIV_SHIFT (9) +#define CLKSEL0_APB2_DIV_MASK (7 << CLKSEL0_APB2_DIV_SHIFT) + +/* CLK_TH0 register bit definitions (Clock threshold controller 0) */ + +#define CLK_TH0_SRAM0_SHIFT (0) +#define CLK_TH0_SRAM0_MASK (0xf << CLK_TH0_SRAM0_SHIFT) +#define CLK_TH0_SRAM1_SHIFT (4) +#define CLK_TH0_SRAM1_MASK (0xf << CLK_TH0_SRAM1_SHIFT) +#define CLK_TH0_AI_SHIFT (8) +#define CLK_TH0_AI_MASK (0xf << CLK_TH0_AI_SHIFT) +#define CLK_TH0_DVP_SHIFT (12) +#define CLK_TH0_DVP_MASK (0xf << CLK_TH0_DVP_SHIFT) +#define CLK_TH0_ROM_SHIFT (16) +#define CLK_TH0_ROM_MASK (0xf << CLK_TH0_ROM_SHIFT) + +/* CLK_TH1 register bit definitions (Clock threshold controller 1) */ + +#define CLK_TH1_SPI0_SHIFT (0) +#define CLK_TH1_SPI0_MASK (0xff << CLK_TH1_SPI0_SHIFT) +#define CLK_TH1_SPI1_SHIFT (8) +#define CLK_TH1_SPI1_MASK (0xff << CLK_TH1_SPI1_SHIFT) +#define CLK_TH1_SPI2_SHIFT (16) +#define CLK_TH1_SPI2_MASK (0xff << CLK_TH1_SPI2_SHIFT) +#define CLK_TH1_SPI3_SHIFT (24) +#define CLK_TH1_SPI3_MASK (0xff << CLK_TH1_SPI3_SHIFT) + +/* CLK_TH2 register bit definitions (Clock threshold controller 2) */ + +#define CLK_TH2_TIMER0_SHIFT (0) +#define CLK_TH2_TIMER0_MASK (0xff << CLK_TH2_TIMER0_SHIFT) +#define CLK_TH2_TIMER1_SHIFT (8) +#define CLK_TH2_TIMER1_MASK (0xff << CLK_TH2_TIMER1_SHIFT) +#define CLK_TH2_TIMER2_SHIFT (16) +#define CLK_TH2_TIMER2_MASK (0xff << CLK_TH2_TIMER2_SHIFT) + +/* CLK_TH3 register bit definitions (Clock threshold controller 3) */ + +#define CLK_TH3_I2S0_SHIFT (0) +#define CLK_TH3_I2S0_MASK (0xffff << CLK_TH3_I2S0_SHIFT) +#define CLK_TH3_I2S1_SHIFT (16) +#define CLK_TH3_I2S1_MASK (0xffff << CLK_TH3_I2S1_SHIFT) + +/* CLK_TH4 register bit definitions (Clock threshold controller 4) */ + +#define CLK_TH4_I2S2_SHIFT (0) +#define CLK_TH4_I2S2_MASK (0xffff << CLK_TH4_I2S2_SHIFT) + +/* CLK_TH5 register bit definitions (Clock threshold controller 5) */ + +#define CLK_TH5_I2C0_SHIFT (8) +#define CLK_TH5_I2C0_MASK (0xff << CLK_TH5_I2C0_SHIFT) +#define CLK_TH5_I2C1_SHIFT (16) +#define CLK_TH5_I2C1_MASK (0xff << CLK_TH5_I2C1_SHIFT) +#define CLK_TH5_I2C2_SHIFT (24) +#define CLK_TH5_I2C2_MASK (0xff << CLK_TH5_I2C2_SHIFT) + +/* CLK_TH6 register bit definitions (Clock threshold controller 6) */ + +#define CLK_TH6_WDT0_SHIFT (0) +#define CLK_TH6_WDT0_MASK (0xff << CLK_TH6_WDT0_SHIFT) +#define CLK_TH6_WDT1_SHIFT (8) +#define CLK_TH6_WDT1_MASK (0xff << CLK_TH6_WDT1_SHIFT) + +/* PLL_LOCK register bit definitions (PLL lock tester) */ + +#define PLL_LOCK_PLL0_SHIFT (0) +#define PLL_LOCK_PLL0_MASK (0x3 << PLL_LOCK_PLL0_SHIFT) +#define PLL_LOCK_PLL0_SLIP_CLR (1 << 2) +#define PLL_LOCK_PLL1_SHIFT (8) +#define PLL_LOCK_PLL1_MASK (0x1 << PLL_LOCK_PLL1_SHIFT) +#define PLL_LOCK_PLL1_SLIP_CLR (1 << 10) +#define PLL_LOCK_PLL2_SHIFT (16) +#define PLL_LOCK_PLL2_MASK (0x1 << PLL_LOCK_PLL2_SHIFT) +#define PLL_LOCK_PLL2_SLIP_CLR (1 << 18) + +#define PLL_LOCK_PLL0_LOCKED (0x3 << PLL_LOCK_PLL0_SHIFT) +#define PLL_LOCK_PLL1_LOCKED (0x1 << PLL_LOCK_PLL1_SHIFT) +#define PLL_LOCK_PLL2_LOCKED (0x1 << PLL_LOCK_PLL2_SHIFT) + +/* Clock select macros */ #define CLKSEL0_ACLK_SEL(n) (n & 0x00000001) diff --git a/arch/risc-v/src/k210/k210_clockconfig.c b/arch/risc-v/src/k210/k210_clockconfig.c index 10819ead985..b911cd3d4b6 100644 --- a/arch/risc-v/src/k210/k210_clockconfig.c +++ b/arch/risc-v/src/k210/k210_clockconfig.c @@ -35,6 +35,7 @@ #include "riscv_internal.h" #include "k210_clockconfig.h" +#include "k210_sysctl.h" /**************************************************************************** * Pre-processor Definitions @@ -46,7 +47,7 @@ * Private Data ****************************************************************************/ -static uint32_t g_cpu_clock = 416000000; +static uint32_t g_cpu_clock = CONFIG_K210_CPU_FREQ; /**************************************************************************** * Public Functions @@ -90,22 +91,43 @@ void k210_clockconfig(void) { #ifndef CONFIG_K210_WITH_QEMU uint32_t clksel0; + uint32_t div; - /* Obtain clock selector for ACLK */ + /* Initialize sysctl driver */ - clksel0 = getreg32(K210_SYSCTL_CLKSEL0); + k210_sysctl_init(); - if (1 == CLKSEL0_ACLK_SEL(clksel0)) + /* Wait for PLL0 to lock before configuring clocks */ + + while (!k210_sysctl_pll_is_locked(K210_SYSCTL_PLL0)) { - /* PLL0 selected */ - - g_cpu_clock = k210_get_pll0clk() / 2; + up_mdelay(1); } - else - { - /* OSC selected */ - g_cpu_clock = OSC_FREQ; + /* Enable essential system clocks */ + + k210_sysctl_clock_enable(K210_CLOCK_CPU); + k210_sysctl_clock_enable(K210_CLOCK_SRAM0); + k210_sysctl_clock_enable(K210_CLOCK_SRAM1); + + /* Use new frequency API to update g_cpu_clock */ + + g_cpu_clock = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + if (g_cpu_clock == 0) + { + /* Fallback to PLL frequency calculation if new API fails */ + + clksel0 = getreg32(K210_SYSCTL_CLKSEL0); + + if (1 == CLKSEL0_ACLK_SEL(clksel0)) + { + div = (clksel0 & CLKSEL0_ACLK_DIV_MASK) >> CLKSEL0_ACLK_DIV_SHIFT; + g_cpu_clock = k210_get_pll0clk() / (2u << div); + } + else + { + g_cpu_clock = OSC_FREQ; + } } /* Workaround for stabilization */ diff --git a/arch/risc-v/src/k210/k210_sysctl.c b/arch/risc-v/src/k210/k210_sysctl.c new file mode 100644 index 00000000000..4c06ead3bf3 --- /dev/null +++ b/arch/risc-v/src/k210/k210_sysctl.c @@ -0,0 +1,1276 @@ +/**************************************************************************** + * arch/risc-v/src/k210/k210_sysctl.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include "riscv_internal.h" +#include "hardware/k210_sysctl.h" +#include "hardware/k210_memorymap.h" +#include "k210_sysctl.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define K210_PLL_INPUT_FREQ 26000000UL + +#define K210_PLL_REF_MIN 13671900UL +#define K210_PLL_REF_MAX 1750000000ULL +#define K210_PLL_VCO_MIN 350000000ULL +#define K210_PLL_VCO_MAX 1750000000ULL + +#define K210_PLL_NR_MIN 1 +#define K210_PLL_NR_MAX 16 +#define K210_PLL_NF_MIN 1 +#define K210_PLL_NF_MAX 64 +#define K210_PLL_OD_MIN 1 +#define K210_PLL_OD_MAX 16 + +#define K210_PLL_LOCK_TIMEOUT_US 50000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct k210_pll_params_s +{ + uint8_t clkr; + uint8_t clkf; + uint8_t clkod; + uint8_t bwadj; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: k210_sysctl_pll_get_freq + * + * Description: + * Calculate PLL output frequency from register fields. + * + * Formula: FOUT = FIN * (CLKF + 1) / (CLKR + 1) / (CLKOD + 1) + * Where FIN = 26MHz (external crystal oscillator) + * + * Input Parameters: + * pll_offset - PLL register offset (K210_SYSCTL_PLL0/PLL1/PLL2) + * + * Returned Value: + * PLL output frequency in Hz + * + ****************************************************************************/ + +static uint32_t k210_sysctl_pll_get_freq(uint32_t pll_base) +{ + uint32_t regval = getreg32(pll_base); + uint32_t clkr = (regval >> PLL_CLKR_SHIFT) & 0x0f; + uint32_t clkf = (regval >> PLL_CLKF_SHIFT) & 0x3f; + uint32_t clkod = (regval >> PLL_CLKOD_SHIFT) & 0x0f; + + return (K210_PLL_INPUT_FREQ * (clkf + 1)) / (clkr + 1) / (clkod + 1); +} + +/**************************************************************************** + * Name: k210_clock_to_bit + * + * Description: + * Map a clock ID to its corresponding bit position in CLK_EN_PERI + * register. + * + * Input Parameters: + * clkid - Clock ID to map + * bit - Pointer to store the bit position + * + * Returned Value: + * OK on success, -EINVAL for invalid clock ID + * + ****************************************************************************/ + +static int k210_clock_to_bit(k210_clockid_t clkid, uint32_t *bit) +{ + switch (clkid) + { + case K210_CLOCK_ROM: + *bit = CLK_EN_PERI_ROM_SHIFT; + break; + case K210_CLOCK_DMA: + *bit = CLK_EN_PERI_DMA_SHIFT; + break; + case K210_CLOCK_AI: + *bit = CLK_EN_PERI_AI_SHIFT; + break; + case K210_CLOCK_DVP: + *bit = CLK_EN_PERI_DVP_SHIFT; + break; + case K210_CLOCK_FFT: + *bit = CLK_EN_PERI_FFT_SHIFT; + break; + case K210_CLOCK_GPIO: + *bit = CLK_EN_PERI_GPIO_SHIFT; + break; + case K210_CLOCK_SPI0: + *bit = CLK_EN_PERI_SPI0_SHIFT; + break; + case K210_CLOCK_SPI1: + *bit = CLK_EN_PERI_SPI1_SHIFT; + break; + case K210_CLOCK_SPI2: + *bit = CLK_EN_PERI_SPI2_SHIFT; + break; + case K210_CLOCK_SPI3: + *bit = CLK_EN_PERI_SPI3_SHIFT; + break; + case K210_CLOCK_I2S0: + *bit = CLK_EN_PERI_I2S0_SHIFT; + break; + case K210_CLOCK_I2S1: + *bit = CLK_EN_PERI_I2S1_SHIFT; + break; + case K210_CLOCK_I2S2: + *bit = CLK_EN_PERI_I2S2_SHIFT; + break; + case K210_CLOCK_I2C0: + *bit = CLK_EN_PERI_I2C0_SHIFT; + break; + case K210_CLOCK_I2C1: + *bit = CLK_EN_PERI_I2C1_SHIFT; + break; + case K210_CLOCK_I2C2: + *bit = CLK_EN_PERI_I2C2_SHIFT; + break; + case K210_CLOCK_UART1: + *bit = CLK_EN_PERI_UART1_SHIFT; + break; + case K210_CLOCK_UART2: + *bit = CLK_EN_PERI_UART2_SHIFT; + break; + case K210_CLOCK_UART3: + *bit = CLK_EN_PERI_UART3_SHIFT; + break; + case K210_CLOCK_AES: + *bit = CLK_EN_PERI_AES_SHIFT; + break; + case K210_CLOCK_FPIOA: + *bit = CLK_EN_PERI_FPIOA_SHIFT; + break; + case K210_CLOCK_TIMER0: + *bit = CLK_EN_PERI_TIMER0_SHIFT; + break; + case K210_CLOCK_TIMER1: + *bit = CLK_EN_PERI_TIMER1_SHIFT; + break; + case K210_CLOCK_TIMER2: + *bit = CLK_EN_PERI_TIMER2_SHIFT; + break; + case K210_CLOCK_WDT0: + *bit = CLK_EN_PERI_WDT0_SHIFT; + break; + case K210_CLOCK_WDT1: + *bit = CLK_EN_PERI_WDT1_SHIFT; + break; + case K210_CLOCK_SHA: + *bit = CLK_EN_PERI_SHA_SHIFT; + break; + case K210_CLOCK_OTP: + *bit = CLK_EN_PERI_OTP_SHIFT; + break; + case K210_CLOCK_RTC: + *bit = CLK_EN_PERI_RTC_SHIFT; + break; + default: + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: k210_reset_to_bit + * + * Description: + * Map a reset ID to its corresponding bit position in PERI_RESET register. + * + * Input Parameters: + * rstidx - Reset ID to map + * bit - Pointer to store the bit position + * + * Returned Value: + * OK on success, -EINVAL for invalid reset ID + * + ****************************************************************************/ + +static int k210_reset_to_bit(k210_rstidx_t rstidx, uint32_t *bit) +{ + switch (rstidx) + { + case K210_RESET_ROM: + *bit = PERI_RESET_ROM_SHIFT; + break; + case K210_RESET_DMA: + *bit = PERI_RESET_DMA_SHIFT; + break; + case K210_RESET_AI: + *bit = PERI_RESET_AI_SHIFT; + break; + case K210_RESET_DVP: + *bit = PERI_RESET_DVP_SHIFT; + break; + case K210_RESET_FFT: + *bit = PERI_RESET_FFT_SHIFT; + break; + case K210_RESET_GPIO: + *bit = PERI_RESET_GPIO_SHIFT; + break; + case K210_RESET_SPI0: + *bit = PERI_RESET_SPI0_SHIFT; + break; + case K210_RESET_SPI1: + *bit = PERI_RESET_SPI1_SHIFT; + break; + case K210_RESET_SPI2: + *bit = PERI_RESET_SPI2_SHIFT; + break; + case K210_RESET_SPI3: + *bit = PERI_RESET_SPI3_SHIFT; + break; + case K210_RESET_I2S0: + *bit = PERI_RESET_I2S0_SHIFT; + break; + case K210_RESET_I2S1: + *bit = PERI_RESET_I2S1_SHIFT; + break; + case K210_RESET_I2S2: + *bit = PERI_RESET_I2S2_SHIFT; + break; + case K210_RESET_I2C0: + *bit = PERI_RESET_I2C0_SHIFT; + break; + case K210_RESET_I2C1: + *bit = PERI_RESET_I2C1_SHIFT; + break; + case K210_RESET_I2C2: + *bit = PERI_RESET_I2C2_SHIFT; + break; + case K210_RESET_UART1: + *bit = PERI_RESET_UART1_SHIFT; + break; + case K210_RESET_UART2: + *bit = PERI_RESET_UART2_SHIFT; + break; + case K210_RESET_UART3: + *bit = PERI_RESET_UART3_SHIFT; + break; + case K210_RESET_AES: + *bit = PERI_RESET_AES_SHIFT; + break; + case K210_RESET_FPIOA: + *bit = PERI_RESET_FPIOA_SHIFT; + break; + case K210_RESET_TIMER0: + *bit = PERI_RESET_TIMER0_SHIFT; + break; + case K210_RESET_TIMER1: + *bit = PERI_RESET_TIMER1_SHIFT; + break; + case K210_RESET_TIMER2: + *bit = PERI_RESET_TIMER2_SHIFT; + break; + case K210_RESET_WDT0: + *bit = PERI_RESET_WDT0_SHIFT; + break; + case K210_RESET_WDT1: + *bit = PERI_RESET_WDT1_SHIFT; + break; + case K210_RESET_SHA: + *bit = PERI_RESET_SHA_SHIFT; + break; + case K210_RESET_RTC: + *bit = PERI_RESET_RTC_SHIFT; + break; + default: + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: k210_clock_to_apb + * + * Description: + * Map a clock ID to its APB bus number. + * + * APB0: GPIO, UART1-3, I2S0-2, I2C0-2, FPIOA, TIMER0-2, SHA + * APB1: AES, WDT0-1, OTP, RTC + * APB2: SPI0, SPI1 + * Others: ROM, DMA, AI, DVP, FFT, SPI2, SPI3 do not require APB bus clock + * + * Input Parameters: + * clkid - Clock ID to map + * + * Returned Value: + * 0 for APB0, 1 for APB1, 2 for APB2, -1 if not on APB bus + * + ****************************************************************************/ + +static int k210_clock_to_apb(k210_clockid_t clkid) +{ + switch (clkid) + { + case K210_CLOCK_GPIO: + case K210_CLOCK_UART1: + case K210_CLOCK_UART2: + case K210_CLOCK_UART3: + case K210_CLOCK_I2S0: + case K210_CLOCK_I2S1: + case K210_CLOCK_I2S2: + case K210_CLOCK_I2C0: + case K210_CLOCK_I2C1: + case K210_CLOCK_I2C2: + case K210_CLOCK_FPIOA: + case K210_CLOCK_TIMER0: + case K210_CLOCK_TIMER1: + case K210_CLOCK_TIMER2: + case K210_CLOCK_SHA: + return 0; + + case K210_CLOCK_AES: + case K210_CLOCK_WDT0: + case K210_CLOCK_WDT1: + case K210_CLOCK_OTP: + case K210_CLOCK_RTC: + return 1; + + case K210_CLOCK_SPI0: + case K210_CLOCK_SPI1: + return 2; + + default: + return -1; + } +} + +/**************************************************************************** + * Name: k210_clock_is_central + * + * Description: + * Check if a clock ID is a central clock (CPU, SRAM, APB). + * + * Central clocks are controlled via CLK_EN_CENT register, while + * peripheral clocks use CLK_EN_PERI register. + * + * Input Parameters: + * clkid - Clock ID to check + * + * Returned Value: + * true if central clock, false otherwise + * + ****************************************************************************/ + +static bool k210_clock_is_central(k210_clockid_t clkid) +{ + switch (clkid) + { + case K210_CLOCK_CPU: + case K210_CLOCK_SRAM0: + case K210_CLOCK_SRAM1: + case K210_CLOCK_APB0: + case K210_CLOCK_APB1: + case K210_CLOCK_APB2: + return true; + default: + return false; + } +} + +/**************************************************************************** + * Name: k210_clock_to_central_bit + * + * Description: + * Map a central clock ID to its corresponding bit mask in CLK_EN_CENT. + * + * Input Parameters: + * clkid - Central clock ID to map + * mask - Pointer to store the bit mask + * + * Returned Value: + * OK on success, -EINVAL for invalid clock ID + * + ****************************************************************************/ + +static int k210_clock_to_central_bit(k210_clockid_t clkid, uint32_t *mask) +{ + switch (clkid) + { + case K210_CLOCK_CPU: + *mask = CLK_EN_CENT_CPU_MASK; + break; + case K210_CLOCK_SRAM0: + *mask = CLK_EN_CENT_SRAM0_MASK; + break; + case K210_CLOCK_SRAM1: + *mask = CLK_EN_CENT_SRAM1_MASK; + break; + case K210_CLOCK_APB0: + *mask = CLK_EN_CENT_APB0_MASK; + break; + case K210_CLOCK_APB1: + *mask = CLK_EN_CENT_APB1_MASK; + break; + case K210_CLOCK_APB2: + *mask = CLK_EN_CENT_APB2_MASK; + break; + default: + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: k210_sysctl_enable_apb_clock + * + * Description: + * Enable the APB bus clock for a peripheral. + * + * K210 peripheral clock enable requires a two-stage process: + * 1. First enable the corresponding APB bus clock (CLK_EN_CENT) + * 2. Then enable the peripheral clock (CLK_EN_PERI) + * + * This separation prevents accidental APB clock disable when CPU + * manipulates peripheral clock bits. + * + * Input Parameters: + * clkid - Clock ID of the peripheral + * + * Returned Value: + * OK on success + * + ****************************************************************************/ + +static int k210_sysctl_enable_apb_clock(k210_clockid_t clkid) +{ + int apb = k210_clock_to_apb(clkid); + uint32_t regval; + + if (apb < 0) + { + return OK; + } + + regval = getreg32(K210_SYSCTL_CLK_EN_CENT); + + switch (apb) + { + case 0: + regval |= CLK_EN_CENT_APB0_MASK; + break; + case 1: + regval |= CLK_EN_CENT_APB1_MASK; + break; + case 2: + regval |= CLK_EN_CENT_APB2_MASK; + break; + } + + putreg32(regval, K210_SYSCTL_CLK_EN_CENT); + return OK; +} + +/**************************************************************************** + * Name: k210_sysctl_pll_wait_lock + ****************************************************************************/ + +static void k210_sysctl_pll_clear_slip(uint32_t pll_base) +{ + uint32_t regval; + + regval = getreg32(K210_SYSCTL_PLL_LOCK); + + if (pll_base == K210_SYSCTL_PLL0) + { + regval |= PLL_LOCK_PLL0_SLIP_CLR; + } + else if (pll_base == K210_SYSCTL_PLL1) + { + regval |= PLL_LOCK_PLL1_SLIP_CLR; + } + else if (pll_base == K210_SYSCTL_PLL2) + { + regval |= PLL_LOCK_PLL2_SLIP_CLR; + } + + putreg32(regval, K210_SYSCTL_PLL_LOCK); +} + +static bool k210_sysctl_pll_wait_lock(uint32_t pll_base) +{ + uint32_t timeout = K210_PLL_LOCK_TIMEOUT_US; + + while (timeout-- > 0) + { + if (k210_sysctl_pll_is_locked(pll_base)) + { + return true; + } + + k210_sysctl_pll_clear_slip(pll_base); + up_udelay(1); + } + + return false; +} + +/**************************************************************************** + * Name: k210_sysctl_pll_calc_params + ****************************************************************************/ + +static bool k210_sysctl_pll_calc_params(uint32_t fin, uint32_t fout, + struct k210_pll_params_s *params) +{ + uint32_t nr; + uint32_t nf; + uint32_t od; + uint64_t best_err = UINT64_MAX; + uint64_t best_vco = 0; + uint32_t best_nr = 0; + bool found = false; + + if (fout == 0 || params == NULL) + { + return false; + } + + for (nr = K210_PLL_NR_MIN; nr <= K210_PLL_NR_MAX; nr++) + { + uint64_t ref_hz = fin / nr; + + if (ref_hz < K210_PLL_REF_MIN || ref_hz > K210_PLL_REF_MAX) + { + continue; + } + + for (nf = K210_PLL_NF_MIN; nf <= K210_PLL_NF_MAX; nf++) + { + uint64_t vco_hz = ((uint64_t)fin * nf) / nr; + + if (vco_hz < K210_PLL_VCO_MIN || vco_hz > K210_PLL_VCO_MAX) + { + continue; + } + + for (od = K210_PLL_OD_MIN; od <= K210_PLL_OD_MAX; od++) + { + uint64_t out_hz = vco_hz / od; + uint64_t err = out_hz > fout ? out_hz - fout : fout - out_hz; + + if (!found || + err < best_err || + (err == best_err && vco_hz > best_vco) || + (err == best_err && vco_hz == best_vco && nr < best_nr)) + { + found = true; + best_err = err; + best_vco = vco_hz; + best_nr = nr; + + params->clkr = nr - 1; + params->clkf = nf - 1; + params->clkod = od - 1; + params->bwadj = nf - 1; + + if (err == 0) + { + return true; + } + } + } + } + } + + return found; +} + +/**************************************************************************** + * Name: k210_sysctl_pll_set_freq + ****************************************************************************/ + +static uint32_t k210_sysctl_pll_set_freq(uint32_t pll_base, uint32_t freq) +{ + struct k210_pll_params_s params = + { + 0 + }; + + uint32_t regval; + uint32_t clksel0; + + if (freq == 0 || pll_base != K210_SYSCTL_PLL0) + { + return 0; + } + + if (!k210_sysctl_pll_calc_params(K210_PLL_INPUT_FREQ, freq, ¶ms)) + { + return 0; + } + + /* 1. Switch CPU clock to IN0 before touching PLL0 */ + + clksel0 = getreg32(K210_SYSCTL_CLKSEL0); + clksel0 &= ~CLKSEL0_ACLK_SEL_MASK; + putreg32(clksel0, K210_SYSCTL_CLKSEL0); + + /* 2. Disable PLL output */ + + regval = getreg32(pll_base); + regval &= ~PLL_OUT_EN_MASK; + putreg32(regval, pll_base); + + /* 3. Power down PLL */ + + regval &= ~PLL_PWRD_MASK; + putreg32(regval, pll_base); + + /* 4. Program PLL parameters */ + + regval &= ~(PLL_CLKR_MASK | PLL_CLKF_MASK | PLL_CLKOD_MASK | + PLL_BWADJ_MASK | PLL_BYPASS_MASK); + regval |= ((uint32_t)params.clkr << PLL_CLKR_SHIFT); + regval |= ((uint32_t)params.clkf << PLL_CLKF_SHIFT); + regval |= ((uint32_t)params.clkod << PLL_CLKOD_SHIFT); + regval |= ((uint32_t)params.bwadj << PLL_BWADJ_SHIFT); + putreg32(regval, pll_base); + + /* 5. Power on PLL */ + + regval |= PLL_PWRD_MASK; + putreg32(regval, pll_base); + up_udelay(1); + + /* 6. Reset PLL and release reset */ + + regval &= ~PLL_RESET_MASK; + putreg32(regval, pll_base); + regval |= PLL_RESET_MASK; + putreg32(regval, pll_base); + up_udelay(1); + regval &= ~PLL_RESET_MASK; + putreg32(regval, pll_base); + + /* 7. Wait lock with timeout */ + + if (!k210_sysctl_pll_wait_lock(pll_base)) + { + return 0; + } + + /* 8. Enable PLL output */ + + regval = getreg32(pll_base); + regval |= PLL_OUT_EN_MASK; + putreg32(regval, pll_base); + + /* 9. Switch CPU clock back to PLL0 */ + + clksel0 = getreg32(K210_SYSCTL_CLKSEL0); + clksel0 |= CLKSEL0_ACLK_SEL_MASK; + putreg32(clksel0, K210_SYSCTL_CLKSEL0); + + return k210_sysctl_pll_get_freq(pll_base); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: k210_sysctl_pll_is_locked + * + * Description: + * Check if a PLL is locked by reading the PLL_LOCK register. + * + * PLL0 requires both overflow and slip bits to be set (value == 3). + * PLL1 and PLL2 only check the lock bit. + * + * Input Parameters: + * pll_offset - PLL register offset (K210_SYSCTL_PLL0/PLL1/PLL2) + * + * Returned Value: + * true if PLL is locked, false otherwise + * + ****************************************************************************/ + +bool k210_sysctl_pll_is_locked(uint32_t pll_offset) +{ + uint32_t regval = getreg32(K210_SYSCTL_PLL_LOCK); + + if (pll_offset == K210_SYSCTL_PLL0) + { + return (regval & (1 << PLL_LOCK_PLL0_SHIFT)) != 0; + } + else if (pll_offset == K210_SYSCTL_PLL1) + { + return (regval & PLL_LOCK_PLL1_MASK) == PLL_LOCK_PLL1_LOCKED; + } + else if (pll_offset == K210_SYSCTL_PLL2) + { + return (regval & PLL_LOCK_PLL2_MASK) == PLL_LOCK_PLL2_LOCKED; + } + + return false; +} + +/**************************************************************************** + * Name: k210_sysctl_init + ****************************************************************************/ + +void k210_sysctl_init(void) +{ + uint32_t clksel0; + + /* Default to ACLK divider /2 */ + + clksel0 = getreg32(K210_SYSCTL_CLKSEL0); + clksel0 &= ~CLKSEL0_ACLK_DIV_MASK; + putreg32(clksel0, K210_SYSCTL_CLKSEL0); + + k210_sysctl_cpu_set_freq(CONFIG_K210_CPU_FREQ); +} + +/**************************************************************************** + * Name: k210_sysctl_cpu_get_freq + ****************************************************************************/ + +uint32_t k210_sysctl_cpu_get_freq(void) +{ + return k210_sysctl_clock_get_freq(K210_CLOCK_CPU); +} + +/**************************************************************************** + * Name: k210_sysctl_cpu_set_freq + ****************************************************************************/ + +uint32_t k210_sysctl_cpu_set_freq(uint32_t freq) +{ + uint32_t actual_pll; + uint32_t actual; + uint32_t clksel0; + uint32_t div_sel; + uint32_t target_pll; + uint64_t target64; + + if (freq == 0) + { + return 0; + } + + clksel0 = getreg32(K210_SYSCTL_CLKSEL0); + div_sel = (clksel0 & CLKSEL0_ACLK_DIV_MASK) >> CLKSEL0_ACLK_DIV_SHIFT; + target64 = (uint64_t)freq * (uint64_t)(2u << div_sel); + if (target64 > UINT32_MAX) + { + return 0; + } + + target_pll = (uint32_t)target64; + actual_pll = k210_sysctl_pll_set_freq(K210_SYSCTL_PLL0, target_pll); + + if (actual_pll == 0) + { + return 0; + } + + actual = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + return actual; +} + +/**************************************************************************** + * Name: k210_sysctl_clock_enable + * + * Description: + * Enable a peripheral or central clock. + * + * For peripheral clocks, this follows a two-stage process: + * 1. Enable the corresponding APB bus clock (CLK_EN_CENT) + * 2. Enable the peripheral clock (CLK_EN_PERI) + * + * For central clocks (CPU, SRAM, APB), only CLK_EN_CENT is used. + * + * Input Parameters: + * clkid - Clock ID to enable + * + * Returned Value: + * OK on success, negative error code on failure + * + ****************************************************************************/ + +int k210_sysctl_clock_enable(k210_clockid_t clkid) +{ + uint32_t regval; + uint32_t bit; + int ret; + + if (clkid >= K210_CLOCK_MAX) + { + return -EINVAL; + } + + if (k210_clock_is_central(clkid)) + { + ret = k210_clock_to_central_bit(clkid, &bit); + if (ret < 0) + { + return ret; + } + + regval = getreg32(K210_SYSCTL_CLK_EN_CENT); + regval |= bit; + putreg32(regval, K210_SYSCTL_CLK_EN_CENT); + } + else + { + ret = k210_sysctl_enable_apb_clock(clkid); + if (ret < 0) + { + return ret; + } + + ret = k210_clock_to_bit(clkid, &bit); + if (ret < 0) + { + return ret; + } + + regval = getreg32(K210_SYSCTL_CLK_EN_PERI); + regval |= (1 << bit); + putreg32(regval, K210_SYSCTL_CLK_EN_PERI); + } + + return OK; +} + +/**************************************************************************** + * Name: k210_sysctl_clock_disable + * + * Description: + * Disable a peripheral or central clock. + * + * For peripheral clocks, only the peripheral clock is disabled. + * The APB bus clock is kept enabled to protect other peripherals + * on the same bus. + * + * For central clocks (CPU, SRAM, APB), CLK_EN_CENT is used. + * WARNING: Disabling central clocks can cause system instability. + * + * Input Parameters: + * clkid - Clock ID to disable + * + * Returned Value: + * OK on success, negative error code on failure + * + ****************************************************************************/ + +int k210_sysctl_clock_disable(k210_clockid_t clkid) +{ + uint32_t regval; + uint32_t bit; + int ret; + + if (clkid >= K210_CLOCK_MAX) + { + return -EINVAL; + } + + if (k210_clock_is_central(clkid)) + { + ret = k210_clock_to_central_bit(clkid, &bit); + if (ret < 0) + { + return ret; + } + + regval = getreg32(K210_SYSCTL_CLK_EN_CENT); + regval &= ~bit; + putreg32(regval, K210_SYSCTL_CLK_EN_CENT); + } + else + { + ret = k210_clock_to_bit(clkid, &bit); + if (ret < 0) + { + return ret; + } + + regval = getreg32(K210_SYSCTL_CLK_EN_PERI); + regval &= ~(1 << bit); + putreg32(regval, K210_SYSCTL_CLK_EN_PERI); + } + + return OK; +} + +/**************************************************************************** + * Name: k210_sysctl_clock_get_freq + * + * Description: + * Get the frequency of a clock. + * + * Clock frequency relationships: + * - PLL0/1/2: Calculated from PLL registers + * - CPU (ACLK): PLL0 / (2^aclk_div) when aclk_sel=1, else IN0 + * - APB0: ACLK / (apb0_div + 1) + * - APB1: ACLK / (apb1_div + 1) + * - APB2: ACLK / (apb2_div + 1) + * - Peripheral clocks: Equal to corresponding APB bus frequency + * + * Input Parameters: + * clkid - Clock ID to query + * + * Returned Value: + * Clock frequency in Hz, 0 for unknown clocks + * + ****************************************************************************/ + +uint32_t k210_sysctl_clock_get_freq(k210_clockid_t clkid) +{ + uint32_t regval; + uint32_t div; + uint32_t source; + + switch (clkid) + { + case K210_CLOCK_PLL0: + return k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + + case K210_CLOCK_PLL1: + return k210_sysctl_pll_get_freq(K210_SYSCTL_PLL1); + + case K210_CLOCK_PLL2: + return k210_sysctl_pll_get_freq(K210_SYSCTL_PLL2); + + case K210_CLOCK_CPU: + regval = getreg32(K210_SYSCTL_CLKSEL0); + if (regval & CLKSEL0_ACLK_SEL_MASK) + { + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + div = (regval >> CLKSEL0_ACLK_DIV_SHIFT) & 0x03; + return source / (2u << div); + } + else + { + return K210_PLL_INPUT_FREQ; + } + + case K210_CLOCK_SRAM0: + source = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + regval = getreg32(K210_SYSCTL_CLK_TH0); + div = (regval >> CLK_TH0_SRAM0_SHIFT) & 0x0f; + return source / (div + 1); + + case K210_CLOCK_SRAM1: + source = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + regval = getreg32(K210_SYSCTL_CLK_TH0); + div = (regval >> CLK_TH0_SRAM1_SHIFT) & 0x0f; + return source / (div + 1); + + case K210_CLOCK_ROM: + source = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + regval = getreg32(K210_SYSCTL_CLK_TH0); + div = (regval >> CLK_TH0_ROM_SHIFT) & 0x0f; + return source / (div + 1); + + case K210_CLOCK_DVP: + source = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + regval = getreg32(K210_SYSCTL_CLK_TH0); + div = (regval >> CLK_TH0_DVP_SHIFT) & 0x0f; + return source / (div + 1); + + case K210_CLOCK_APB0: + source = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + regval = getreg32(K210_SYSCTL_CLKSEL0); + div = (regval >> CLKSEL0_APB0_DIV_SHIFT) & 0x07; + return source / (div + 1); + + case K210_CLOCK_APB1: + source = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + regval = getreg32(K210_SYSCTL_CLKSEL0); + div = (regval >> CLKSEL0_APB1_DIV_SHIFT) & 0x07; + return source / (div + 1); + + case K210_CLOCK_APB2: + source = k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + regval = getreg32(K210_SYSCTL_CLKSEL0); + div = (regval >> CLKSEL0_APB2_DIV_SHIFT) & 0x07; + return source / (div + 1); + + case K210_CLOCK_AI: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL1); + regval = getreg32(K210_SYSCTL_CLK_TH0); + div = (regval >> CLK_TH0_AI_SHIFT) & 0x0f; + return source / (div + 1); + + case K210_CLOCK_DMA: + case K210_CLOCK_FFT: + return k210_sysctl_clock_get_freq(K210_CLOCK_CPU); + + case K210_CLOCK_SPI0: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + regval = getreg32(K210_SYSCTL_CLK_TH1); + div = (regval >> CLK_TH1_SPI0_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_SPI1: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + regval = getreg32(K210_SYSCTL_CLK_TH1); + div = (regval >> CLK_TH1_SPI1_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_SPI2: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + regval = getreg32(K210_SYSCTL_CLK_TH1); + div = (regval >> CLK_TH1_SPI2_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_SPI3: + regval = getreg32(K210_SYSCTL_CLKSEL0); + if ((regval >> 12) & 0x01) + { + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + } + else + { + source = K210_PLL_INPUT_FREQ; + } + + regval = getreg32(K210_SYSCTL_CLK_TH1); + div = (regval >> CLK_TH1_SPI3_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_I2S0: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL2); + regval = getreg32(K210_SYSCTL_CLK_TH3); + div = (regval >> CLK_TH3_I2S0_SHIFT) & 0xffff; + return source / ((div + 1) * 2); + + case K210_CLOCK_I2S1: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL2); + regval = getreg32(K210_SYSCTL_CLK_TH3); + div = (regval >> CLK_TH3_I2S1_SHIFT) & 0xffff; + return source / ((div + 1) * 2); + + case K210_CLOCK_I2S2: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL2); + regval = getreg32(K210_SYSCTL_CLK_TH4); + div = (regval >> CLK_TH4_I2S2_SHIFT) & 0xffff; + return source / ((div + 1) * 2); + + case K210_CLOCK_I2C0: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + regval = getreg32(K210_SYSCTL_CLK_TH5); + div = (regval >> CLK_TH5_I2C0_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_I2C1: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + regval = getreg32(K210_SYSCTL_CLK_TH5); + div = (regval >> CLK_TH5_I2C1_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_I2C2: + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + regval = getreg32(K210_SYSCTL_CLK_TH5); + div = (regval >> CLK_TH5_I2C2_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_TIMER0: + regval = getreg32(K210_SYSCTL_CLKSEL0); + if ((regval >> 13) & 0x01) + { + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + } + else + { + source = K210_PLL_INPUT_FREQ; + } + + regval = getreg32(K210_SYSCTL_CLK_TH2); + div = (regval >> CLK_TH2_TIMER0_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_TIMER1: + regval = getreg32(K210_SYSCTL_CLKSEL0); + if ((regval >> 14) & 0x01) + { + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + } + else + { + source = K210_PLL_INPUT_FREQ; + } + + regval = getreg32(K210_SYSCTL_CLK_TH2); + div = (regval >> CLK_TH2_TIMER1_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_TIMER2: + regval = getreg32(K210_SYSCTL_CLKSEL0); + if ((regval >> 15) & 0x01) + { + source = k210_sysctl_pll_get_freq(K210_SYSCTL_PLL0); + } + else + { + source = K210_PLL_INPUT_FREQ; + } + + regval = getreg32(K210_SYSCTL_CLK_TH2); + div = (regval >> CLK_TH2_TIMER2_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_WDT0: + source = K210_PLL_INPUT_FREQ; + regval = getreg32(K210_SYSCTL_CLK_TH6); + div = (regval >> CLK_TH6_WDT0_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_WDT1: + source = K210_PLL_INPUT_FREQ; + regval = getreg32(K210_SYSCTL_CLK_TH6); + div = (regval >> CLK_TH6_WDT1_SHIFT) & 0xff; + return source / ((div + 1) * 2); + + case K210_CLOCK_GPIO: + case K210_CLOCK_UART1: + case K210_CLOCK_UART2: + case K210_CLOCK_UART3: + case K210_CLOCK_FPIOA: + case K210_CLOCK_SHA: + return k210_sysctl_clock_get_freq(K210_CLOCK_APB0); + + case K210_CLOCK_AES: + case K210_CLOCK_OTP: + return k210_sysctl_clock_get_freq(K210_CLOCK_APB1); + + case K210_CLOCK_RTC: + return K210_PLL_INPUT_FREQ; + + default: + return 0; + } +} + +/**************************************************************************** + * Name: k210_sysctl_reset + * + * Description: + * Reset a peripheral with proper timing sequence. + * + * Reset sequence: + * 1. Assert reset (set bit in PERI_RESET register) + * 2. Wait approximately 10us for reset to take effect + * 3. Deassert reset (clear bit in PERI_RESET register) + * + * Input Parameters: + * rstidx - Reset ID of the peripheral to reset + * + * Returned Value: + * OK on success, negative error code on failure + * + ****************************************************************************/ + +int k210_sysctl_reset(k210_rstidx_t rstidx) +{ + uint32_t bit; + uint32_t regval; + int ret; + + ret = k210_reset_to_bit(rstidx, &bit); + if (ret < 0) + { + return ret; + } + + regval = getreg32(K210_SYSCTL_PERI_RESET); + + regval |= (1 << bit); + putreg32(regval, K210_SYSCTL_PERI_RESET); + + up_udelay(10); + + regval &= ~(1 << bit); + putreg32(regval, K210_SYSCTL_PERI_RESET); + + return OK; +} + +/**************************************************************************** + * Name: k210_sysctl_init_peripheral + * + * Description: + * Initialize a peripheral by enabling its clock and releasing from reset. + * + * This is a convenience function that combines clock enable and reset + * operations for simplified peripheral initialization. + * + * Typical usage: + * k210_sysctl_init_peripheral(K210_CLOCK_UART1, K210_RESET_UART1); + * k210_sysctl_init_peripheral(K210_CLOCK_SPI0, K210_RESET_SPI0); + * + * Input Parameters: + * clkid - Clock ID of the peripheral + * rstidx - Reset ID of the peripheral + * + * Returned Value: + * OK on success, negative error code on failure + * + ****************************************************************************/ + +int k210_sysctl_init_peripheral(k210_clockid_t clkid, k210_rstidx_t rstidx) +{ + int ret; + + ret = k210_sysctl_clock_enable(clkid); + if (ret < 0) + { + return ret; + } + + ret = k210_sysctl_reset(rstidx); + if (ret < 0) + { + k210_sysctl_clock_disable(clkid); + return ret; + } + + return OK; +} diff --git a/arch/risc-v/src/k210/k210_sysctl.h b/arch/risc-v/src/k210/k210_sysctl.h new file mode 100644 index 00000000000..ad02ac13247 --- /dev/null +++ b/arch/risc-v/src/k210/k210_sysctl.h @@ -0,0 +1,239 @@ +/**************************************************************************** + * arch/risc-v/src/k210/k210_sysctl.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_K210_K210_SYSCTL_H +#define __ARCH_RISCV_SRC_K210_K210_SYSCTL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * @brief K210 peripheral clock ID enumeration + * + * This enumeration defines all clock IDs for K210 peripherals. + * These IDs are used to enable/disable clocks and get clock frequencies. + */ + +typedef enum +{ + K210_CLOCK_PLL0 = 0, /* PLL0 clock */ + K210_CLOCK_PLL1, /* PLL1 clock */ + K210_CLOCK_PLL2, /* PLL2 clock */ + K210_CLOCK_CPU, /* CPU clock */ + K210_CLOCK_SRAM0, /* SRAM0 clock */ + K210_CLOCK_SRAM1, /* SRAM1 clock */ + K210_CLOCK_APB0, /* APB0 bus clock */ + K210_CLOCK_APB1, /* APB1 bus clock */ + K210_CLOCK_APB2, /* APB2 bus clock */ + K210_CLOCK_ROM, /* ROM clock */ + K210_CLOCK_DMA, /* DMA clock */ + K210_CLOCK_AI, /* AI accelerator clock */ + K210_CLOCK_DVP, /* DVP camera interface clock */ + K210_CLOCK_FFT, /* FFT accelerator clock */ + K210_CLOCK_GPIO, /* GPIO clock */ + K210_CLOCK_SPI0, /* SPI0 clock */ + K210_CLOCK_SPI1, /* SPI1 clock */ + K210_CLOCK_SPI2, /* SPI2 clock */ + K210_CLOCK_SPI3, /* SPI3 clock */ + K210_CLOCK_I2S0, /* I2S0 clock */ + K210_CLOCK_I2S1, /* I2S1 clock */ + K210_CLOCK_I2S2, /* I2S2 clock */ + K210_CLOCK_I2C0, /* I2C0 clock */ + K210_CLOCK_I2C1, /* I2C1 clock */ + K210_CLOCK_I2C2, /* I2C2 clock */ + K210_CLOCK_UART1, /* UART1 clock */ + K210_CLOCK_UART2, /* UART2 clock */ + K210_CLOCK_UART3, /* UART3 clock */ + K210_CLOCK_AES, /* AES accelerator clock */ + K210_CLOCK_FPIOA, /* FPIOA (GPIO mux) clock */ + K210_CLOCK_TIMER0, /* TIMER0 clock */ + K210_CLOCK_TIMER1, /* TIMER1 clock */ + K210_CLOCK_TIMER2, /* TIMER2 clock */ + K210_CLOCK_WDT0, /* Watchdog timer 0 clock */ + K210_CLOCK_WDT1, /* Watchdog timer 1 clock */ + K210_CLOCK_SHA, /* SHA accelerator clock */ + K210_CLOCK_OTP, /* OTP (one-time programmable) clock */ + K210_CLOCK_RTC, /* RTC clock */ + K210_CLOCK_MAX /* Boundary check value */ +} k210_clockid_t; + +/** + * @brief K210 peripheral reset ID enumeration + * + * This enumeration defines all reset IDs for K210 peripherals. + * These IDs are used to reset individual peripherals. + */ + +typedef enum +{ + K210_RESET_SOC = 0, /* SOC reset */ + K210_RESET_ROM, /* ROM reset */ + K210_RESET_DMA, /* DMA reset */ + K210_RESET_AI, /* AI accelerator reset */ + K210_RESET_DVP, /* DVP camera interface reset */ + K210_RESET_FFT, /* FFT accelerator reset */ + K210_RESET_GPIO, /* GPIO reset */ + K210_RESET_SPI0, /* SPI0 reset */ + K210_RESET_SPI1, /* SPI1 reset */ + K210_RESET_SPI2, /* SPI2 reset */ + K210_RESET_SPI3, /* SPI3 reset */ + K210_RESET_I2S0, /* I2S0 reset */ + K210_RESET_I2S1, /* I2S1 reset */ + K210_RESET_I2S2, /* I2S2 reset */ + K210_RESET_I2C0, /* I2C0 reset */ + K210_RESET_I2C1, /* I2C1 reset */ + K210_RESET_I2C2, /* I2C2 reset */ + K210_RESET_UART1, /* UART1 reset */ + K210_RESET_UART2, /* UART2 reset */ + K210_RESET_UART3, /* UART3 reset */ + K210_RESET_AES, /* AES accelerator reset */ + K210_RESET_FPIOA, /* FPIOA reset */ + K210_RESET_TIMER0, /* TIMER0 reset */ + K210_RESET_TIMER1, /* TIMER1 reset */ + K210_RESET_TIMER2, /* TIMER2 reset */ + K210_RESET_WDT0, /* Watchdog timer 0 reset */ + K210_RESET_WDT1, /* Watchdog timer 1 reset */ + K210_RESET_SHA, /* SHA accelerator reset */ + K210_RESET_RTC, /* RTC reset */ + K210_RESET_MAX /* Boundary check value */ +} k210_rstidx_t; + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/** + * @brief Initialize the system controller + * + * This function initializes the K210 system controller, including + * PLL configuration and clock tree setup. + */ + +EXTERN void k210_sysctl_init(void); + +/** + * @brief Enable a peripheral clock + * + * @param clkid Clock ID of the peripheral + * @return 0 on success, negative error code on failure + */ + +EXTERN int k210_sysctl_clock_enable(k210_clockid_t clkid); + +/** + * @brief Disable a peripheral clock + * + * @param clkid Clock ID of the peripheral + * @return 0 on success, negative error code on failure + */ + +EXTERN int k210_sysctl_clock_disable(k210_clockid_t clkid); + +/** + * @brief Get the frequency of a peripheral clock + * + * @param clkid Clock ID of the peripheral + * @return Clock frequency in Hz, 0 on error + */ + +EXTERN uint32_t k210_sysctl_clock_get_freq(k210_clockid_t clkid); + +/** + * @brief Get current CPU clock frequency + * + * @return CPU clock frequency in Hz + */ + +EXTERN uint32_t k210_sysctl_cpu_get_freq(void); + +/** + * @brief Set CPU clock frequency (SDK-like) + * + * This function adjusts PLL0 and keeps current ACLK divider setting. + * The final frequency may be near the target due to integer PLL factors. + * + * @param freq Target CPU frequency in Hz + * @return Actual CPU clock frequency in Hz, 0 on failure + */ + +EXTERN uint32_t k210_sysctl_cpu_set_freq(uint32_t freq); + +/** + * @brief Reset a peripheral + * + * @param rstidx Reset ID of the peripheral + * @return 0 on success, negative error code on failure + */ + +EXTERN int k210_sysctl_reset(k210_rstidx_t rstidx); + +/** + * @brief Initialize a peripheral (enable clock and deassert reset) + * + * This is a convenience function that enables the clock and releases + * the peripheral from reset state. + * + * @param clkid Clock ID of the peripheral + * @param rstidx Reset ID of the peripheral + * @return 0 on success, negative error code on failure + */ + +EXTERN int k210_sysctl_init_peripheral(k210_clockid_t clkid, + k210_rstidx_t rstidx); + +/** + * @brief Check if a PLL is locked + * + * @param pll_offset PLL register offset (K210_SYSCTL_PLL0/PLL1/PLL2) + * @return true if PLL is locked, false otherwise + */ + +EXTERN bool k210_sysctl_pll_is_locked(uint32_t pll_offset); + +#if defined(__cplusplus) +} +#endif +#undef EXTERN + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_RISCV_SRC_K210_K210_SYSCTL_H */