diff --git a/drivers/can/can_nrf.c b/drivers/can/can_nrf.c index c538291ecb4..f8c037835a8 100644 --- a/drivers/can/can_nrf.c +++ b/drivers/can/can_nrf.c @@ -171,7 +171,7 @@ static int can_nrf_init(const struct device *dev) return ret; } - ret = clock_control_on(config->auxpll, NULL); + ret = nrf_clock_control_request_sync(config->auxpll, NULL, K_FOREVER); if (ret < 0) { return ret; } diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index b1dbe191197..0bf5e0075eb 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -54,6 +54,8 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_FLL16M clock_cont zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF54H_HFXO clock_control_nrf54h_hfxo.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_HSFLL_LOCAL clock_control_nrf_hsfll_local.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_LFCLK clock_control_nrf_lfclk.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_AUXPLL clock_control_nrf_auxpll.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL60X clock_control_bl60x.c) if(CONFIG_CLOCK_CONTROL_RENESAS_RZA2M_CPG) zephyr_library_sources(clock_control_renesas_rza2m_cpg.c) @@ -115,5 +117,4 @@ endif() zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_AST10X0 clock_control_ast10x0.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MAX32 clock_control_max32.c) -zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_AUXPLL clock_control_nrf_auxpll.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_WCH_RCC clock_control_wch_rcc.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index f528aa2dad3..05a2a7fb8fa 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -32,6 +32,8 @@ source "drivers/clock_control/Kconfig.stm32" source "drivers/clock_control/Kconfig.beetle" +source "drivers/clock_control/Kconfig.bflb" + source "drivers/clock_control/Kconfig.fixed" source "drivers/clock_control/Kconfig.lpc11u6x" @@ -102,8 +104,6 @@ source "drivers/clock_control/Kconfig.pwm" source "drivers/clock_control/Kconfig.rpi_pico" -source "drivers/clock_control/Kconfig.nrf_auxpll" - source "drivers/clock_control/Kconfig.arm_scmi" source "drivers/clock_control/Kconfig.silabs" diff --git a/drivers/clock_control/Kconfig.bflb b/drivers/clock_control/Kconfig.bflb new file mode 100644 index 00000000000..dd89dbdf4f8 --- /dev/null +++ b/drivers/clock_control/Kconfig.bflb @@ -0,0 +1,7 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_BOUFFALOLAB_BL60X + bool "Bouffalolab BL60x Clock Control" + default y + depends on DT_HAS_BFLB_BL60X_CLOCK_CONTROLLER_ENABLED diff --git a/drivers/clock_control/Kconfig.nrf b/drivers/clock_control/Kconfig.nrf index bbd4a86f5a9..a7c1be10a12 100644 --- a/drivers/clock_control/Kconfig.nrf +++ b/drivers/clock_control/Kconfig.nrf @@ -297,3 +297,9 @@ config CLOCK_CONTROL_NRF_LFCLK_CLOCK_TIMEOUT_MS default 1000 endif # CLOCK_CONTROL_NRF_LFCLK + +config CLOCK_CONTROL_NRF_AUXPLL + bool "nRF Auxiliary PLL driver" + default y + depends on DT_HAS_NORDIC_NRF_AUXPLL_ENABLED + select CLOCK_CONTROL_NRF2_COMMON diff --git a/drivers/clock_control/Kconfig.nrf_auxpll b/drivers/clock_control/Kconfig.nrf_auxpll deleted file mode 100644 index 413452c1ac4..00000000000 --- a/drivers/clock_control/Kconfig.nrf_auxpll +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2024 Nordic Semiconductor ASA -# SPDX-License-Identifier: Apache-2.0 - -config CLOCK_CONTROL_NRF_AUXPLL - bool "nRF Auxiliary PLL driver" - default y - depends on DT_HAS_NORDIC_NRF_AUXPLL_ENABLED - help - Driver for nRF Auxiliary PLL. diff --git a/drivers/clock_control/clock_control_bl60x.c b/drivers/clock_control/clock_control_bl60x.c new file mode 100644 index 00000000000..eb81b04eec8 --- /dev/null +++ b/drivers/clock_control/clock_control_bl60x.c @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2025 MASSDRIVER EI (massdriver.space) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT bflb_bl60x_clock_controller + +#include +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(clock_control_bl60x, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include + +#define CLK_SRC_IS(clk, src) \ + DT_SAME_NODE(DT_CLOCKS_CTLR_BY_IDX(DT_INST_CLOCKS_CTLR_BY_NAME(0, clk), 0), \ + DT_INST_CLOCKS_CTLR_BY_NAME(0, src)) + +#define CLOCK_TIMEOUT 1024 +#define EFUSE_RC32M_TRIM_OFFSET 0x0C +#define EFUSE_RC32M_TRIM_EN_POS 19 +#define EFUSE_RC32M_TRIM_PARITY_POS 18 +#define EFUSE_RC32M_TRIM_POS 10 +#define EFUSE_RC32M_TRIM_MSK 0x3FC00 + +#define CRYSTAL_ID_FREQ_32000000 0 +#define CRYSTAL_ID_FREQ_24000000 1 +#define CRYSTAL_ID_FREQ_38400000 2 +#define CRYSTAL_ID_FREQ_40000000 3 +#define CRYSTAL_ID_FREQ_26000000 4 + +#define CRYSTAL_FREQ_TO_ID(freq) CONCAT(CRYSTAL_ID_FREQ_, freq) + +enum bl60x_clkid { + bl60x_clkid_clk_root = BL60X_CLKID_CLK_ROOT, + bl60x_clkid_clk_rc32m = BL60X_CLKID_CLK_RC32M, + bl60x_clkid_clk_crystal = BL60X_CLKID_CLK_CRYSTAL, + bl60x_clkid_clk_pll = BL60X_CLKID_CLK_PLL, + bl60x_clkid_clk_bclk = BL60X_CLKID_CLK_BCLK, +}; + +struct clock_control_bl60x_pll_config { + enum bl60x_clkid source; + bool overclock; +}; + +struct clock_control_bl60x_root_config { + enum bl60x_clkid source; + uint8_t pll_select; + uint8_t divider; +}; + +struct clock_control_bl60x_bclk_config { + uint8_t divider; +}; + +struct clock_control_bl60x_config { + uint32_t crystal_id; +}; + +struct clock_control_bl60x_data { + bool crystal_enabled; + bool pll_enabled; + struct clock_control_bl60x_pll_config pll; + struct clock_control_bl60x_root_config root; + struct clock_control_bl60x_bclk_config bclk; +}; + +const static uint32_t clock_control_bl60x_crystal_SDMIN_table[5] = { + /* 32M */ + 0x3C0000, + /* 24M */ + 0x500000, + /* 38.4M */ + 0x320000, + /* 40M */ + 0x300000, + /* 26M */ + 0x49D39D, +}; + +static inline void clock_control_bl60x_clock_settle(void) +{ + __asm__ volatile(".rept 15 ; nop ; .endr"); +} + +/* 32 Mhz Oscillator: 0 + * crystal: 1 + * PLL and 32M: 2 + * PLL and crystal: 3 + */ +static void clock_control_bl60x_set_root_clock(uint32_t clock) +{ + uint32_t tmp; + + /* invalid value, fallback to internal 32M */ + if (clock > 3) { + clock = 0; + } + tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET); + tmp = (tmp & HBN_ROOT_CLK_SEL_UMSK) | (clock << HBN_ROOT_CLK_SEL_POS); + sys_write32(tmp, HBN_BASE + HBN_GLB_OFFSET); + + clock_control_bl60x_clock_settle(); +} + +static uint32_t clock_control_bl60x_get_root_clock(void) +{ + uint32_t tmp; + + tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET); + return (((tmp & HBN_ROOT_CLK_SEL_MSK) >> HBN_ROOT_CLK_SEL_POS) & 0x3); +} + +static int clock_control_bl60x_deinit_crystal(void) +{ + uint32_t tmp; + + /* unpower crystal */ + tmp = sys_read32(AON_BASE + AON_RF_TOP_AON_OFFSET); + tmp = tmp & AON_PU_XTAL_AON_UMSK; + tmp = tmp & AON_PU_XTAL_BUF_AON_UMSK; + sys_write32(tmp, AON_BASE + AON_RF_TOP_AON_OFFSET); + + clock_control_bl60x_clock_settle(); + return 0; +} + +static int clock_control_bl60x_init_crystal(void) +{ + uint32_t tmp; + int count = CLOCK_TIMEOUT; + + /* power crystal */ + tmp = sys_read32(AON_BASE + AON_RF_TOP_AON_OFFSET); + tmp = (tmp & AON_PU_XTAL_AON_UMSK) | (1U << AON_PU_XTAL_AON_POS); + tmp = (tmp & AON_PU_XTAL_BUF_AON_UMSK) | (1U << AON_PU_XTAL_BUF_AON_POS); + sys_write32(tmp, AON_BASE + AON_RF_TOP_AON_OFFSET); + + /* wait for crystal to be powered on */ + do { + clock_control_bl60x_clock_settle(); + tmp = sys_read32(AON_BASE + AON_TSEN_OFFSET); + count--; + } while (!(tmp & AON_XTAL_RDY_MSK) && count > 0); + + clock_control_bl60x_clock_settle(); + if (count < 1) { + return -1; + } + return 0; +} + +/* HCLK is the core clock */ +static int clock_control_bl60x_set_root_clock_dividers(uint32_t hclk_div, uint32_t bclk_div) +{ + uint32_t tmp; + uint32_t old_rootclk; + + old_rootclk = clock_control_bl60x_get_root_clock(); + + /* security RC32M */ + if (old_rootclk > 1) { + clock_control_bl60x_set_root_clock(0); + } + + /* set dividers */ + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG0_OFFSET); + tmp = (tmp & GLB_REG_HCLK_DIV_UMSK) | (hclk_div << GLB_REG_HCLK_DIV_POS); + tmp = (tmp & GLB_REG_BCLK_DIV_UMSK) | (bclk_div << GLB_REG_BCLK_DIV_POS); + sys_write32(tmp, GLB_BASE + GLB_CLK_CFG0_OFFSET); + + /* do something undocumented, probably acknowledging clock change by disabling then + * reenabling bclk + */ + sys_write32(0x00000001, 0x40000FFC); + sys_write32(0x00000000, 0x40000FFC); + + clock_control_bl60x_clock_settle(); + + /* enable clocks */ + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG0_OFFSET); + tmp = (tmp & GLB_REG_BCLK_EN_UMSK) | (1U << GLB_REG_BCLK_EN_POS); + tmp = (tmp & GLB_REG_HCLK_EN_UMSK) | (1U << GLB_REG_HCLK_EN_POS); + sys_write32(tmp, GLB_BASE + GLB_CLK_CFG0_OFFSET); + + clock_control_bl60x_set_root_clock(old_rootclk); + clock_control_bl60x_clock_settle(); + + return 0; +} + +static void clock_control_bl60x_set_machine_timer_clock_enable(bool enable) +{ + uint32_t tmp; + + tmp = sys_read32(GLB_BASE + GLB_CPU_CLK_CFG_OFFSET); + if (enable) { + tmp = (tmp & GLB_CPU_RTC_EN_UMSK) | (1U << GLB_CPU_RTC_EN_POS); + } else { + tmp = (tmp & GLB_CPU_RTC_EN_UMSK) | (0U << GLB_CPU_RTC_EN_POS); + } + sys_write32(tmp, GLB_BASE + GLB_CPU_CLK_CFG_OFFSET); +} + +/* clock: + * 0: BCLK + * 1: 32Khz Oscillator (RC32*K*) + */ +static void clock_control_bl60x_set_machine_timer_clock(bool enable, uint32_t clock, + uint32_t divider) +{ + uint32_t tmp; + + if (divider > 0x1FFFF) { + divider = 0x1FFFF; + } + if (clock > 1) { + clock = 1; + } + + /* disable first, then set div */ + clock_control_bl60x_set_machine_timer_clock_enable(false); + + tmp = sys_read32(GLB_BASE + GLB_CPU_CLK_CFG_OFFSET); + tmp = (tmp & GLB_CPU_RTC_SEL_UMSK) | (clock << GLB_CPU_RTC_SEL_POS); + tmp = (tmp & GLB_CPU_RTC_DIV_UMSK) | (divider << GLB_CPU_RTC_DIV_POS); + sys_write32(tmp, GLB_BASE + GLB_CPU_CLK_CFG_OFFSET); + + clock_control_bl60x_set_machine_timer_clock_enable(enable); +} + +static void clock_control_bl60x_deinit_pll(void) +{ + uint32_t tmp; + + /* PLL Off */ + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_PU_CLKPLL_SFREG_UMSK) | (0U << PDS_PU_CLKPLL_SFREG_POS); + tmp = (tmp & PDS_PU_CLKPLL_UMSK) | (0U << PDS_PU_CLKPLL_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + + /* needs 2 steps ? */ + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_CLKPLL_PU_CP_UMSK) | (0U << PDS_CLKPLL_PU_CP_POS); + tmp = (tmp & PDS_CLKPLL_PU_PFD_UMSK) | (0U << PDS_CLKPLL_PU_PFD_POS); + tmp = (tmp & PDS_CLKPLL_PU_FBDV_UMSK) | (0U << PDS_CLKPLL_PU_FBDV_POS); + tmp = (tmp & PDS_CLKPLL_PU_POSTDIV_UMSK) | (0U << PDS_CLKPLL_PU_POSTDIV_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); +} + +/* RC32M : 0 + * XTAL : 1 + */ +static void clock_control_bl60x_set_pll_source(uint32_t source) +{ + uint32_t tmp; + + tmp = sys_read32(PDS_BASE + PDS_CLKPLL_TOP_CTRL_OFFSET); + if (source > 0) { + tmp = (tmp & PDS_CLKPLL_REFCLK_SEL_UMSK) | (1U << PDS_CLKPLL_REFCLK_SEL_POS); + tmp = (tmp & PDS_CLKPLL_XTAL_RC32M_SEL_UMSK) | + (0U << PDS_CLKPLL_XTAL_RC32M_SEL_POS); + } else { + tmp = (tmp & PDS_CLKPLL_REFCLK_SEL_UMSK) | (0U << PDS_CLKPLL_REFCLK_SEL_POS); + tmp = (tmp & PDS_CLKPLL_XTAL_RC32M_SEL_UMSK) | + (1U << PDS_CLKPLL_XTAL_RC32M_SEL_POS); + } + sys_write32(tmp, PDS_BASE + PDS_CLKPLL_TOP_CTRL_OFFSET); +} + +static void clock_control_bl60x_init_pll(enum bl60x_clkid source, uint32_t crystal_id) +{ + uint32_t tmp; + uint32_t old_rootclk; + + old_rootclk = clock_control_bl60x_get_root_clock(); + + /* security RC32M */ + if (old_rootclk > 1) { + clock_control_bl60x_set_root_clock(0); + } + + clock_control_bl60x_deinit_pll(); + + if (source == BL60X_CLKID_CLK_CRYSTAL) { + clock_control_bl60x_set_pll_source(1); + } else { + clock_control_bl60x_set_pll_source(0); + } + + /* 26M special treatment */ + tmp = sys_read32(PDS_BASE + PDS_CLKPLL_CP_OFFSET); + if (crystal_id == CRYSTAL_ID_FREQ_26000000) { + tmp = (tmp & PDS_CLKPLL_ICP_1U_UMSK) | (1U << PDS_CLKPLL_ICP_1U_POS); + tmp = (tmp & PDS_CLKPLL_ICP_5U_UMSK) | (0U << PDS_CLKPLL_ICP_5U_POS); + tmp = (tmp & PDS_CLKPLL_INT_FRAC_SW_UMSK) | (1U << PDS_CLKPLL_INT_FRAC_SW_POS); + } else { + tmp = (tmp & PDS_CLKPLL_ICP_1U_UMSK) | (0U << PDS_CLKPLL_ICP_1U_POS); + tmp = (tmp & PDS_CLKPLL_ICP_5U_UMSK) | (2U << PDS_CLKPLL_ICP_5U_POS); + tmp = (tmp & PDS_CLKPLL_INT_FRAC_SW_UMSK) | (0U << PDS_CLKPLL_INT_FRAC_SW_POS); + } + sys_write32(tmp, PDS_BASE + PDS_CLKPLL_CP_OFFSET); + + /* More 26M special treatment */ + tmp = sys_read32(PDS_BASE + PDS_CLKPLL_RZ_OFFSET); + if (crystal_id == CRYSTAL_ID_FREQ_26000000) { + tmp = (tmp & PDS_CLKPLL_C3_UMSK) | (2U << PDS_CLKPLL_C3_POS); + tmp = (tmp & PDS_CLKPLL_CZ_UMSK) | (2U << PDS_CLKPLL_CZ_POS); + tmp = (tmp & PDS_CLKPLL_RZ_UMSK) | (5U << PDS_CLKPLL_RZ_POS); + tmp = (tmp & PDS_CLKPLL_R4_SHORT_UMSK) | (0U << PDS_CLKPLL_R4_SHORT_POS); + } else { + tmp = (tmp & PDS_CLKPLL_C3_UMSK) | (3U << PDS_CLKPLL_C3_POS); + tmp = (tmp & PDS_CLKPLL_CZ_UMSK) | (1U << PDS_CLKPLL_CZ_POS); + tmp = (tmp & PDS_CLKPLL_RZ_UMSK) | (1U << PDS_CLKPLL_RZ_POS); + tmp = (tmp & PDS_CLKPLL_R4_SHORT_UMSK) | (1U << PDS_CLKPLL_R4_SHORT_POS); + } + tmp = (tmp & PDS_CLKPLL_R4_UMSK) | (2U << PDS_CLKPLL_R4_POS); + sys_write32(tmp, PDS_BASE + PDS_CLKPLL_RZ_OFFSET); + + /* set pll dividers */ + tmp = sys_read32(PDS_BASE + PDS_CLKPLL_TOP_CTRL_OFFSET); + tmp = (tmp & PDS_CLKPLL_POSTDIV_UMSK) | ((uint32_t)(0x14) << PDS_CLKPLL_POSTDIV_POS); + tmp = (tmp & PDS_CLKPLL_REFDIV_RATIO_UMSK) | (2U << PDS_CLKPLL_REFDIV_RATIO_POS); + sys_write32(tmp, PDS_BASE + PDS_CLKPLL_TOP_CTRL_OFFSET); + + /* set SDMIN */ + tmp = sys_read32(PDS_BASE + PDS_CLKPLL_SDM_OFFSET); + if (source == BL60X_CLKID_CLK_CRYSTAL) { + tmp = (tmp & PDS_CLKPLL_SDMIN_UMSK) | + (clock_control_bl60x_crystal_SDMIN_table[crystal_id] + << PDS_CLKPLL_SDMIN_POS); + } else { + tmp = (tmp & PDS_CLKPLL_SDMIN_UMSK) | + (clock_control_bl60x_crystal_SDMIN_table[CRYSTAL_ID_FREQ_32000000] + << PDS_CLKPLL_SDMIN_POS); + } + sys_write32(tmp, PDS_BASE + PDS_CLKPLL_SDM_OFFSET); + + /* phase comparator settings? */ + tmp = sys_read32(PDS_BASE + PDS_CLKPLL_FBDV_OFFSET); + tmp = (tmp & PDS_CLKPLL_SEL_FB_CLK_UMSK) | (1U << PDS_CLKPLL_SEL_FB_CLK_POS); + tmp = (tmp & PDS_CLKPLL_SEL_SAMPLE_CLK_UMSK) | (1U << PDS_CLKPLL_SEL_SAMPLE_CLK_POS); + sys_write32(tmp, PDS_BASE + PDS_CLKPLL_FBDV_OFFSET); + + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_PU_CLKPLL_SFREG_UMSK) | (1U << PDS_PU_CLKPLL_SFREG_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + clock_control_bl60x_clock_settle(); + + /* enable PPL clock actual? */ + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_PU_CLKPLL_UMSK) | (1U << PDS_PU_CLKPLL_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + + /* More power up sequencing*/ + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_CLKPLL_PU_CP_UMSK) | (1U << PDS_CLKPLL_PU_CP_POS); + tmp = (tmp & PDS_CLKPLL_PU_PFD_UMSK) | (1U << PDS_CLKPLL_PU_PFD_POS); + tmp = (tmp & PDS_CLKPLL_PU_FBDV_UMSK) | (1U << PDS_CLKPLL_PU_FBDV_POS); + tmp = (tmp & PDS_CLKPLL_PU_POSTDIV_UMSK) | (1U << PDS_CLKPLL_PU_POSTDIV_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + + clock_control_bl60x_clock_settle(); + + /* reset couple things one by one? */ + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_CLKPLL_SDM_RESET_UMSK) | (1U << PDS_CLKPLL_SDM_RESET_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_CLKPLL_RESET_FBDV_UMSK) | (1U << PDS_CLKPLL_RESET_FBDV_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_CLKPLL_RESET_FBDV_UMSK) | (0U << PDS_CLKPLL_RESET_FBDV_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + + tmp = sys_read32(PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + tmp = (tmp & PDS_CLKPLL_SDM_RESET_UMSK) | (0U << PDS_CLKPLL_SDM_RESET_POS); + sys_write32(tmp, PDS_BASE + PDS_PU_RST_CLKPLL_OFFSET); + + clock_control_bl60x_set_root_clock(old_rootclk); + clock_control_bl60x_clock_settle(); +} + +/* + * 0: 48M + * 1: 120M + * 2: 160M + * 3: 192M + */ +static void clock_control_bl60x_select_PLL(uint8_t pll) +{ + uint32_t tmp; + + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG0_OFFSET); + tmp = (tmp & GLB_REG_PLL_SEL_UMSK) | (pll << GLB_REG_PLL_SEL_POS); + sys_write32(tmp, GLB_BASE + GLB_CLK_CFG0_OFFSET); +} + +static int clock_control_bl60x_clock_trim_32M(void) +{ + uint32_t tmp; + int err; + uint32_t trim, trim_parity; + const struct device *efuse = DEVICE_DT_GET_ONE(bflb_efuse); + + err = syscon_read_reg(efuse, EFUSE_RC32M_TRIM_OFFSET, &trim); + if (err < 0) { + LOG_ERR("Error: Couldn't read efuses: err: %d.\n", err); + return err; + } + if (!((trim >> EFUSE_RC32M_TRIM_EN_POS) & 1)) { + LOG_ERR("RC32M trim disabled!"); + return -EINVAL; + } + + trim_parity = (trim >> EFUSE_RC32M_TRIM_PARITY_POS) & 1; + trim = (trim & EFUSE_RC32M_TRIM_MSK) >> EFUSE_RC32M_TRIM_POS; + + if (trim_parity != (POPCOUNT(trim) & 1)) { + LOG_ERR("Bad trim parity"); + return -EINVAL; + } + + tmp = sys_read32(PDS_BASE + PDS_RC32M_CTRL0_OFFSET); + tmp = (tmp & PDS_RC32M_EXT_CODE_EN_UMSK) | 1 << PDS_RC32M_EXT_CODE_EN_POS; + tmp = (tmp & PDS_RC32M_CODE_FR_EXT_UMSK) | trim << PDS_RC32M_CODE_FR_EXT_POS; + sys_write32(tmp, PDS_BASE + PDS_RC32M_CTRL0_OFFSET); + + clock_control_bl60x_clock_settle(); + + return 0; +} + +/* source for most clocks, either XTAL or RC32M */ +static uint32_t clock_control_bl60x_get_xclk(const struct device *dev) +{ + uint32_t tmp; + + tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET); + tmp &= HBN_ROOT_CLK_SEL_MSK; + tmp = tmp >> HBN_ROOT_CLK_SEL_POS; + tmp &= 1; + if (tmp == 0) { + return BFLB_RC32M_FREQUENCY; + } else if (tmp == 1) { + return DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(0, crystal), clock_frequency); + } else { + return 0; + } +} + +static uint32_t clock_control_bl60x_get_clk(const struct device *dev) +{ + uint32_t tmp; + uint32_t hclk_div; + + hclk_div = sys_read32(GLB_BASE + GLB_CLK_CFG0_OFFSET); + hclk_div = (hclk_div & GLB_REG_HCLK_DIV_MSK) >> GLB_REG_HCLK_DIV_POS; + + tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET); + tmp &= HBN_ROOT_CLK_SEL_MSK; + tmp = (tmp >> HBN_ROOT_CLK_SEL_POS) >> 1; + tmp &= 1; + + if (tmp == 0) { + return clock_control_bl60x_get_xclk(dev) / (hclk_div + 1); + } + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG0_OFFSET); + tmp = (tmp & GLB_REG_PLL_SEL_MSK) >> GLB_REG_PLL_SEL_POS; + if (tmp == 3) { + return MHZ(192) / (hclk_div + 1); + } else if (tmp == 2) { + return MHZ(160) / (hclk_div + 1); + } else if (tmp == 1) { + return MHZ(120) / (hclk_div + 1); + } else if (tmp == 0) { + return MHZ(48) / (hclk_div + 1); + } + return 0; +} + +/* most peripherals clock */ +static uint32_t clock_control_bl60x_get_bclk(const struct device *dev) +{ + uint32_t tmp; + uint32_t clock_id; + + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG0_OFFSET); + tmp = (tmp & GLB_REG_BCLK_DIV_MSK) >> GLB_REG_BCLK_DIV_POS; + clock_id = clock_control_bl60x_get_clk(dev); + return clock_id / (tmp + 1); +} + +static uint32_t clock_control_bl60x_mtimer_get_clk_src_div(const struct device *dev) +{ + return clock_control_bl60x_get_bclk(dev) / 1000 / 1000 - 1; +} + +static void clock_control_bl60x_cache_2T(bool yes) +{ + uint32_t tmp; + + tmp = sys_read32(L1C_BASE + L1C_CONFIG_OFFSET); + + if (yes) { + tmp |= L1C_IROM_2T_ACCESS_MSK; + } else { + tmp &= ~L1C_IROM_2T_ACCESS_MSK; + } + + sys_write32(tmp, L1C_BASE + L1C_CONFIG_OFFSET); +} + +/* HCLK: 0 + * PLL120M: 1 + */ +static void clock_control_bl60x_set_PKA_clock(uint32_t pka_clock) +{ + uint32_t tmp; + + tmp = sys_read32(GLB_BASE + GLB_SWRST_CFG2_OFFSET); + tmp = (tmp & GLB_PKA_CLK_SEL_UMSK) | (pka_clock << GLB_PKA_CLK_SEL_POS); + sys_write32(tmp, GLB_BASE + GLB_SWRST_CFG2_OFFSET); +} + +static void clock_control_bl60x_init_root_as_pll(const struct device *dev) +{ + struct clock_control_bl60x_data *data = dev->data; + const struct clock_control_bl60x_config *config = dev->config; + uint32_t tmp; + + clock_control_bl60x_init_pll(data->pll.source, config->crystal_id); + + /* enable all 'PDS' clocks */ + tmp = sys_read32(PDS_BASE + PDS_CLKPLL_OUTPUT_EN_OFFSET); + tmp |= 0x1FF; + sys_write32(tmp, PDS_BASE + PDS_CLKPLL_OUTPUT_EN_OFFSET); + + /* glb enable pll actual? */ + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG0_OFFSET); + tmp = (tmp & GLB_REG_PLL_EN_UMSK) | (1U << GLB_REG_PLL_EN_POS); + sys_write32(tmp, GLB_BASE + GLB_CLK_CFG0_OFFSET); + + clock_control_bl60x_select_PLL(data->root.pll_select); + + if (data->pll.source == bl60x_clkid_clk_crystal) { + clock_control_bl60x_set_root_clock(3); + } else { + clock_control_bl60x_set_root_clock(2); + } + + if (clock_control_bl60x_get_clk(dev) > MHZ(120)) { + clock_control_bl60x_cache_2T(true); + } + + sys_write32(clock_control_bl60x_get_clk(dev), CORECLOCKREGISTER); + clock_control_bl60x_set_PKA_clock(1); +} + +static void clock_control_bl60x_init_root_as_crystal(const struct device *dev) +{ + clock_control_bl60x_set_root_clock(1); + sys_write32(clock_control_bl60x_get_clk(dev), CORECLOCKREGISTER); +} + +static int clock_control_bl60x_update_root(const struct device *dev) +{ + struct clock_control_bl60x_data *data = dev->data; + uint32_t tmp; + int ret; + + /* make sure all clocks are enabled */ + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG0_OFFSET); + tmp = (tmp & GLB_REG_BCLK_EN_UMSK) | (1U << GLB_REG_BCLK_EN_POS); + tmp = (tmp & GLB_REG_HCLK_EN_UMSK) | (1U << GLB_REG_HCLK_EN_POS); + tmp = (tmp & GLB_REG_FCLK_EN_UMSK) | (1U << GLB_REG_FCLK_EN_POS); + sys_write32(tmp, GLB_BASE + GLB_CLK_CFG0_OFFSET); + + /* set root clock to internal 32MHz Oscillator as failsafe */ + clock_control_bl60x_set_root_clock(0); + if (clock_control_bl60x_set_root_clock_dividers(0, 0) != 0) { + return -EIO; + } + sys_write32(BFLB_RC32M_FREQUENCY, CORECLOCKREGISTER); + + clock_control_bl60x_set_PKA_clock(0); + + if (data->crystal_enabled) { + if (clock_control_bl60x_init_crystal() < 0) { + return -EIO; + } + } else { + clock_control_bl60x_deinit_crystal(); + } + + ret = clock_control_bl60x_set_root_clock_dividers(data->root.divider - 1, + data->bclk.divider - 1); + if (ret < 0) { + return ret; + } + + if (data->root.source == bl60x_clkid_clk_pll) { + clock_control_bl60x_init_root_as_pll(dev); + } else if (data->root.source == bl60x_clkid_clk_crystal) { + clock_control_bl60x_init_root_as_crystal(dev); + } else { + /* Root clock already setup as RC32M */ + } + + ret = clock_control_bl60x_clock_trim_32M(); + if (ret < 0) { + return ret; + } + clock_control_bl60x_set_machine_timer_clock( + 1, 0, clock_control_bl60x_mtimer_get_clk_src_div(dev)); + + clock_control_bl60x_clock_settle(); + + return ret; +} + +static void clock_control_bl60x_uart_set_clock_enable(bool enable) +{ + uint32_t tmp; + + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG2_OFFSET); + if (enable) { + tmp = (tmp & GLB_UART_CLK_EN_UMSK) | (1U << GLB_UART_CLK_EN_POS); + } else { + tmp = (tmp & GLB_UART_CLK_EN_UMSK) | (0U << GLB_UART_CLK_EN_POS); + } + sys_write32(tmp, GLB_BASE + GLB_CLK_CFG2_OFFSET); +} + +/* Clock: + * FCLK: 0 + * 160 Mhz PLL: 1 + * When using PLL root clock, we can use either setting, when using the 32Mhz Oscillator with a + * uninitialized PLL, only FCLK will be available. + */ +static void clock_control_bl60x_uart_set_clock(bool enable, uint32_t clock, uint32_t divider) +{ + uint32_t tmp; + + if (divider > 0x7) { + divider = 0x7; + } + if (clock > 1) { + clock = 1; + } + /* disable uart clock */ + clock_control_bl60x_uart_set_clock_enable(false); + + tmp = sys_read32(GLB_BASE + GLB_CLK_CFG2_OFFSET); + tmp = (tmp & GLB_UART_CLK_DIV_UMSK) | (divider << GLB_UART_CLK_DIV_POS); + sys_write32(tmp, GLB_BASE + GLB_CLK_CFG2_OFFSET); + + tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET); + tmp = (tmp & HBN_UART_CLK_SEL_UMSK) | (clock << HBN_UART_CLK_SEL_POS); + sys_write32(tmp, HBN_BASE + HBN_GLB_OFFSET); + + clock_control_bl60x_uart_set_clock_enable(enable); +} + +/* Simple function to enable all peripherals for now */ +static void clock_control_bl60x_peripheral_clock_init(void) +{ + uint32_t regval = sys_read32(GLB_BASE + GLB_CGEN_CFG1_OFFSET); + + /* enable ADC clock routing */ + regval |= (1 << 2); + /* enable UART0 clock routing */ + regval |= (1 << 16); + /* enable I2C0 clock routing */ + regval |= (1 << 19); + + sys_write32(regval, GLB_BASE + GLB_CGEN_CFG1_OFFSET); + + clock_control_bl60x_uart_set_clock(1, 0, 0); +} + +static int clock_control_bl60x_on(const struct device *dev, clock_control_subsys_t sys) +{ + struct clock_control_bl60x_data *data = dev->data; + int ret = -EINVAL; + uint32_t key; + enum bl60x_clkid oldroot; + + key = irq_lock(); + + if ((enum bl60x_clkid)sys == bl60x_clkid_clk_crystal) { + if (data->crystal_enabled) { + ret = 0; + } else { + data->crystal_enabled = true; + ret = clock_control_bl60x_update_root(dev); + if (ret < 0) { + data->crystal_enabled = false; + } + } + } else if ((enum bl60x_clkid)sys == bl60x_clkid_clk_pll) { + if (data->pll_enabled) { + ret = 0; + } else { + data->pll_enabled = true; + ret = clock_control_bl60x_update_root(dev); + if (ret < 0) { + data->pll_enabled = false; + } + } + } else if ((int)sys == BFLB_FORCE_ROOT_RC32M) { + if (data->root.source == bl60x_clkid_clk_rc32m) { + ret = 0; + } else { + /* Cannot fail to set root to rc32m */ + data->root.source = bl60x_clkid_clk_rc32m; + ret = clock_control_bl60x_update_root(dev); + } + } else if ((int)sys == BFLB_FORCE_ROOT_CRYSTAL) { + if (data->root.source == bl60x_clkid_clk_crystal) { + ret = 0; + } else { + oldroot = data->root.source; + data->root.source = bl60x_clkid_clk_crystal; + ret = clock_control_bl60x_update_root(dev); + if (ret < 0) { + data->root.source = oldroot; + } + } + } else if ((int)sys == BFLB_FORCE_ROOT_PLL) { + if (data->root.source == bl60x_clkid_clk_pll) { + ret = 0; + } else { + oldroot = data->root.source; + data->root.source = bl60x_clkid_clk_pll; + ret = clock_control_bl60x_update_root(dev); + if (ret < 0) { + data->root.source = oldroot; + } + } + } + + irq_unlock(key); + return ret; +} + +static int clock_control_bl60x_off(const struct device *dev, clock_control_subsys_t sys) +{ + struct clock_control_bl60x_data *data = dev->data; + int ret = -EINVAL; + uint32_t key; + + key = irq_lock(); + + if ((enum bl60x_clkid)sys == bl60x_clkid_clk_crystal) { + if (!data->crystal_enabled) { + ret = 0; + } else { + data->crystal_enabled = false; + ret = clock_control_bl60x_update_root(dev); + if (ret < 0) { + data->crystal_enabled = true; + } + } + } else if ((enum bl60x_clkid)sys == bl60x_clkid_clk_pll) { + if (!data->pll_enabled) { + ret = 0; + } else { + data->pll_enabled = false; + ret = clock_control_bl60x_update_root(dev); + if (ret < 0) { + data->pll_enabled = true; + } + } + } + + irq_unlock(key); + return ret; +} + +static enum clock_control_status clock_control_bl60x_get_status(const struct device *dev, + clock_control_subsys_t sys) +{ + struct clock_control_bl60x_data *data = dev->data; + + switch ((enum bl60x_clkid)sys) { + case bl60x_clkid_clk_root: + case bl60x_clkid_clk_bclk: + case bl60x_clkid_clk_rc32m: + return CLOCK_CONTROL_STATUS_ON; + case bl60x_clkid_clk_crystal: + if (data->crystal_enabled) { + return CLOCK_CONTROL_STATUS_ON; + } + return CLOCK_CONTROL_STATUS_OFF; + case bl60x_clkid_clk_pll: + if (data->pll_enabled) { + return CLOCK_CONTROL_STATUS_ON; + } + return CLOCK_CONTROL_STATUS_OFF; + default: + return CLOCK_CONTROL_STATUS_UNKNOWN; + } +} + +static int clock_control_bl60x_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + if ((enum bl60x_clkid)sys == bl60x_clkid_clk_root) { + *rate = clock_control_bl60x_get_clk(dev); + } else if ((enum bl60x_clkid)sys == bl60x_clkid_clk_bclk) { + *rate = clock_control_bl60x_get_bclk(dev); + } else if ((enum bl60x_clkid)sys == bl60x_clkid_clk_crystal) { + *rate = DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(0, crystal), clock_frequency); + } else if ((enum bl60x_clkid)sys == bl60x_clkid_clk_rc32m) { + *rate = BFLB_RC32M_FREQUENCY; + } else { + return -EINVAL; + } + return 0; +} + +static int clock_control_bl60x_init(const struct device *dev) +{ + int ret; + uint32_t key; + + key = irq_lock(); + + ret = clock_control_bl60x_update_root(dev); + if (ret < 0) { + irq_unlock(key); + return ret; + } + + clock_control_bl60x_peripheral_clock_init(); + + irq_unlock(key); + + return 0; +} + +static DEVICE_API(clock_control, clock_control_bl60x_api) = { + .on = clock_control_bl60x_on, + .off = clock_control_bl60x_off, + .get_rate = clock_control_bl60x_get_rate, + .get_status = clock_control_bl60x_get_status, +}; + +static const struct clock_control_bl60x_config clock_control_bl60x_config = { + .crystal_id = CRYSTAL_FREQ_TO_ID(DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(0, crystal), + clock_frequency)), +}; + +static struct clock_control_bl60x_data clock_control_bl60x_data = { + .crystal_enabled = DT_NODE_HAS_STATUS_OKAY(DT_INST_CLOCKS_CTLR_BY_NAME(0, crystal)), + .pll_enabled = DT_NODE_HAS_STATUS_OKAY(DT_INST_CLOCKS_CTLR_BY_NAME(0, pll_192)), + + .root = { +#if CLK_SRC_IS(root, pll_192) + .source = bl60x_clkid_clk_pll, + .pll_select = DT_CLOCKS_CELL(DT_INST_CLOCKS_CTLR_BY_NAME(0, root), select), +#elif CLK_SRC_IS(root, crystal) + .source = bl60x_clkid_clk_crystal, +#else + .source = bl60x_clkid_clk_rc32m, +#endif + .divider = DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(0, root), divider), + }, + + .pll = { +#if CLK_SRC_IS(pll_192, crystal) + .source = bl60x_clkid_clk_crystal, +#else + .source = bl60x_clkid_clk_rc32m, +#endif + }, + + .bclk = { + .divider = DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(0, bclk), divider), + }, +}; + +BUILD_ASSERT(CLK_SRC_IS(pll_192, crystal) || CLK_SRC_IS(root, crystal) + ? DT_NODE_HAS_STATUS_OKAY(DT_INST_CLOCKS_CTLR_BY_NAME(0, crystal)) + : 1, + "Crystal must be enabled to use it"); + +BUILD_ASSERT(CLK_SRC_IS(root, pll_192) ? + DT_NODE_HAS_STATUS_OKAY(DT_INST_CLOCKS_CTLR_BY_NAME(0, pll_192)) : 1, + "PLL must be enabled to use it"); + +BUILD_ASSERT(DT_NODE_HAS_STATUS_OKAY(DT_INST_CLOCKS_CTLR_BY_NAME(0, rc32m)), "RC32M is always on"); + +BUILD_ASSERT(DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(0, rc32m), clock_frequency) + == BFLB_RC32M_FREQUENCY, "RC32M must be 32M"); + +DEVICE_DT_INST_DEFINE(0, clock_control_bl60x_init, NULL, &clock_control_bl60x_data, + &clock_control_bl60x_config, PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &clock_control_bl60x_api); diff --git a/drivers/clock_control/clock_control_nrf2_common.c b/drivers/clock_control/clock_control_nrf2_common.c index f070d5d7b69..c2657f45478 100644 --- a/drivers/clock_control/clock_control_nrf2_common.c +++ b/drivers/clock_control/clock_control_nrf2_common.c @@ -5,7 +5,6 @@ #include "clock_control_nrf2_common.h" #include -#include #include LOG_MODULE_REGISTER(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); @@ -20,8 +19,6 @@ LOG_MODULE_REGISTER(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); (idx * sizeof(array[0])) - \ offsetof(type, array[0])) -#define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr)) - /* * Definition of `struct clock_config_generic`. * Used to access `clock_config_*` structures in a common way. @@ -92,40 +89,6 @@ static inline uint8_t get_index_of_highest_bit(uint32_t value) return value ? (uint8_t)(31 - __builtin_clz(value)) : 0; } -int lfosc_get_accuracy(uint16_t *accuracy) -{ - switch (nrf_bicr_lfosc_accuracy_get(BICR)) { - case NRF_BICR_LFOSC_ACCURACY_500PPM: - *accuracy = 500U; - break; - case NRF_BICR_LFOSC_ACCURACY_250PPM: - *accuracy = 250U; - break; - case NRF_BICR_LFOSC_ACCURACY_150PPM: - *accuracy = 150U; - break; - case NRF_BICR_LFOSC_ACCURACY_100PPM: - *accuracy = 100U; - break; - case NRF_BICR_LFOSC_ACCURACY_75PPM: - *accuracy = 75U; - break; - case NRF_BICR_LFOSC_ACCURACY_50PPM: - *accuracy = 50U; - break; - case NRF_BICR_LFOSC_ACCURACY_30PPM: - *accuracy = 30U; - break; - case NRF_BICR_LFOSC_ACCURACY_20PPM: - *accuracy = 20U; - break; - default: - return -EINVAL; - } - - return 0; -} - int clock_config_init(void *clk_cfg, uint8_t onoff_cnt, k_work_handler_t update_work_handler) { struct clock_config_generic *cfg = clk_cfg; diff --git a/drivers/clock_control/clock_control_nrf2_common.h b/drivers/clock_control/clock_control_nrf2_common.h index 7f934fdac5b..4b1c7fe000e 100644 --- a/drivers/clock_control/clock_control_nrf2_common.h +++ b/drivers/clock_control/clock_control_nrf2_common.h @@ -36,16 +36,6 @@ struct clock_onoff { struct clock_onoff onoff[_onoff_cnt]; \ } -/** - * @brief Obtain LFOSC accuracy in ppm. - * - * @param[out] accuracy Accuracy in ppm. - * - * @retval 0 On success - * @retval -EINVAL If accuracy is not configured. - */ -int lfosc_get_accuracy(uint16_t *accuracy); - /** * @brief Initializes a clock configuration structure. * diff --git a/drivers/clock_control/clock_control_nrf_auxpll.c b/drivers/clock_control/clock_control_nrf_auxpll.c index 19dee8c4493..d048f4ed1c7 100644 --- a/drivers/clock_control/clock_control_nrf_auxpll.c +++ b/drivers/clock_control/clock_control_nrf_auxpll.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2025 Nordic Semiconductor ASA * SPDX-License-Identifier: Apache-2.0 */ @@ -9,55 +9,65 @@ #include #include -#include #include -#include -#include +#include #include #include +#include +#include "clock_control_nrf2_common.h" #include -/* maximum lock time in ms, >10x time observed experimentally */ -#define AUXPLL_LOCK_TIME_MAX_MS 20 -/* lock wait step in ms*/ -#define AUXPLL_LOCK_WAIT_STEP_MS 1 + +/* Check dt-bindings match MDK frequency division definitions*/ +BUILD_ASSERT(NRF_AUXPLL_FREQ_DIV_MIN == NRF_AUXPLL_FREQUENCY_DIV_MIN, + "Different AUXPLL_FREQ_DIV_MIN definition in MDK and devicetree binding"); +BUILD_ASSERT(NRF_AUXPLL_FREQ_DIV_AUDIO_44K1 == NRF_AUXPLL_FREQUENCY_AUDIO_44K1, + "Different AUXPLL_FREQ_DIV_AUDIO_44K1 definition in MDK and devicetree binding"); +BUILD_ASSERT(NRF_AUXPLL_FREQ_DIV_USB24M == NRF_AUXPLL_FREQUENCY_USB_24M, + "Different AUXPLL_FREQ_DIV_USB24M definition in MDK and devicetree binding"); +BUILD_ASSERT(NRF_AUXPLL_FREQ_DIV_AUDIO_48K == NRF_AUXPLL_FREQUENCY_AUDIO_48K, + "Different AUXPLL_FREQ_DIV_AUDIO_48K definition in MDK and devicetree binding"); +BUILD_ASSERT(NRF_AUXPLL_FREQ_DIV_MAX == NRF_AUXPLL_FREQUENCY_DIV_MAX, + "Different AUXPLL_FREQ_DIV_MAX definition in MDK and devicetree binding"); + +/* maximum lock time in us, >10x time observed experimentally */ +#define AUXPLL_LOCK_TIME_MAX_US 20000 +/* lock wait step in us*/ +#define AUXPLL_LOCK_WAIT_STEP_US 1000 + +struct dev_data_auxpll { + struct onoff_manager mgr; + onoff_notify_fn notify; + const struct device *dev; +}; struct clock_control_nrf_auxpll_config { NRF_AUXPLL_Type *auxpll; uint32_t ref_clk_hz; uint32_t ficr_ctune; nrf_auxpll_config_t cfg; - uint16_t frequency; + nrf_auxpll_freq_div_ratio_t frequency; nrf_auxpll_ctrl_outsel_t out_div; }; -static int clock_control_nrf_auxpll_on(const struct device *dev, clock_control_subsys_t sys) +static int clock_control_nrf_auxpll_on(struct dev_data_auxpll *dev_data) { - const struct clock_control_nrf_auxpll_config *config = dev->config; + const struct clock_control_nrf_auxpll_config *config = dev_data->dev->config; bool locked; - unsigned int wait = 0U; - - ARG_UNUSED(sys); nrf_auxpll_task_trigger(config->auxpll, NRF_AUXPLL_TASK_START); - do { - locked = nrf_auxpll_mode_locked_check(config->auxpll); - if (!locked) { - k_msleep(AUXPLL_LOCK_WAIT_STEP_MS); - wait += AUXPLL_LOCK_WAIT_STEP_MS; - } - } while (wait < AUXPLL_LOCK_TIME_MAX_MS && !locked); + NRFX_WAIT_FOR(nrf_auxpll_mode_locked_check(config->auxpll), + AUXPLL_LOCK_TIME_MAX_US / AUXPLL_LOCK_WAIT_STEP_US, + AUXPLL_LOCK_WAIT_STEP_US, locked); return locked ? 0 : -ETIMEDOUT; } -static int clock_control_nrf_auxpll_off(const struct device *dev, clock_control_subsys_t sys) +static int clock_control_nrf_auxpll_off(struct dev_data_auxpll *dev_data) { - const struct clock_control_nrf_auxpll_config *config = dev->config; - - ARG_UNUSED(sys); + const struct clock_control_nrf_auxpll_config *config = dev_data->dev->config; nrf_auxpll_task_trigger(config->auxpll, NRF_AUXPLL_TASK_STOP); @@ -67,6 +77,58 @@ static int clock_control_nrf_auxpll_off(const struct device *dev, clock_control_ return 0; } +static void onoff_start_auxpll(struct onoff_manager *mgr, onoff_notify_fn notify) +{ + struct dev_data_auxpll *dev_data = + CONTAINER_OF(mgr, struct dev_data_auxpll, mgr); + + int ret = clock_control_nrf_auxpll_on(dev_data); + + notify(&dev_data->mgr, ret); + +} + +static void onoff_stop_auxpll(struct onoff_manager *mgr, onoff_notify_fn notify) +{ + struct dev_data_auxpll *dev_data = + CONTAINER_OF(mgr, struct dev_data_auxpll, mgr); + + clock_control_nrf_auxpll_off(dev_data); + notify(mgr, 0); +} + +static int api_request_auxpll(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + struct dev_data_auxpll *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_request(&dev_data->mgr, cli); +} + +static int api_release_auxpll(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + struct dev_data_auxpll *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_release(&dev_data->mgr); +} + +static int api_cancel_or_release_auxpll(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + struct dev_data_auxpll *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_cancel_or_release(&dev_data->mgr, cli); +} + static int clock_control_nrf_auxpll_get_rate(const struct device *dev, clock_control_subsys_t sys, uint32_t *rate) { @@ -99,16 +161,21 @@ static enum clock_control_status clock_control_nrf_auxpll_get_status(const struc return CLOCK_CONTROL_STATUS_OFF; } -static DEVICE_API(clock_control, clock_control_nrf_auxpll_api) = { - .on = clock_control_nrf_auxpll_on, - .off = clock_control_nrf_auxpll_off, - .get_rate = clock_control_nrf_auxpll_get_rate, - .get_status = clock_control_nrf_auxpll_get_status, +static const struct onoff_transitions transitions = { + .start = onoff_start_auxpll, + .stop = onoff_stop_auxpll }; static int clock_control_nrf_auxpll_init(const struct device *dev) { + struct dev_data_auxpll *dev_data = dev->data; const struct clock_control_nrf_auxpll_config *config = dev->config; + int rc; + + rc = onoff_manager_init(&dev_data->mgr, &transitions); + if (rc < 0) { + return rc; + } nrf_auxpll_ctrl_frequency_set(config->auxpll, config->frequency); @@ -123,7 +190,31 @@ static int clock_control_nrf_auxpll_init(const struct device *dev) return 0; } +static DEVICE_API(nrf_clock_control, drv_api_auxpll) = { + .std_api = { + .on = api_nosys_on_off, + .off = api_nosys_on_off, + .get_rate = clock_control_nrf_auxpll_get_rate, + .get_status = clock_control_nrf_auxpll_get_status, + }, + .request = api_request_auxpll, + .release = api_release_auxpll, + .cancel_or_release = api_cancel_or_release_auxpll, +}; + #define CLOCK_CONTROL_NRF_AUXPLL_DEFINE(n) \ + BUILD_ASSERT( \ + DT_INST_PROP(n, nordic_frequency) == NRF_AUXPLL_FREQUENCY_DIV_MIN || \ + DT_INST_PROP(n, nordic_frequency) == NRF_AUXPLL_FREQUENCY_AUDIO_44K1 || \ + DT_INST_PROP(n, nordic_frequency) == NRF_AUXPLL_FREQUENCY_USB_24M || \ + DT_INST_PROP(n, nordic_frequency) == NRF_AUXPLL_FREQUENCY_AUDIO_48K || \ + DT_INST_PROP(n, nordic_frequency) == NRF_AUXPLL_FREQUENCY_DIV_MAX, \ + "Invalid nordic,frequency value in DeviceTree for AUXPLL instance " #n); \ + BUILD_ASSERT(DT_INST_PROP(n, nordic_out_div) > 0, \ + "nordic,out_div must be greater than 0 for AUXPLL instance " #n); \ + static struct dev_data_auxpll data_auxpll##n = { \ + .dev = DEVICE_DT_INST_GET(n), \ + }; \ static const struct clock_control_nrf_auxpll_config config##n = { \ .auxpll = (NRF_AUXPLL_Type *)DT_INST_REG_ADDR(n), \ .ref_clk_hz = DT_PROP(DT_INST_CLOCKS_CTLR(n), clock_frequency), \ @@ -140,9 +231,9 @@ static int clock_control_nrf_auxpll_init(const struct device *dev) .frequency = DT_INST_PROP(n, nordic_frequency), \ .out_div = DT_INST_PROP(n, nordic_out_div), \ }; \ - \ - DEVICE_DT_INST_DEFINE(n, clock_control_nrf_auxpll_init, NULL, NULL, &config##n, \ + \ + DEVICE_DT_INST_DEFINE(n, clock_control_nrf_auxpll_init, NULL, &data_auxpll##n, &config##n, \ PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, \ - &clock_control_nrf_auxpll_api); + &drv_api_auxpll); DT_INST_FOREACH_STATUS_OKAY(CLOCK_CONTROL_NRF_AUXPLL_DEFINE) diff --git a/drivers/clock_control/clock_control_nrf_lfclk.c b/drivers/clock_control/clock_control_nrf_lfclk.c index fcdb8bd573b..829416f662c 100644 --- a/drivers/clock_control/clock_control_nrf_lfclk.c +++ b/drivers/clock_control/clock_control_nrf_lfclk.c @@ -69,6 +69,40 @@ struct lfclk_dev_config { uint32_t fixed_frequency; }; +static int lfosc_get_accuracy(uint16_t *accuracy) +{ + switch (nrf_bicr_lfosc_accuracy_get(BICR)) { + case NRF_BICR_LFOSC_ACCURACY_500PPM: + *accuracy = 500U; + break; + case NRF_BICR_LFOSC_ACCURACY_250PPM: + *accuracy = 250U; + break; + case NRF_BICR_LFOSC_ACCURACY_150PPM: + *accuracy = 150U; + break; + case NRF_BICR_LFOSC_ACCURACY_100PPM: + *accuracy = 100U; + break; + case NRF_BICR_LFOSC_ACCURACY_75PPM: + *accuracy = 75U; + break; + case NRF_BICR_LFOSC_ACCURACY_50PPM: + *accuracy = 50U; + break; + case NRF_BICR_LFOSC_ACCURACY_30PPM: + *accuracy = 30U; + break; + case NRF_BICR_LFOSC_ACCURACY_20PPM: + *accuracy = 20U; + break; + default: + return -EINVAL; + } + + return 0; +} + static void clock_evt_handler(nrfs_clock_evt_t const *p_evt, void *context) { struct lfclk_dev_data *dev_data = context; diff --git a/dts/bindings/clock/bflb,bclk.yaml b/dts/bindings/clock/bflb,bclk.yaml new file mode 100644 index 00000000000..343c5831ed0 --- /dev/null +++ b/dts/bindings/clock/bflb,bclk.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +description: | + The BCLK clock, or peripheral clock + Source Clock -> Root Clock -> / divider -> BCLK + +compatible: "bflb,bclk" + +include: [base.yaml, clock-controller.yaml] + +properties: + divider: + type: int + required: true + description: Divide root clock by this 8-bits value + + "#clock-cells": + const: 0 diff --git a/dts/bindings/clock/bflb,bl60x-clock-controller.yaml b/dts/bindings/clock/bflb,bl60x-clock-controller.yaml new file mode 100644 index 00000000000..a9c02425a9f --- /dev/null +++ b/dts/bindings/clock/bflb,bl60x-clock-controller.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +description: Bouffalolab BL60x Clock Controller + +compatible: "bflb,bl60x-clock-controller" + +include: [base.yaml, clock-controller.yaml] + +properties: + "#clock-cells": + const: 1 + +clock-cells: + - id diff --git a/dts/bindings/clock/bflb,bl60x-pll.yaml b/dts/bindings/clock/bflb,bl60x-pll.yaml new file mode 100644 index 00000000000..a1c9719ea3f --- /dev/null +++ b/dts/bindings/clock/bflb,bl60x-pll.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +description: The BL60x PLL + +compatible: "bflb,bl60x-pll" + +include: [base.yaml, clock-controller.yaml] + +properties: + clocks: + type: phandle-array + required: true + description: source + + "#clock-cells": + const: 1 + +clock-cells: + - select diff --git a/dts/bindings/clock/bflb,bl60x-root-clk.yaml b/dts/bindings/clock/bflb,bl60x-root-clk.yaml new file mode 100644 index 00000000000..9e1bb7948f6 --- /dev/null +++ b/dts/bindings/clock/bflb,bl60x-root-clk.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +description: | + The BL60x Root Clock + +compatible: "bflb,bl60x-root-clk" + +include: [base.yaml, clock-controller.yaml] + +properties: + clocks: + type: phandle-array + required: true + description: source + + divider: + type: int + required: true + description: Divide source clock by this 8-bits value. Typically 1. + + "#clock-cells": + const: 0 diff --git a/dts/bindings/clock/bflb,clock-controller.yaml b/dts/bindings/clock/bflb,clock-controller.yaml new file mode 100644 index 00000000000..c0a63a06656 --- /dev/null +++ b/dts/bindings/clock/bflb,clock-controller.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +description: Bouffalolab Clock Controller for Drivers + +compatible: "bflb,clock-controller" + +include: [base.yaml, clock-controller.yaml] diff --git a/dts/bindings/clock/nordic,nrf-auxpll.yaml b/dts/bindings/clock/nordic,nrf-auxpll.yaml index 6eec285f39e..9555995f0b4 100644 --- a/dts/bindings/clock/nordic,nrf-auxpll.yaml +++ b/dts/bindings/clock/nordic,nrf-auxpll.yaml @@ -43,7 +43,7 @@ properties: required: true description: | Value used to set the fractional PLL divider ratio (can be set between - divider ratios 4 to 5). Valid values range from 0 to 65535. + divider ratios 4 to 5). Valid values shown in dt-bindings/clock/nrf-auxpll.h. nordic,out-div: type: int diff --git a/dts/vendor/nordic/nrf54h20.dtsi b/dts/vendor/nordic/nrf54h20.dtsi index 1ff7fae23cd..c81178f6a12 100644 --- a/dts/vendor/nordic/nrf54h20.dtsi +++ b/dts/vendor/nordic/nrf54h20.dtsi @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -571,7 +572,7 @@ clocks = <&hfxo>; #clock-cells = <0>; nordic,ficrs = <&ficr NRF_FICR_TRIM_GLOBAL_CANPLL_TRIM_CTUNE>; - nordic,frequency = <0>; + nordic,frequency = ; nordic,out-div = <2>; nordic,out-drive = <0>; nordic,current-tune = <6>; diff --git a/include/zephyr/dt-bindings/clock/bflb_bl60x_clock.h b/include/zephyr/dt-bindings/clock/bflb_bl60x_clock.h new file mode 100644 index 00000000000..a08db84b082 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/bflb_bl60x_clock.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 MASSDRIVER EI (massdriver.space) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_BFLB_BL60X_CLOCK_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_BFLB_BL60X_CLOCK_H_ + +#include "bflb_clock_common.h" + +#define BL60X_CLKID_CLK_ROOT BFLB_CLKID_CLK_ROOT +#define BL60X_CLKID_CLK_RC32M BFLB_CLKID_CLK_RC32M +#define BL60X_CLKID_CLK_CRYSTAL BFLB_CLKID_CLK_CRYSTAL +#define BL60X_CLKID_CLK_BCLK BFLB_CLKID_CLK_BCLK +#define BL60X_CLKID_CLK_PLL 4 + +#define BL60X_PLL_48MHz 0 +#define BL60X_PLL_120MHz 1 +#define BL60X_PLL_160MHz 2 +#define BL60X_PLL_192MHz 3 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_BFLB_BL60X_CLOCK_H_ */ diff --git a/include/zephyr/dt-bindings/clock/bflb_clock_common.h b/include/zephyr/dt-bindings/clock/bflb_clock_common.h new file mode 100644 index 00000000000..203bac3d434 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/bflb_clock_common.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 MASSDRIVER EI (massdriver.space) + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_BFLB_CLOCK_COMMON_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_BFLB_CLOCK_COMMON_H_ + +#define BFLB_CLKID_CLK_ROOT 0 +#define BFLB_CLKID_CLK_RC32M 1 +#define BFLB_CLKID_CLK_CRYSTAL 2 +#define BFLB_CLKID_CLK_BCLK 3 + +#define BFLB_FORCE_ROOT_RC32M 10 +#define BFLB_FORCE_ROOT_CRYSTAL 11 +#define BFLB_FORCE_ROOT_PLL 12 + +#define BFLB_RC32M_FREQUENCY 32000000 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_BFLB_CLOCK_COMMON_H_ */ diff --git a/include/zephyr/dt-bindings/clock/nrf-auxpll.h b/include/zephyr/dt-bindings/clock/nrf-auxpll.h new file mode 100644 index 00000000000..a07c9699759 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/nrf-auxpll.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NRF_AUXPLL_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NRF_AUXPLL_H_ + +#define NRF_AUXPLL_FREQ_DIV_MIN 0 +#define NRF_AUXPLL_FREQ_DIV_AUDIO_44K1 15309 +#define NRF_AUXPLL_FREQ_DIV_USB24M 32768 +#define NRF_AUXPLL_FREQ_DIV_AUDIO_48K 39845 +#define NRF_AUXPLL_FREQ_DIV_MAX 65535 + +#endif /* #define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NRF_AUXPLL_H_ */ diff --git a/tests/drivers/clock_control/nrf_clock_control/src/main.c b/tests/drivers/clock_control/nrf_clock_control/src/main.c index 87778bbaae5..8db19d38200 100644 --- a/tests/drivers/clock_control/nrf_clock_control/src/main.c +++ b/tests/drivers/clock_control/nrf_clock_control/src/main.c @@ -9,6 +9,7 @@ #include #include #include +#include struct test_clk_context { const struct device *clk_dev; @@ -16,6 +17,7 @@ struct test_clk_context { size_t clk_specs_size; }; +#if defined(CONFIG_CLOCK_CONTROL_NRF_HSFLL_LOCAL) const struct nrf_clock_spec test_clk_specs_hsfll[] = { { .frequency = MHZ(128), @@ -33,6 +35,7 @@ const struct nrf_clock_spec test_clk_specs_hsfll[] = { .precision = NRF_CLOCK_CONTROL_PRECISION_DEFAULT, }, }; +#endif #if CONFIG_BOARD_NRF54H20DK_NRF54H20_CPUAPP const struct nrf_clock_spec test_clk_specs_fll16m[] = { @@ -99,6 +102,7 @@ static const struct test_clk_context cpurad_hsfll_test_clk_contexts[] = { }; #endif +#if defined(CONFIG_CLOCK_CONTROL_NRF_HSFLL_GLOBAL) const struct nrf_clock_spec test_clk_specs_global_hsfll[] = { { .frequency = MHZ(320), @@ -121,7 +125,9 @@ static const struct test_clk_context global_hsfll_test_clk_contexts[] = { .clk_specs_size = ARRAY_SIZE(test_clk_specs_global_hsfll), }, }; +#endif +#if defined(CONFIG_CLOCK_CONTROL_NRF_LFCLK) const struct nrf_clock_spec test_clk_specs_lfclk[] = { { .frequency = 32768, @@ -147,6 +153,44 @@ static const struct test_clk_context lfclk_test_clk_contexts[] = { .clk_specs_size = ARRAY_SIZE(test_clk_specs_lfclk), }, }; +#endif + +#if defined(CONFIG_CLOCK_CONTROL_NRF_AUXPLL) + +#define AUXPLL_COMPAT nordic_nrf_auxpll +#define AUXPLL_NODE DT_INST(0, AUXPLL_COMPAT) +#define AUXPLL_FREQ DT_PROP(AUXPLL_NODE, nordic_frequency) + +/* Gets selected AUXPLL DIV and selects the expected frequency */ +#if AUXPLL_FREQ == NRF_AUXPLL_FREQUENCY_DIV_MIN +#define AUXPLL_FREQ_OUT 80000000 +#elif AUXPLL_FREQ == NRF_AUXPLL_FREQ_DIV_AUDIO_44K1 +#define AUXPLL_FREQ_OUT 11289591 +#elif AUXPLL_FREQ == NRF_AUXPLL_FREQ_DIV_USB_24M +#define AUXPLL_FREQ_OUT 24000000 +#elif AUXPLL_FREQ == NRF_AUXPLL_FREQ_DIV_AUDIO_48K +#define AUXPLL_FREQ_OUT 12287963 +#else +/*No use case for NRF_AUXPLL_FREQ_DIV_MAX or others yet*/ +#error "Unsupported AUXPLL frequency selection" +#endif + +const struct nrf_clock_spec test_clk_specs_auxpll[] = { + { + .frequency = AUXPLL_FREQ_OUT, + .accuracy = 0, + .precision = NRF_CLOCK_CONTROL_PRECISION_DEFAULT, + }, +}; + +static const struct test_clk_context auxpll_test_clk_contexts[] = { + { + .clk_dev = DEVICE_DT_GET(AUXPLL_NODE), + .clk_specs = test_clk_specs_auxpll, + .clk_specs_size = ARRAY_SIZE(test_clk_specs_auxpll), + }, +}; +#endif static void test_request_release_clock_spec(const struct device *clk_dev, const struct nrf_clock_spec *clk_spec) @@ -266,18 +310,23 @@ ZTEST(nrf2_clock_control, test_cpurad_hsfll_control) } #endif -ZTEST(nrf2_clock_control, test_lfclk_control) -{ - TC_PRINT("LFCLK test\n"); - test_clock_control_request(lfclk_test_clk_contexts, ARRAY_SIZE(lfclk_test_clk_contexts)); -} + +#if defined(CONFIG_CLOCK_CONTROL_NRF_HSFLL_GLOBAL) ZTEST(nrf2_clock_control, test_global_hsfll_control) { TC_PRINT("Global HSFLL test\n"); test_clock_control_request(global_hsfll_test_clk_contexts, ARRAY_SIZE(global_hsfll_test_clk_contexts)); } +#endif + +#if defined(CONFIG_CLOCK_CONTROL_NRF_LFCLK) +ZTEST(nrf2_clock_control, test_lfclk_control) +{ + TC_PRINT("LFCLK test\n"); + test_clock_control_request(lfclk_test_clk_contexts, ARRAY_SIZE(lfclk_test_clk_contexts)); +} ZTEST(nrf2_clock_control, test_safe_request_cancellation) { @@ -303,6 +352,16 @@ ZTEST(nrf2_clock_control, test_safe_request_cancellation) TC_PRINT("Clock control safe cancellation return value: %d\n", ret); zassert_between_inclusive(ret, ONOFF_STATE_ON, ONOFF_STATE_TO_ON); } +#endif + +#if defined(CONFIG_CLOCK_CONTROL_NRF_AUXPLL) +ZTEST(nrf2_clock_control, test_auxpll_control) +{ + TC_PRINT("AUXPLL control test\n"); + test_clock_control_request(auxpll_test_clk_contexts, + ARRAY_SIZE(auxpll_test_clk_contexts)); +} +#endif static void *setup(void) {