From 399cd88e7fc65b0aa9355b352ad6683949db76ba Mon Sep 17 00:00:00 2001 From: Adam Comley Date: Sat, 3 Feb 2024 10:10:31 -0600 Subject: [PATCH] Support RP2040 Clock Outputs --- Documentation/platforms/arm/rp2040/index.rst | 1 + arch/arm/src/rp2040/Kconfig | 212 ++++++++++++++++++ arch/arm/src/rp2040/rp2040_clock.c | 109 +++++++++ arch/arm/src/rp2040/rp2040_gpio.h | 15 +- .../common/src/rp2040_common_initialize.c | 17 ++ 5 files changed, 350 insertions(+), 4 deletions(-) diff --git a/Documentation/platforms/arm/rp2040/index.rst b/Documentation/platforms/arm/rp2040/index.rst index f24d245c58..4ff293b480 100644 --- a/Documentation/platforms/arm/rp2040/index.rst +++ b/Documentation/platforms/arm/rp2040/index.rst @@ -23,6 +23,7 @@ USB PIO RP2040 Programmable I/O IRQs DMA +Clock Output ws2812 Smart pixels (e.g. Neopixel) Flash ROM Boot SRAM Boot If Pico SDK is available a nuttx.uf2 file will be created diff --git a/arch/arm/src/rp2040/Kconfig b/arch/arm/src/rp2040/Kconfig index 751f0c806c..a03ac05029 100644 --- a/arch/arm/src/rp2040/Kconfig +++ b/arch/arm/src/rp2040/Kconfig @@ -663,6 +663,218 @@ endif # ADC endif # RP2040_ADC +##################################################################### +# CLK_GPOUT Configuration +##################################################################### + +menuconfig RP2040_CLK_GPOUT_ENABLE + bool "Enable Clock Outputs" + default n + +if RP2040_CLK_GPOUT_ENABLE + +config RP2040_CLK_GPOUT0 + bool "CLK_GPOUT0 Clock Output (pin 21)" + default n + ---help--- + Enable CLK_GPOUT0 + +if RP2040_CLK_GPOUT0 + +choice + prompt "Source Clock" + + config RP2040_CLK_GPOUT0_SRC_REF + bool "REF (6 - 12 MHz)" + ---help--- + Reference clock that is always running unless in DORMANT mode. Runs from + Ring Oscillator (ROSC) at power-up but can be switched to Crystal + Oscillator (XOSC) for more accuracy. + + config RP2040_CLK_GPOUT0_SRC_SYS + bool "SYS (125 MHz)" + ---help--- + System clock that is always running unless in DORMANT mode. Runs from + clk_ref at power-up but is typically switched to a PLL. + + config RP2040_CLK_GPOUT0_SRC_USB + bool "USB (48 MHz)" + ---help--- + USB reference clock. Must be 48MHz. + + config RP2040_CLK_GPOUT0_SRC_ADC + bool "ADC (48 MHz)" + ---help--- + ADC reference clock. Must be 48MHz. + + config RP2040_CLK_GPOUT0_SRC_RTC + bool "RTC (46875 Hz)" + ---help--- + RTC reference clock. The RTC divides this clock to generate a 1 second reference. +endchoice + +config RP2040_CLK_GPOUT0_DIVINT + int "Divisor (Integer)" + default 1 + +config RP2040_CLK_GPOUT0_DIVFRAC + int "Divisor (Fractional)" + default 0 + +endif # RP2040_CLK_GPOUT0 + +config RP2040_CLK_GPOUT1 + bool "CLK_GPOUT1 Clock Output (pin 23)" + default n + ---help--- + Enable CLK_GPOUT1 + +if RP2040_CLK_GPOUT1 + +choice + prompt "Source Clock" + + config RP2040_CLK_GPOUT1_SRC_REF + bool "REF (6 - 12 MHz)" + ---help--- + Reference clock that is always running unless in DORMANT mode. Runs from + Ring Oscillator (ROSC) at power-up but can be switched to Crystal + Oscillator (XOSC) for more accuracy. + + config RP2040_CLK_GPOUT1_SRC_SYS + bool "SYS (125 MHz)" + ---help--- + System clock that is always running unless in DORMANT mode. Runs from + clk_ref at power-up but is typically switched to a PLL. + + config RP2040_CLK_GPOUT1_SRC_USB + bool "USB (48 MHz)" + ---help--- + USB reference clock. Must be 48MHz. + + config RP2040_CLK_GPOUT1_SRC_ADC + bool "ADC (48 MHz)" + ---help--- + ADC reference clock. Must be 48MHz. + + config RP2040_CLK_GPOUT1_SRC_RTC + bool "RTC (46875 Hz)" + ---help--- + RTC reference clock. The RTC divides this clock to generate a 1 second reference. +endchoice + +config RP2040_CLK_GPOUT1_DIVINT + int "Divisor (Integer)" + default 1 + +config RP2040_CLK_GPOUT1_DIVFRAC + int "Divisor (Fractional)" + default 0 + +endif # RP2040_CLK_GPOUT1 + +config RP2040_CLK_GPOUT2 + bool "CLK_GPOUT2 Clock Output (pin 24)" + default n + ---help--- + Enable CLK_GPOUT2 + +if RP2040_CLK_GPOUT2 + +choice + prompt "Source Clock" + + config RP2040_CLK_GPOUT2_SRC_REF + bool "REF (6 - 12 MHz)" + ---help--- + Reference clock that is always running unless in DORMANT mode. Runs from + Ring Oscillator (ROSC) at power-up but can be switched to Crystal + Oscillator (XOSC) for more accuracy. + + config RP2040_CLK_GPOUT2_SRC_SYS + bool "SYS (125 MHz)" + ---help--- + System clock that is always running unless in DORMANT mode. Runs from + clk_ref at power-up but is typically switched to a PLL. + + config RP2040_CLK_GPOUT2_SRC_USB + bool "USB (48 MHz)" + ---help--- + USB reference clock. Must be 48MHz. + + config RP2040_CLK_GPOUT2_SRC_ADC + bool "ADC (48 MHz)" + ---help--- + ADC reference clock. Must be 48MHz. + + config RP2040_CLK_GPOUT2_SRC_RTC + bool "RTC (46875 Hz)" + ---help--- + RTC reference clock. The RTC divides this clock to generate a 1 second reference. +endchoice + +config RP2040_CLK_GPOUT2_DIVINT + int "Divisor (Integer)" + default 1 + +config RP2040_CLK_GPOUT2_DIVFRAC + int "Divisor (Fractional)" + default 0 + +endif # RP2040_CLK_GPOUT2 + +config RP2040_CLK_GPOUT3 + bool "CLK_GPOUT3 Clock Output (pin 25)" + default n + ---help--- + Enable CLK_GPOUT3 + +if RP2040_CLK_GPOUT3 + +choice + prompt "Source Clock" + + config RP2040_CLK_GPOUT3_SRC_REF + bool "REF (6 - 12 MHz)" + ---help--- + Reference clock that is always running unless in DORMANT mode. Runs from + Ring Oscillator (ROSC) at power-up but can be switched to Crystal + Oscillator (XOSC) for more accuracy. + + config RP2040_CLK_GPOUT3_SRC_SYS + bool "SYS (125 MHz)" + ---help--- + System clock that is always running unless in DORMANT mode. Runs from + clk_ref at power-up but is typically switched to a PLL. + + config RP2040_CLK_GPOUT3_SRC_USB + bool "USB (48 MHz)" + ---help--- + USB reference clock. Must be 48MHz. + + config RP2040_CLK_GPOUT3_SRC_ADC + bool "ADC (48 MHz)" + ---help--- + ADC reference clock. Must be 48MHz. + + config RP2040_CLK_GPOUT3_SRC_RTC + bool "RTC (46875 Hz)" + ---help--- + RTC reference clock. The RTC divides this clock to generate a 1 second reference. +endchoice + +config RP2040_CLK_GPOUT3_DIVINT + int "Divisor (Integer)" + default 1 + +config RP2040_CLK_GPOUT3_DIVFRAC + int "Divisor (Fractional)" + default 0 + +endif # RP2040_CLK_GPOUT3 + +endif # RP2040_CLK_GPOUT_ENABLE + ##################################################################### # WS2812 Configuration ##################################################################### diff --git a/arch/arm/src/rp2040/rp2040_clock.c b/arch/arm/src/rp2040/rp2040_clock.c index e874ac543e..5c8cf20553 100644 --- a/arch/arm/src/rp2040/rp2040_clock.c +++ b/arch/arm/src/rp2040/rp2040_clock.c @@ -81,6 +81,30 @@ static inline bool has_glitchless_mux(int clk_index) clk_index == RP2040_CLOCKS_NDX_REF; } +#if defined(CONFIG_RP2040_CLK_GPOUT_ENABLE) +static bool rp2040_clock_configure_gpout(int clk_index, + uint32_t src, + uint32_t div_int, + uint32_t div_frac) +{ + if (clk_index > RP2040_CLOCKS_NDX_GPOUT3 || + clk_index < RP2040_CLOCKS_NDX_GPOUT0 || + (src >> RP2040_CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_SHIFT) > 0xa) + { + return false; + } + + putreg32((div_int << RP2040_CLOCKS_CLK_GPOUT0_DIV_INT_SHIFT) | + (div_frac & RP2040_CLOCKS_CLK_GPOUT0_DIV_FRAC_MASK), + (RP2040_CLOCKS_CLK_NDX_DIV(clk_index))); + putreg32((src << RP2040_CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_SHIFT) | + RP2040_CLOCKS_CLK_GPOUT0_CTRL_ENABLE, + (RP2040_CLOCKS_CLK_NDX_CTRL(clk_index))); + + return true; +} +#endif + bool rp2040_clock_configure(int clk_index, uint32_t src, uint32_t auxsrc, uint32_t src_freq, uint32_t freq) @@ -274,6 +298,91 @@ void clocks_init(void) RP2040_CLOCKS_CLK_PERI_CTRL_AUXSRC_CLK_SYS, BOARD_SYS_FREQ, BOARD_PERI_FREQ); + +#if defined(CONFIG_RP2040_CLK_GPOUT_ENABLE) + uint32_t src; + + #if defined(CONFIG_RP2040_CLK_GPOUT0) + #if defined(CONFIG_RP2040_CLK_GPOUT0_SRC_REF) + src = RP2040_CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_CLK_REF; + #elif defined(CONFIG_RP2040_CLK_GPOUT0_SRC_SYS) + src = RP2040_CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_CLK_SYS; + #elif defined(CONFIG_RP2040_CLK_GPOUT0_SRC_USB) + src = RP2040_CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_CLK_USB; + #elif defined(CONFIG_RP2040_CLK_GPOUT0_SRC_ADC) + src = RP2040_CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_CLK_ADC; + #elif defined(CONFIG_RP2040_CLK_GPOUT0_SRC_RTC) + src = RP2040_CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_CLK_RTC; + #else + src = 0; + #endif + rp2040_clock_configure_gpout(RP2040_CLOCKS_NDX_GPOUT0, + src, + CONFIG_RP2040_CLK_GPOUT0_DIVINT, + CONFIG_RP2040_CLK_GPOUT0_DIVFRAC); + #endif + + #if defined(CONFIG_RP2040_CLK_GPOUT1) + #if defined(CONFIG_RP2040_CLK_GPOUT1_SRC_REF) + src = RP2040_CLOCKS_CLK_GPOUT1_CTRL_AUXSRC_CLK_REF; + #elif defined(CONFIG_RP2040_CLK_GPOUT1_SRC_SYS) + src = RP2040_CLOCKS_CLK_GPOUT1_CTRL_AUXSRC_CLK_SYS; + #elif defined(CONFIG_RP2040_CLK_GPOUT1_SRC_USB) + src = RP2040_CLOCKS_CLK_GPOUT1_CTRL_AUXSRC_CLK_USB; + #elif defined(CONFIG_RP2040_CLK_GPOUT1_SRC_ADC) + src = RP2040_CLOCKS_CLK_GPOUT1_CTRL_AUXSRC_CLK_ADC; + #elif defined(CONFIG_RP2040_CLK_GPOUT1_SRC_RTC) + src = RP2040_CLOCKS_CLK_GPOUT1_CTRL_AUXSRC_CLK_RTC; + #else + src = 0; + #endif + rp2040_clock_configure_gpout(RP2040_CLOCKS_NDX_GPOUT1, + src, + CONFIG_RP2040_CLK_GPOUT1_DIVINT, + CONFIG_RP2040_CLK_GPOUT1_DIVFRAC); + #endif + + #if defined(CONFIG_RP2040_CLK_GPOUT2) + #if defined(CONFIG_RP2040_CLK_GPOUT2_SRC_REF) + src = RP2040_CLOCKS_CLK_GPOUT2_CTRL_AUXSRC_CLK_REF; + #elif defined(CONFIG_RP2040_CLK_GPOUT2_SRC_SYS) + src = RP2040_CLOCKS_CLK_GPOUT2_CTRL_AUXSRC_CLK_SYS; + #elif defined(CONFIG_RP2040_CLK_GPOUT2_SRC_USB) + src = RP2040_CLOCKS_CLK_GPOUT2_CTRL_AUXSRC_CLK_USB; + #elif defined(CONFIG_RP2040_CLK_GPOUT2_SRC_ADC) + src = RP2040_CLOCKS_CLK_GPOUT2_CTRL_AUXSRC_CLK_ADC; + #elif defined(CONFIG_RP2040_CLK_GPOUT2_SRC_RTC) + src = RP2040_CLOCKS_CLK_GPOUT2_CTRL_AUXSRC_CLK_RTC; + #else + src = 0; + #endif + rp2040_clock_configure_gpout(RP2040_CLOCKS_NDX_GPOUT2, + src, + CONFIG_RP2040_CLK_GPOUT2_DIVINT, + CONFIG_RP2040_CLK_GPOUT2_DIVFRAC); + #endif + + #if defined(CONFIG_RP2040_CLK_GPOUT3) + #if defined(CONFIG_RP2040_CLK_GPOUT3_SRC_REF) + src = RP2040_CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_CLK_REF; + #elif defined(CONFIG_RP2040_CLK_GPOUT3_SRC_SYS) + src = RP2040_CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_CLK_SYS; + #elif defined(CONFIG_RP2040_CLK_GPOUT3_SRC_USB) + src = RP2040_CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_CLK_USB; + #elif defined(CONFIG_RP2040_CLK_GPOUT3_SRC_ADC) + src = RP2040_CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_CLK_ADC; + #elif defined(CONFIG_RP2040_CLK_GPOUT3_SRC_RTC) + src = RP2040_CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_CLK_RTC; + #else + src = 0; + #endif + rp2040_clock_configure_gpout(RP2040_CLOCKS_NDX_GPOUT3, + src, + CONFIG_RP2040_CLK_GPOUT3_DIVINT, + CONFIG_RP2040_CLK_GPOUT3_DIVFRAC); + #endif + +#endif } /**************************************************************************** diff --git a/arch/arm/src/rp2040/rp2040_gpio.h b/arch/arm/src/rp2040/rp2040_gpio.h index 7d6aa14898..f68eea0252 100644 --- a/arch/arm/src/rp2040/rp2040_gpio.h +++ b/arch/arm/src/rp2040/rp2040_gpio.h @@ -55,12 +55,19 @@ #define RP2040_GPIO_FUNC_USB RP2040_IO_BANK0_GPIO_CTRL_FUNCSEL_USB #define RP2040_GPIO_FUNC_NULL RP2040_IO_BANK0_GPIO_CTRL_FUNCSEL_NULL +/* GPIO function pins *******************************************************/ + +#define RP2040_GPIO_PIN_CLK_GPOUT0 (21) +#define RP2040_GPIO_PIN_CLK_GPOUT1 (23) +#define RP2040_GPIO_PIN_CLK_GPOUT2 (24) +#define RP2040_GPIO_PIN_CLK_GPOUT3 (25) + /* GPIO interrupt modes *****************************************************/ -#define RP2040_GPIO_INTR_LEVEL_LOW 0 -#define RP2040_GPIO_INTR_LEVEL_HIGH 1 -#define RP2040_GPIO_INTR_EDGE_LOW 2 -#define RP2040_GPIO_INTR_EDGE_HIGH 3 +#define RP2040_GPIO_INTR_LEVEL_LOW 0 +#define RP2040_GPIO_INTR_LEVEL_HIGH 1 +#define RP2040_GPIO_INTR_EDGE_LOW 2 +#define RP2040_GPIO_INTR_EDGE_HIGH 3 /**************************************************************************** * Public Types diff --git a/boards/arm/rp2040/common/src/rp2040_common_initialize.c b/boards/arm/rp2040/common/src/rp2040_common_initialize.c index 6e86c52986..4a9058193f 100644 --- a/boards/arm/rp2040/common/src/rp2040_common_initialize.c +++ b/boards/arm/rp2040/common/src/rp2040_common_initialize.c @@ -94,6 +94,23 @@ void rp2040_common_earlyinitialize(void) RP2040_GPIO_FUNC_UART); /* RTS */ #endif #endif + +#if defined(CONFIG_RP2040_CLK_GPOUT0) + rp2040_gpio_set_function(RP2040_GPIO_PIN_CLK_GPOUT0, + RP2040_GPIO_FUNC_CLOCKS); +#endif +#if defined(CONFIG_RP2040_CLK_GPOUT1) + rp2040_gpio_set_function(RP2040_GPIO_PIN_CLK_GPOUT1, + RP2040_GPIO_FUNC_CLOCKS); +#endif +#if defined(CONFIG_RP2040_CLK_GPOUT2) + rp2040_gpio_set_function(RP2040_GPIO_PIN_CLK_GPOUT2, + RP2040_GPIO_FUNC_CLOCKS); +#endif +#if defined(CONFIG_RP2040_CLK_GPOUT3) + rp2040_gpio_set_function(RP2040_GPIO_PIN_CLK_GPOUT3, + RP2040_GPIO_FUNC_CLOCKS); +#endif } /****************************************************************************