From d7ab187d0e99d66b099b90942df2d97b01cf8351 Mon Sep 17 00:00:00 2001 From: Michal Frankiewicz Date: Mon, 13 Oct 2025 11:49:37 +0200 Subject: [PATCH 1/4] [nrf fromlist] drivers: clock_control: Separated nrf hfclk shim from nrf clock shim. Separated clock_control_nrf_hfclk shim from clock_control_nrf shim. Upstream PR #: 97195 Signed-off-by: Michal Frankiewicz --- .../nrf54l_10_15_cpuapp_common.dtsi | 4 + .../bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi | 4 + .../nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts | 4 + .../nrf54lm20bsim_nrf54lm20a_cpuapp.dts | 4 + .../nrf54l_05_10_15_cpuapp_common.dtsi | 4 + .../nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi | 4 + .../panb611evb_nrf54l15_cpuapp_common.dtsi | 4 + .../raytac_an54l15q_db_cpuapp_common.dtsi | 4 + .../ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts | 4 + drivers/clock_control/CMakeLists.txt | 4 + drivers/clock_control/Kconfig.nrf | 15 + drivers/clock_control/clock_control_nrf.c | 49 +- .../clock_control/clock_control_nrf_common.c | 41 ++ .../clock_control/clock_control_nrf_common.h | 20 + .../clock_control/clock_control_nrf_hfclk.c | 451 ++++++++++++++++++ .../clock_control_nrf_irq_handlers.ld | 2 + dts/arm/nordic/nrf51822.dtsi | 7 + dts/arm/nordic/nrf52805.dtsi | 7 + dts/arm/nordic/nrf52810.dtsi | 7 + dts/arm/nordic/nrf52811.dtsi | 7 + dts/arm/nordic/nrf52820.dtsi | 7 + dts/arm/nordic/nrf52832.dtsi | 7 + dts/arm/nordic/nrf52833.dtsi | 7 + dts/arm/nordic/nrf52840.dtsi | 7 + .../nordic/nrf5340_cpuapp_peripherals.dtsi | 7 + dts/arm/nordic/nrf5340_cpunet.dtsi | 7 + dts/arm/nordic/nrf91_peripherals.dtsi | 7 + .../clock/nordic,nrf-clock-hfclk.yaml | 15 + dts/bindings/clock/nordic,nrf-clock-xo.yaml | 15 + dts/vendor/nordic/nrf54l_05_10_15.dtsi | 7 + dts/vendor/nordic/nrf54lm20a.dtsi | 7 + modules/hal_nordic/nrfx/CMakeLists.txt | 1 + .../clock_control_api/src/nrf_device_subsys.h | 20 + 33 files changed, 715 insertions(+), 45 deletions(-) create mode 100644 drivers/clock_control/clock_control_nrf_common.c create mode 100644 drivers/clock_control/clock_control_nrf_common.h create mode 100644 drivers/clock_control/clock_control_nrf_hfclk.c create mode 100644 drivers/clock_control/clock_control_nrf_irq_handlers.ld create mode 100644 dts/bindings/clock/nordic,nrf-clock-hfclk.yaml create mode 100644 dts/bindings/clock/nordic,nrf-clock-xo.yaml diff --git a/boards/ezurio/bl54l15_dvk/nrf54l_10_15_cpuapp_common.dtsi b/boards/ezurio/bl54l15_dvk/nrf54l_10_15_cpuapp_common.dtsi index 8671beb29cf..3e2b19c68a1 100644 --- a/boards/ezurio/bl54l15_dvk/nrf54l_10_15_cpuapp_common.dtsi +++ b/boards/ezurio/bl54l15_dvk/nrf54l_10_15_cpuapp_common.dtsi @@ -92,6 +92,10 @@ status = "okay"; }; +&xo { + status = "okay"; +}; + &spi00 { status = "okay"; cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; diff --git a/boards/ezurio/bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi b/boards/ezurio/bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi index e630abab4a9..fd78f2d0899 100644 --- a/boards/ezurio/bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi +++ b/boards/ezurio/bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi @@ -92,6 +92,10 @@ status = "okay"; }; +&xo { + status = "okay"; +}; + &spi00 { status = "okay"; cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; diff --git a/boards/native/nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts b/boards/native/nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts index 35a052b7808..0640c6b2adc 100644 --- a/boards/native/nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts +++ b/boards/native/nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts @@ -135,3 +135,7 @@ &clock { status = "okay"; }; + +&xo { + status = "okay"; +}; diff --git a/boards/native/nrf_bsim/nrf54lm20bsim_nrf54lm20a_cpuapp.dts b/boards/native/nrf_bsim/nrf54lm20bsim_nrf54lm20a_cpuapp.dts index bd424f2e15e..e65262bae05 100644 --- a/boards/native/nrf_bsim/nrf54lm20bsim_nrf54lm20a_cpuapp.dts +++ b/boards/native/nrf_bsim/nrf54lm20bsim_nrf54lm20a_cpuapp.dts @@ -142,3 +142,7 @@ &clock { status = "okay"; }; + +&xo { + status = "okay"; +}; diff --git a/boards/nordic/nrf54l15dk/nrf54l_05_10_15_cpuapp_common.dtsi b/boards/nordic/nrf54l15dk/nrf54l_05_10_15_cpuapp_common.dtsi index f3c1d924d27..dcae050111a 100644 --- a/boards/nordic/nrf54l15dk/nrf54l_05_10_15_cpuapp_common.dtsi +++ b/boards/nordic/nrf54l15dk/nrf54l_05_10_15_cpuapp_common.dtsi @@ -101,6 +101,10 @@ status = "okay"; }; +&xo { + status = "okay"; +}; + &gpregret1 { status = "okay"; diff --git a/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi b/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi index 0ee33f1fd45..70ee2d89a05 100644 --- a/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi +++ b/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi @@ -130,6 +130,10 @@ status = "okay"; }; +&xo { + status = "okay"; +}; + &ieee802154 { status = "okay"; }; diff --git a/boards/panasonic/panb611evb/panb611evb_nrf54l15_cpuapp_common.dtsi b/boards/panasonic/panb611evb/panb611evb_nrf54l15_cpuapp_common.dtsi index ba978a54128..a4e0b7add0f 100644 --- a/boards/panasonic/panb611evb/panb611evb_nrf54l15_cpuapp_common.dtsi +++ b/boards/panasonic/panb611evb/panb611evb_nrf54l15_cpuapp_common.dtsi @@ -91,6 +91,10 @@ status = "okay"; }; +&xo { + status = "okay"; +}; + &spi00 { status = "okay"; cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; diff --git a/boards/raytac/an54l15q_db/raytac_an54l15q_db_cpuapp_common.dtsi b/boards/raytac/an54l15q_db/raytac_an54l15q_db_cpuapp_common.dtsi index 952a8f6d7ae..bf42a5b3a1f 100644 --- a/boards/raytac/an54l15q_db/raytac_an54l15q_db_cpuapp_common.dtsi +++ b/boards/raytac/an54l15q_db/raytac_an54l15q_db_cpuapp_common.dtsi @@ -92,6 +92,10 @@ status = "okay"; }; +&xo { + status = "okay"; +}; + &spi00 { status = "okay"; cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; diff --git a/boards/we/ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts b/boards/we/ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts index 2380fcd5b8d..ef7455d3a5d 100644 --- a/boards/we/ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts +++ b/boards/we/ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts @@ -99,6 +99,10 @@ status = "okay"; }; +&xo { + status = "okay"; +}; + &spi00 { status = "okay"; diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index f649af13c46..975ec7bdcd5 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -60,6 +60,8 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_HSFLL_LOCAL clock_cont zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL clock_control_nrf_iron_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_NRF_HFCLK clock_control_nrf_hfclk.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_COMMON clock_control_nrf_common.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL60X clock_control_bl60x.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL61X clock_control_bl61x.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL70X clock_control_bl70x.c) @@ -129,3 +131,5 @@ 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_WCH_RCC clock_control_wch_rcc.c) + +zephyr_linker_sources(SECTIONS clock_control_nrf_irq_handlers.ld) diff --git a/drivers/clock_control/Kconfig.nrf b/drivers/clock_control/Kconfig.nrf index a051ebd62b0..f8622eab5ab 100644 --- a/drivers/clock_control/Kconfig.nrf +++ b/drivers/clock_control/Kconfig.nrf @@ -323,6 +323,21 @@ config CLOCK_CONTROL_NRF_LFCLK_CLOCK_TIMEOUT_MS endif # CLOCK_CONTROL_NRF_LFCLK +config CLOCK_CONTROL_NRF_COMMON + bool + +config CLOCK_CONTROL_NRF_HFCLK + bool "NRF HFCLK driver support" + depends on DT_HAS_NORDIC_NRF_CLOCK_HFCLK_ENABLED + select CLOCK_CONTROL_NRF_COMMON + default y + +config CLOCK_CONTROL_NRF_XO + bool "NRF XO driver support" + depends on DT_HAS_NORDIC_NRF_CLOCK_XO_ENABLED + select CLOCK_CONTROL_NRF_COMMON + default y + config CLOCK_CONTROL_NRF_AUXPLL bool "nRF Auxiliary PLL driver" default y diff --git a/drivers/clock_control/clock_control_nrf.c b/drivers/clock_control/clock_control_nrf.c index caa7aadff61..18481b6cfaa 100644 --- a/drivers/clock_control/clock_control_nrf.c +++ b/drivers/clock_control/clock_control_nrf.c @@ -10,6 +10,7 @@ #include #include #include "nrf_clock_calibration.h" +#include "clock_control_nrf_common.h" #include #include #include @@ -317,7 +318,7 @@ static void hfclk_start(void) hf_start_tstamp = k_uptime_get(); } - nrfx_clock_hfclk_start(); + nrfx_clock_start(NRF_CLOCK_DOMAIN_HFCLK); } static void hfclk_stop(void) @@ -326,7 +327,7 @@ static void hfclk_stop(void) hf_stop_tstamp = k_uptime_get(); } - nrfx_clock_hfclk_stop(); + nrfx_clock_stop(NRF_CLOCK_DOMAIN_HFCLK); } #if NRF_CLOCK_HAS_HFCLK24M @@ -421,47 +422,6 @@ static void generic_hfclk_stop(void) irq_unlock(key); } - -void z_nrf_clock_bt_ctlr_hf_request(void) -{ - if (atomic_or(&hfclk_users, HF_USER_BT) & HF_USER_GENERIC) { - /* generic request already activated clock. */ - return; - } - - hfclk_start(); -} - -void z_nrf_clock_bt_ctlr_hf_release(void) -{ - /* It's not enough to use only atomic_and() here for synchronization, - * see the explanation in generic_hfclk_stop(). - */ - unsigned int key = irq_lock(); - - hfclk_users &= ~HF_USER_BT; - /* Skip stopping if generic is still requesting the clock. */ - if (!(hfclk_users & HF_USER_GENERIC)) { - struct nrf_clock_control_sub_data *sub_data = - get_sub_data(CLOCK_DEVICE, CLOCK_CONTROL_NRF_TYPE_HFCLK); - - /* State needs to be set to OFF as BT API does not call stop API which - * normally setting this state. - */ - sub_data->flags = CLOCK_CONTROL_STATUS_OFF; - hfclk_stop(); - } - - irq_unlock(key); -} - -#if DT_NODE_EXISTS(DT_NODELABEL(hfxo)) -uint32_t z_nrf_clock_bt_ctlr_hf_get_startup_time_us(void) -{ - return DT_PROP(DT_NODELABEL(hfxo), startup_time_us); -} -#endif - static int stop(const struct device *dev, clock_control_subsys_t subsys, uint32_t ctx) { @@ -812,8 +772,7 @@ static int clk_init(const struct device *dev) IRQ_CONNECT(LFRC_IRQn, DT_INST_IRQ(0, priority), nrfx_isr, nrfx_power_clock_irq_handler, 0); #endif - IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), - nrfx_isr, nrfx_power_clock_irq_handler, 0); + clock_control_nrf_common_connect_irq(); nrfx_err = nrfx_clock_init(clock_event_handler); if (nrfx_err != NRFX_SUCCESS) { diff --git a/drivers/clock_control/clock_control_nrf_common.c b/drivers/clock_control/clock_control_nrf_common.c new file mode 100644 index 00000000000..0d379563d18 --- /dev/null +++ b/drivers/clock_control/clock_control_nrf_common.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "clock_control_nrf_common.h" +#include +#include + +#if NRFX_CHECK(NRFX_POWER_ENABLED) +#include +#endif + +#define DT_DRV_COMPAT nordic_nrf_clock + +static bool irq_connected; + +static void clock_irq_handler(void) +{ +#if NRFX_CHECK(NRFX_POWER_ENABLED) + nrfx_power_irq_handler(); +#endif + + STRUCT_SECTION_FOREACH(clock_control_nrf_irq_handler, irq) { + irq->handler(); + } + + /* temporary fix, it will be removed when all the clocks are moved to their files */ + nrfx_clock_irq_handler(); +} + +void clock_control_nrf_common_connect_irq(void) +{ + if (irq_connected) { + return; + } + irq_connected = true; + + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), + nrfx_isr, clock_irq_handler, 0); +} diff --git a/drivers/clock_control/clock_control_nrf_common.h b/drivers/clock_control/clock_control_nrf_common.h new file mode 100644 index 00000000000..26df26dfba4 --- /dev/null +++ b/drivers/clock_control/clock_control_nrf_common.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CLOCK_CONTROL_NRF_COMMON_H__ +#define CLOCK_CONTROL_NRF_COMMON_H__ + +struct clock_control_nrf_irq_handler { + void (*handler)(void); /* Clock interrupt handler */ +}; + +#define CLOCK_CONTROL_NRF_IRQ_HANDLERS_ITERABLE(name, _a) \ + STRUCT_SECTION_ITERABLE(clock_control_nrf_irq_handler, name) = { \ + .handler = _a, \ + } + +void clock_control_nrf_common_connect_irq(void); + +#endif /* CLOCK_CONTROL_NRF_COMMON_H__ */ diff --git a/drivers/clock_control/clock_control_nrf_hfclk.c b/drivers/clock_control/clock_control_nrf_hfclk.c new file mode 100644 index 00000000000..89197873870 --- /dev/null +++ b/drivers/clock_control/clock_control_nrf_hfclk.c @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "nrf_clock_calibration.h" +#include +#include +#include +#include +#include +#include "clock_control_nrf_common.h" + +LOG_MODULE_REGISTER(clock_control_hfclk, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +#define DT_DRV_COMPAT nordic_nrf_clock_hfclk + +#define CLOCK_DEVICE_HFCLK DEVICE_DT_GET(DT_NODELABEL(hfclk)) + +#define CTX_ONOFF BIT(6) +#define CTX_API BIT(7) +#define CTX_MASK (CTX_ONOFF | CTX_API) + +#define STATUS_MASK 0x7 +#define GET_STATUS(flags) (flags & STATUS_MASK) +#define GET_CTX(flags) (flags & CTX_MASK) + +/* Used only by HF clock */ +#define HF_USER_BT BIT(0) +#define HF_USER_GENERIC BIT(1) + +/* Helper logging macros which prepends hfclk name to the log. */ +#ifdef CONFIG_LOG +#define CLOCK_LOG(lvl, dev, ...) \ + LOG_##lvl("%s: " GET_ARG_N(1, __VA_ARGS__), \ + "hfclk" \ + COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__),\ + (), (, GET_ARGS_LESS_N(1, __VA_ARGS__)))) +#else +#define CLOCK_LOG(...) +#endif + +#define ERR(dev, ...) CLOCK_LOG(ERR, dev, __VA_ARGS__) +#define WRN(dev, ...) CLOCK_LOG(WRN, dev, __VA_ARGS__) +#define INF(dev, ...) CLOCK_LOG(INF, dev, __VA_ARGS__) +#define DBG(dev, ...) CLOCK_LOG(DBG, dev, __VA_ARGS__) + +typedef void (*clk_ctrl_func_t)(void); + +typedef struct { + struct onoff_manager mgr; + clock_control_cb_t cb; + void *user_data; + uint32_t flags; +} hfclk_data_t; + +typedef struct { + clk_ctrl_func_t start; /* Clock start function */ + clk_ctrl_func_t stop; /* Clock stop function */ +#ifdef CONFIG_LOG + const char *name; +#endif +} hfclk_config_t; + +static atomic_t hfclk_users; +static uint64_t hf_start_tstamp; +static uint64_t hf_stop_tstamp; + +static int set_starting_state(uint32_t *flags, uint32_t ctx) +{ + int err = 0; + unsigned int key = irq_lock(); + uint32_t current_ctx = GET_CTX(*flags); + + if ((*flags & (STATUS_MASK)) == CLOCK_CONTROL_STATUS_OFF) { + *flags = CLOCK_CONTROL_STATUS_STARTING | ctx; + } else if (current_ctx != ctx) { + err = -EPERM; + } else { + err = -EALREADY; + } + + irq_unlock(key); + + return err; +} + +static int async_start(const struct device *dev, clock_control_cb_t cb, void *user_data, + uint32_t ctx) +{ + int err; + + err = set_starting_state(&((hfclk_data_t *)dev->data)->flags, ctx); + if (err < 0) { + return err; + } + + ((hfclk_data_t *)dev->data)->cb = cb; + ((hfclk_data_t *)dev->data)->user_data = user_data; + + ((hfclk_config_t *)dev->config)->start(); + + return 0; +} + +static int set_off_state(uint32_t *flags, uint32_t ctx) +{ + int err = 0; + unsigned int key = irq_lock(); + uint32_t current_ctx = GET_CTX(*flags); + + if ((current_ctx != 0) && (current_ctx != ctx)) { + err = -EPERM; + } else { + *flags = CLOCK_CONTROL_STATUS_OFF; + } + + irq_unlock(key); + + return err; +} + +static void hfclk_start(void) +{ + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_SHELL)) { + hf_start_tstamp = k_uptime_get(); + } + + nrfx_clock_hfclk_start(); +} + +static void hfclk_stop(void) +{ + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_SHELL)) { + hf_stop_tstamp = k_uptime_get(); + } + + nrfx_clock_hfclk_stop(); +} + +static int stop(const struct device *dev, uint32_t ctx) +{ + int err; + + err = set_off_state(&((hfclk_data_t *)dev->data)->flags, ctx); + if (err < 0) { + return err; + } + + ((hfclk_config_t *)dev->config)->stop(); + + return 0; +} + +static void onoff_started_callback(const struct device *dev, + clock_control_subsys_t sys, + void *user_data) +{ + ARG_UNUSED(sys); + + onoff_notify_fn notify = user_data; + + notify(&((hfclk_data_t *)dev->data)->mgr, 0); +} + +static void onoff_start(struct onoff_manager *mgr, + onoff_notify_fn notify) +{ + int err; + + err = async_start(CLOCK_DEVICE_HFCLK, onoff_started_callback, notify, CTX_ONOFF); + if (err < 0) { + notify(mgr, err); + } +} + +static void onoff_stop(struct onoff_manager *mgr, + onoff_notify_fn notify) +{ + int res; + + res = stop(CLOCK_DEVICE_HFCLK, CTX_ONOFF); + notify(mgr, res); +} + +static void set_on_state(uint32_t *flags) +{ + unsigned int key = irq_lock(); + + *flags = CLOCK_CONTROL_STATUS_ON | GET_CTX(*flags); + irq_unlock(key); +} + +static void clkstarted_handle(const struct device *dev) +{ + clock_control_cb_t callback = ((hfclk_data_t *)dev->data)->cb; + + ((hfclk_data_t *)dev->data)->cb = NULL; + set_on_state(&((hfclk_data_t *)dev->data)->flags); + DBG(dev, "Clock started"); + + if (callback) { + callback(dev, NULL, ((hfclk_data_t *)dev->data)->user_data); + } +} + +static void clock_event_handler(void) +{ + const struct device *dev = CLOCK_DEVICE_HFCLK; + + /* Check needed due to anomaly 201: + * HFCLKSTARTED may be generated twice. + */ + if (GET_STATUS(((hfclk_data_t *)dev->data)->flags) == CLOCK_CONTROL_STATUS_STARTING) { + clkstarted_handle(dev); + } +} + +static void generic_hfclk_start(void) +{ + nrf_clock_hfclk_t type; + bool already_started = false; + unsigned int key = irq_lock(); + + hfclk_users |= HF_USER_GENERIC; + if (hfclk_users & HF_USER_BT) { + (void)nrfx_clock_hfclk_running_check(&type); + if (type == NRF_CLOCK_HFCLK_HIGH_ACCURACY) { + already_started = true; + /* Set on state in case clock interrupt comes and we + * want to avoid handling that. + */ + + set_on_state(&((hfclk_data_t *)CLOCK_DEVICE_HFCLK->data)->flags); + } + } + + irq_unlock(key); + + if (already_started) { + /* Clock already started by z_nrf_clock_bt_ctlr_hf_request */ + clkstarted_handle(CLOCK_DEVICE_HFCLK); + return; + } + + hfclk_start(); +} + +static void generic_hfclk_stop(void) +{ + /* It's not enough to use only atomic_and() here for synchronization, + * as the thread could be preempted right after that function but + * before hfclk_stop() is called and the preempting code could request + * the HFCLK again. Then, the HFCLK would be stopped inappropriately + * and hfclk_user would be left with an incorrect value. + */ + unsigned int key = irq_lock(); + + hfclk_users &= ~HF_USER_GENERIC; + /* Skip stopping if BT is still requesting the clock. */ + if (!(hfclk_users & HF_USER_BT)) { + hfclk_stop(); + } + + irq_unlock(key); +} + +static void blocking_start_callback(const struct device *dev, + clock_control_subsys_t subsys, + void *user_data) +{ + struct k_sem *sem = user_data; + + k_sem_give(sem); +} + +void z_nrf_clock_bt_ctlr_hf_request(void) +{ + if (atomic_or(&hfclk_users, HF_USER_BT) & HF_USER_GENERIC) { + /* generic request already activated clock. */ + return; + } + + hfclk_start(); +} + +void z_nrf_clock_bt_ctlr_hf_release(void) +{ + /* It's not enough to use only atomic_and() here for synchronization, + * see the explanation in generic_hfclk_stop(). + */ + unsigned int key = irq_lock(); + + hfclk_users &= ~HF_USER_BT; + /* Skip stopping if generic is still requesting the clock. */ + if (!(hfclk_users & HF_USER_GENERIC)) { + + /* State needs to be set to OFF as BT API does not call stop API which + * normally setting this state. + */ + ((hfclk_data_t *)CLOCK_DEVICE_HFCLK->data)->flags = CLOCK_CONTROL_STATUS_OFF; + hfclk_stop(); + } + + irq_unlock(key); +} + +#if DT_NODE_EXISTS(DT_NODELABEL(hfxo)) +uint32_t z_nrf_clock_bt_ctlr_hf_get_startup_time_us(void) +{ + return DT_PROP(DT_NODELABEL(hfxo), startup_time_us); +} +#endif + +static int api_start(const struct device *dev, clock_control_subsys_t subsys, + clock_control_cb_t cb, void *user_data) +{ + ARG_UNUSED(subsys); + + return async_start(dev, cb, user_data, CTX_API); +} + +static int api_blocking_start(const struct device *dev, + clock_control_subsys_t subsys) +{ + struct k_sem sem = Z_SEM_INITIALIZER(sem, 0, 1); + int err; + + if (!IS_ENABLED(CONFIG_MULTITHREADING)) { + return -ENOTSUP; + } + + err = api_start(dev, subsys, blocking_start_callback, &sem); + if (err < 0) { + return err; + } + + return k_sem_take(&sem, K_MSEC(500)); +} + +static int api_stop(const struct device *dev, clock_control_subsys_t subsys) +{ + ARG_UNUSED(subsys); + + return stop(dev, CTX_API); +} + +static enum clock_control_status api_get_status(const struct device *dev, + clock_control_subsys_t subsys) +{ + ARG_UNUSED(subsys); + + return GET_STATUS(((hfclk_data_t *)dev->data)->flags); +} + +static int api_request(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + hfclk_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_request(&dev_data->mgr, cli); +} + +static int api_release(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + hfclk_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_release(&dev_data->mgr); +} + +static int api_cancel_or_release(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + hfclk_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_cancel_or_release(&dev_data->mgr, cli); +} + +static int clk_init(const struct device *dev) +{ + nrfx_err_t nrfx_err; + int err; + static const struct onoff_transitions transitions = { + .start = onoff_start, + .stop = onoff_stop + }; + + clock_control_nrf_common_connect_irq(); + + nrfx_err = nrfx_clock_hfclk_init(clock_event_handler); + if (nrfx_err != NRFX_SUCCESS) { + return -EIO; + } + + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { + hfclk_data_t *data = ((hfclk_data_t *)dev->data); + + z_nrf_clock_calibration_init(&data->mgr); + } + + err = onoff_manager_init(&((hfclk_data_t *)dev->data)->mgr, + &transitions); + if (err < 0) { + return err; + } + + ((hfclk_data_t *)dev->data)->flags = CLOCK_CONTROL_STATUS_OFF; + + return 0; +} + +CLOCK_CONTROL_NRF_IRQ_HANDLERS_ITERABLE(clock_control_nrf_hfclk, + &nrfx_clock_hfclk_irq_handler); + +static DEVICE_API(nrf_clock_control, clock_control_api) = { + .std_api = { + .on = api_blocking_start, + .off = api_stop, + .async_on = api_start, + .get_status = api_get_status, + }, + .request = api_request, + .release = api_release, + .cancel_or_release = api_cancel_or_release, +}; + +static hfclk_data_t data; + +static const hfclk_config_t config = { + .start = generic_hfclk_start, + .stop = generic_hfclk_stop, + IF_ENABLED(CONFIG_LOG, (.name = "hfclk",)) +}; + +DEVICE_DT_DEFINE(DT_NODELABEL(hfclk), clk_init, NULL, + &data, &config, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &clock_control_api); diff --git a/drivers/clock_control/clock_control_nrf_irq_handlers.ld b/drivers/clock_control/clock_control_nrf_irq_handlers.ld new file mode 100644 index 00000000000..0836263fa57 --- /dev/null +++ b/drivers/clock_control/clock_control_nrf_irq_handlers.ld @@ -0,0 +1,2 @@ +#include +ITERABLE_SECTION_ROM(clock_control_nrf_irq_handler, Z_LINK_ITERABLE_SUBALIGN) diff --git a/dts/arm/nordic/nrf51822.dtsi b/dts/arm/nordic/nrf51822.dtsi index cbfef90faa5..1d2bce6ed8d 100644 --- a/dts/arm/nordic/nrf51822.dtsi +++ b/dts/arm/nordic/nrf51822.dtsi @@ -73,6 +73,13 @@ status = "okay"; }; + hfclk: hfclk@40000000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + nrf_mpu: nrf-mpu@40000000 { compatible = "nordic,nrf-mpu"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52805.dtsi b/dts/arm/nordic/nrf52805.dtsi index 2134605c9f8..a0f37359d56 100644 --- a/dts/arm/nordic/nrf52805.dtsi +++ b/dts/arm/nordic/nrf52805.dtsi @@ -61,6 +61,13 @@ status = "okay"; }; + hfclk: hfclk@40000000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52810.dtsi b/dts/arm/nordic/nrf52810.dtsi index 6e09220e78b..32315026121 100644 --- a/dts/arm/nordic/nrf52810.dtsi +++ b/dts/arm/nordic/nrf52810.dtsi @@ -65,6 +65,13 @@ status = "okay"; }; + hfclk: hfclk@40000000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52811.dtsi b/dts/arm/nordic/nrf52811.dtsi index 12d0a0ea4d6..91258f52ea1 100644 --- a/dts/arm/nordic/nrf52811.dtsi +++ b/dts/arm/nordic/nrf52811.dtsi @@ -69,6 +69,13 @@ status = "okay"; }; + hfclk: hfclk@40000000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52820.dtsi b/dts/arm/nordic/nrf52820.dtsi index d15fbb2ae4e..8f68df4776e 100644 --- a/dts/arm/nordic/nrf52820.dtsi +++ b/dts/arm/nordic/nrf52820.dtsi @@ -69,6 +69,13 @@ status = "okay"; }; + hfclk: hfclk@40000000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52832.dtsi b/dts/arm/nordic/nrf52832.dtsi index eef2297c43b..70c0b4a8fb7 100644 --- a/dts/arm/nordic/nrf52832.dtsi +++ b/dts/arm/nordic/nrf52832.dtsi @@ -65,6 +65,13 @@ status = "okay"; }; + hfclk: hfclk@40000000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52833.dtsi b/dts/arm/nordic/nrf52833.dtsi index 1b3620aa01c..1b77bf4f94b 100644 --- a/dts/arm/nordic/nrf52833.dtsi +++ b/dts/arm/nordic/nrf52833.dtsi @@ -69,6 +69,13 @@ status = "okay"; }; + hfclk: hfclk@40000000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52840.dtsi b/dts/arm/nordic/nrf52840.dtsi index f19383ba7e7..78e07e8beb3 100644 --- a/dts/arm/nordic/nrf52840.dtsi +++ b/dts/arm/nordic/nrf52840.dtsi @@ -65,6 +65,13 @@ status = "okay"; }; + hfclk: hfclk@40000000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi index 7021b7eedeb..9c51df186c2 100644 --- a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi +++ b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi @@ -68,6 +68,13 @@ clock: clock@5000 { status = "okay"; }; +hfclk: hfclk@5000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x5000 0x1000>; + interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; +}; + power: power@5000 { compatible = "nordic,nrf-power"; reg = <0x5000 0x1000>; diff --git a/dts/arm/nordic/nrf5340_cpunet.dtsi b/dts/arm/nordic/nrf5340_cpunet.dtsi index be0fad16d66..92d33340952 100644 --- a/dts/arm/nordic/nrf5340_cpunet.dtsi +++ b/dts/arm/nordic/nrf5340_cpunet.dtsi @@ -62,6 +62,13 @@ status = "okay"; }; + hfclk: hfclk@41005000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x41005000 0x1000>; + interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@41005000 { compatible = "nordic,nrf-power"; reg = <0x41005000 0x1000>; diff --git a/dts/arm/nordic/nrf91_peripherals.dtsi b/dts/arm/nordic/nrf91_peripherals.dtsi index 476f8415853..66b817599e4 100644 --- a/dts/arm/nordic/nrf91_peripherals.dtsi +++ b/dts/arm/nordic/nrf91_peripherals.dtsi @@ -344,6 +344,13 @@ clock: clock@5000 { status = "okay"; }; +hfclk: hfclk@5000 { + compatible = "nordic,nrf-clock-hfclk"; + reg = <0x5000 0x1000>; + interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; +}; + power: power@5000 { compatible = "nordic,nrf-power"; reg = <0x5000 0x1000>; diff --git a/dts/bindings/clock/nordic,nrf-clock-hfclk.yaml b/dts/bindings/clock/nordic,nrf-clock-hfclk.yaml new file mode 100644 index 00000000000..371acddc982 --- /dev/null +++ b/dts/bindings/clock/nordic,nrf-clock-hfclk.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Nordic nRF hfclk clock control node + +compatible: "nordic,nrf-clock-hfclk" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/dts/bindings/clock/nordic,nrf-clock-xo.yaml b/dts/bindings/clock/nordic,nrf-clock-xo.yaml new file mode 100644 index 00000000000..3032d84f638 --- /dev/null +++ b/dts/bindings/clock/nordic,nrf-clock-xo.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Nordic nRF xo clock control node + +compatible: "nordic,nrf-clock-xo" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/dts/vendor/nordic/nrf54l_05_10_15.dtsi b/dts/vendor/nordic/nrf54l_05_10_15.dtsi index 95c27375038..65fb42a5621 100644 --- a/dts/vendor/nordic/nrf54l_05_10_15.dtsi +++ b/dts/vendor/nordic/nrf54l_05_10_15.dtsi @@ -639,6 +639,13 @@ status = "disabled"; }; + xo: xo@10e000 { + compatible = "nordic,nrf-clock-xo"; + reg = <0x10e000 0x1000>; + interrupts = <261 NRF_DEFAULT_IRQ_PRIORITY>; + status = "disabled"; + }; + power: power@10e000 { compatible = "nordic,nrf-power"; reg = <0x10e000 0x1000>; diff --git a/dts/vendor/nordic/nrf54lm20a.dtsi b/dts/vendor/nordic/nrf54lm20a.dtsi index d1e6d1b0d28..02268bcdbab 100644 --- a/dts/vendor/nordic/nrf54lm20a.dtsi +++ b/dts/vendor/nordic/nrf54lm20a.dtsi @@ -791,6 +791,13 @@ status = "disabled"; }; + xo: xo@10e000 { + compatible = "nordic,nrf-clock-xo"; + reg = <0x10e000 0x1000>; + interrupts = <261 NRF_DEFAULT_IRQ_PRIORITY>; + status = "disabled"; + }; + power: power@10e000 { compatible = "nordic,nrf-power"; reg = <0x10e000 0x1000>; diff --git a/modules/hal_nordic/nrfx/CMakeLists.txt b/modules/hal_nordic/nrfx/CMakeLists.txt index 770e1798019..53b7d95f500 100644 --- a/modules/hal_nordic/nrfx/CMakeLists.txt +++ b/modules/hal_nordic/nrfx/CMakeLists.txt @@ -138,6 +138,7 @@ zephyr_library_sources_ifdef(CONFIG_NRFX_PRS ${SRC_DIR}/prs/nrfx_prs.c) zephyr_library_sources_ifdef(CONFIG_NRFX_ADC ${SRC_DIR}/nrfx_adc.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock.c) +zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_hfclk.c) zephyr_library_sources_ifdef(CONFIG_NRFX_COMP ${SRC_DIR}/nrfx_comp.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CRACEN ${SRC_DIR}/nrfx_cracen.c) zephyr_library_sources_ifdef(CONFIG_NRFX_DPPI ${SRC_DIR}/nrfx_dppi.c) diff --git a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h index 47a3060d630..904d74a6cea 100644 --- a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h +++ b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h @@ -7,11 +7,14 @@ #include "device_subsys.h" #include +#if NRF_CLOCK_HAS_XO || !defined(CONFIG_SOC_NRF52832) static const struct device_subsys_data subsys_data[] = { +#if NRF_CLOCK_HAS_XO { .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, .startup_us = CONFIG_TEST_NRF_HF_STARTUP_TIME_US }, +#endif /* NRF_CLOCK_HAS_XO */ #ifndef CONFIG_SOC_NRF52832 /* On nrf52832 LF clock cannot be stopped because it leads * to RTC COUNTER register reset and that is unexpected by @@ -24,11 +27,28 @@ static const struct device_subsys_data subsys_data[] = { } #endif /* !CONFIG_SOC_NRF52832 */ }; +#endif /* NRF_CLOCK_HAS_XO || !defined(CONFIG_SOC_NRF52832) */ + +static const struct device_subsys_data subsys_data_hfclk[] = { + { + .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, + .startup_us = CONFIG_TEST_NRF_HF_STARTUP_TIME_US + } +}; static const struct device_data devices[] = { +#if NRF_CLOCK_HAS_HFCLK + { + .dev = DEVICE_DT_GET_ONE(nordic_nrf_clock_hfclk), + .subsys_data = subsys_data_hfclk, + .subsys_cnt = ARRAY_SIZE(subsys_data_hfclk) + }, +#endif /* NRF_CLOCK_HAS_HFCLK */ +#if NRF_CLOCK_HAS_XO || !defined(CONFIG_SOC_NRF52832) { .dev = DEVICE_DT_GET_ONE(nordic_nrf_clock), .subsys_data = subsys_data, .subsys_cnt = ARRAY_SIZE(subsys_data) } +#endif /* NRF_CLOCK_HAS_XO || !defined(CONFIG_SOC_NRF52832) */ }; From ee9dcd2ac1d59c14cb3516bb93dd68fc7d1650b8 Mon Sep 17 00:00:00 2001 From: Michal Frankiewicz Date: Mon, 13 Oct 2025 11:49:49 +0200 Subject: [PATCH 2/4] [nrf fromlist] drivers: clock_control: Separated nrf xo shim from nrf clock shim. Separated clock_control_nrf_xo shim from clock_control_nrf shim. Upstream PR #: 97196 Signed-off-by: Michal Frankiewicz --- drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/clock_control_nrf.c | 1 - drivers/clock_control/clock_control_nrf_xo.c | 563 ++++++++++++++++++ modules/hal_nordic/nrfx/CMakeLists.txt | 1 + .../clock_control_api/src/nrf_device_subsys.h | 30 +- 5 files changed, 583 insertions(+), 13 deletions(-) create mode 100644 drivers/clock_control/clock_control_nrf_xo.c diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 975ec7bdcd5..9c016180b26 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -61,6 +61,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL clock_con 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_NRF_HFCLK clock_control_nrf_hfclk.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_XO clock_control_nrf_xo.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_COMMON clock_control_nrf_common.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL60X clock_control_bl60x.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL61X clock_control_bl61x.c) diff --git a/drivers/clock_control/clock_control_nrf.c b/drivers/clock_control/clock_control_nrf.c index 18481b6cfaa..60500febf26 100644 --- a/drivers/clock_control/clock_control_nrf.c +++ b/drivers/clock_control/clock_control_nrf.c @@ -771,7 +771,6 @@ static int clk_init(const struct device *dev) #if NRF_LFRC_HAS_CALIBRATION IRQ_CONNECT(LFRC_IRQn, DT_INST_IRQ(0, priority), nrfx_isr, nrfx_power_clock_irq_handler, 0); #endif - clock_control_nrf_common_connect_irq(); nrfx_err = nrfx_clock_init(clock_event_handler); diff --git a/drivers/clock_control/clock_control_nrf_xo.c b/drivers/clock_control/clock_control_nrf_xo.c new file mode 100644 index 00000000000..d2a656945ea --- /dev/null +++ b/drivers/clock_control/clock_control_nrf_xo.c @@ -0,0 +1,563 @@ +#include +#include +#include +#include +#include "nrf_clock_calibration.h" +#include +#include +#include +#include +#include +#include "clock_control_nrf_common.h" + +LOG_MODULE_REGISTER(clock_control_xo, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +#define DT_DRV_COMPAT nordic_nrf_clock_xo + +#define CLOCK_DEVICE_XO DEVICE_DT_GET(DT_NODELABEL(xo)) + +#define CTX_ONOFF BIT(6) +#define CTX_API BIT(7) +#define CTX_MASK (CTX_ONOFF | CTX_API) + +#define STATUS_MASK 0x7 +#define GET_STATUS(flags) (flags & STATUS_MASK) +#define GET_CTX(flags) (flags & CTX_MASK) + +/* Used only by HF clock */ +#define XO_USER_BT BIT(0) +#define XO_USER_GENERIC BIT(1) + +/* Helper logging macros which prepends subsys name to the log. */ +#ifdef CONFIG_LOG +#define CLOCK_LOG(lvl, dev, ...) \ + LOG_##lvl("%s: " GET_ARG_N(1, __VA_ARGS__), \ + "xo" \ + COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__),\ + (), (, GET_ARGS_LESS_N(1, __VA_ARGS__)))) +#else +#define CLOCK_LOG(...) +#endif + +#define ERR(dev, ...) CLOCK_LOG(ERR, dev, __VA_ARGS__) +#define WRN(dev, ...) CLOCK_LOG(WRN, dev, __VA_ARGS__) +#define INF(dev, ...) CLOCK_LOG(INF, dev, __VA_ARGS__) +#define DBG(dev, ...) CLOCK_LOG(DBG, dev, __VA_ARGS__) + +typedef void (*clk_ctrl_xo_func_t)(void); + +typedef struct { + struct onoff_manager mgr; + clock_control_cb_t cb; + void *user_data; + uint32_t flags; +} xo_data_t; + +typedef struct { + clk_ctrl_xo_func_t start; /* Clock start function */ + clk_ctrl_xo_func_t stop; /* Clock stop function */ +#ifdef CONFIG_LOG + const char *name; +#endif +} xo_config_t; + +static atomic_t xo_users; +static uint64_t xo_start_tstamp; +static uint64_t xo_stop_tstamp; + +static int set_starting_state(uint32_t *flags, uint32_t ctx) +{ + int err = 0; + unsigned int key = irq_lock(); + uint32_t current_ctx = GET_CTX(*flags); + + if ((*flags & (STATUS_MASK)) == CLOCK_CONTROL_STATUS_OFF) { + *flags = CLOCK_CONTROL_STATUS_STARTING | ctx; + } else if (current_ctx != ctx) { + err = -EPERM; + } else { + err = -EALREADY; + } + + irq_unlock(key); + + return err; +} + +static int async_start(const struct device *dev, clock_control_cb_t cb, + void *user_data, uint32_t ctx) +{ + int err; + + err = set_starting_state(&((xo_data_t *)dev->data)->flags, ctx); + if (err < 0) { + return err; + } + + ((xo_data_t *)dev->data)->cb = cb; + ((xo_data_t *)dev->data)->user_data = user_data; + + ((xo_config_t *)dev->config)->start(); + + return 0; +} + +static int set_off_state(uint32_t *flags, uint32_t ctx) +{ + int err = 0; + unsigned int key = irq_lock(); + uint32_t current_ctx = GET_CTX(*flags); + + if ((current_ctx != 0) && (current_ctx != ctx)) { + err = -EPERM; + } else { + *flags = CLOCK_CONTROL_STATUS_OFF; + } + + irq_unlock(key); + + return err; +} + +static void set_on_state(uint32_t *flags) +{ + unsigned int key = irq_lock(); + + *flags = CLOCK_CONTROL_STATUS_ON | GET_CTX(*flags); + irq_unlock(key); +} + +static int stop(const struct device *dev, uint32_t ctx) +{ + int err; + + err = set_off_state(&((xo_data_t *)dev->data)->flags, ctx); + if (err < 0) { + return err; + } + + ((xo_config_t *)dev->config)->stop(); + + return 0; +} + +static void xo_start(void) +{ + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_SHELL)) { + xo_start_tstamp = k_uptime_get(); + } + + nrfx_clock_xo_start(); +} + +static void xo_stop(void) +{ + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_SHELL)) { + xo_stop_tstamp = k_uptime_get(); + } + + nrfx_clock_xo_stop(); +} + +#ifdef CONFIG_CLOCK_CONTROL_NRF_HFINT_CALIBRATION + +static void nrf54l_errata_30_workaround(void) +{ + while (FIELD_GET(CLOCK_XO_STAT_STATE_Msk, NRF_CLOCK->XO.STAT) != + CLOCK_XO_STAT_STATE_Running) { + } + const uint32_t higher_bits = *((volatile uint32_t *)0x50120820UL) & 0xFFFFFFC0; + *((volatile uint32_t *)0x50120864UL) = 1 | BIT(31); + *((volatile uint32_t *)0x50120848UL) = 1; + uint32_t off_abs = 24; + + while (off_abs >= 24) { + *((volatile uint32_t *)0x50120844UL) = 1; + while (((*((volatile uint32_t *)0x50120840UL)) & (1 << 16)) != 0) { + } + const uint32_t current_cal = *((volatile uint32_t *)0x50120820UL) & 0x3F; + const uint32_t cal_result = *((volatile uint32_t *)0x50120840UL) & 0x7FF; + int32_t off = 1024 - cal_result; + + off_abs = (off < 0) ? -off : off; + + if (off >= 24 && current_cal < 0x3F) { + *((volatile uint32_t *)0x50120820UL) = higher_bits | (current_cal + 1); + } else if (off <= -24 && current_cal > 0) { + *((volatile uint32_t *)0x50120820UL) = higher_bits | (current_cal - 1); + } + } + + *((volatile uint32_t *)0x50120848UL) = 0; + *((volatile uint32_t *)0x50120864UL) = 0; +} + +#if CONFIG_CLOCK_CONTROL_NRF_HFINT_CALIBRATION_PERIOD + +static struct onoff_client hf_cal_cli; + +static void calibration_finished_callback(struct onoff_manager *mgr, + struct onoff_client *cli, + uint32_t state, + int res) +{ + (void)onoff_cancel_or_release(mgr, cli); +} + +static void calibration_handler(struct k_timer *timer) +{ + nrf_clock_xo_t clk_src; + + bool ret = nrfx_clock_xo_running_check(&clk_src); + + if (ret && (clk_src == NRF_CLOCK_HFCLK_HIGH_ACCURACY)) { + return; + } + + sys_notify_init_callback(&hf_cal_cli.notify, calibration_finished_callback); + (void)onoff_request(&((xo_data_t*)CLOCK_DEVICE_XO->data)->mgr, &hf_cal_cli); +} + +static K_TIMER_DEFINE(calibration_timer, calibration_handler, NULL); + +static int calibration_init(void) +{ + k_timer_start(&calibration_timer, + K_NO_WAIT, + K_MSEC(CONFIG_CLOCK_CONTROL_NRF_HFINT_CALIBRATION_PERIOD)); + + return 0; +} + +SYS_INIT(calibration_init, APPLICATION, 0); + +#endif /* CONFIG_CLOCK_CONTROL_NRF_HFINT_CALIBRATION_PERIOD */ +#endif /* CONFIG_CLOCK_CONTROL_NRF_HFINT_CALIBRATION */ + +static void clkstarted_handle(const struct device *dev) +{ +#if CONFIG_CLOCK_CONTROL_NRF_HFINT_CALIBRATION + if (nrf54l_errata_30()) { + nrf54l_errata_30_workaround(); + } +#endif + + clock_control_cb_t callback = ((xo_data_t *)dev->data)->cb; + void *user_data = ((xo_data_t *)dev->data)->user_data; + + ((xo_data_t *)dev->data)->cb = NULL; + set_on_state(&((xo_data_t *)dev->data)->flags); + DBG(dev, "Clock started"); + + if (callback) { + callback(dev, NULL, user_data); + } +} + +static void generic_xo_start(void) +{ + nrf_clock_hfclk_t type; + bool already_started = false; + unsigned int key = irq_lock(); + + xo_users |= XO_USER_GENERIC; + if (xo_users & XO_USER_BT) { + (void)nrfx_clock_xo_running_check(&type); + if (type == NRF_CLOCK_HFCLK_HIGH_ACCURACY) { + already_started = true; + /* Set on state in case clock interrupt comes and we + * want to avoid handling that. + */ + set_on_state( + &((xo_data_t *)CLOCK_DEVICE_XO->data)->flags); + } + } + + irq_unlock(key); + + if (already_started) { + /* Clock already started by z_nrf_clock_bt_ctlr_hf_request */ + clkstarted_handle(CLOCK_DEVICE_XO); + return; + } + + xo_start(); +} + +static void generic_xo_stop(void) +{ + /* It's not enough to use only atomic_and() here for synchronization, + * as the thread could be preempted right after that function but + * before xo_stop() is called and the preempting code could request + * the XO again. Then, the XO would be stopped inappropriately + * and xo_user would be left with an incorrect value. + */ + unsigned int key = irq_lock(); + + xo_users &= ~XO_USER_GENERIC; + /* Skip stopping if BT is still requesting the clock. */ + if (!(xo_users & XO_USER_BT)) { + xo_stop(); + } + + irq_unlock(key); +} + +static void onoff_started_callback(const struct device *dev, + clock_control_subsys_t sys, + void *user_data) +{ + ARG_UNUSED(sys); + + onoff_notify_fn notify = user_data; + + notify(&(((xo_data_t *)dev->data)->mgr), 0); +} + + +static void onoff_start(struct onoff_manager *mgr, + onoff_notify_fn notify) +{ + int err; + + err = async_start(CLOCK_DEVICE_XO, onoff_started_callback, notify, CTX_ONOFF); + if (err < 0) { + notify(mgr, err); + } +} + +static void onoff_stop(struct onoff_manager *mgr, + onoff_notify_fn notify) +{ + int res; + + res = stop(CLOCK_DEVICE_XO, CTX_ONOFF); + notify(mgr, res); +} + +static void clock_event_handler(nrfx_clock_xo_evt_type_t event) +{ + const struct device *dev = CLOCK_DEVICE_XO; + + switch (event) { +#if NRF_CLOCK_HAS_XO_TUNE + case NRFX_CLOCK_XO_EVT_XO_TUNED: + clkstarted_handle(dev); + break; + case NRFX_CLOCK_XO_EVT_XO_TUNE_ERROR: + case NRFX_CLOCK_XO_EVT_XO_TUNE_FAILED: + /* No processing needed. */ + break; + case NRFX_CLOCK_XO_EVT_HFCLK_STARTED: + /* HFCLK is stable after XOTUNED event. + * HFCLK_STARTED means only that clock has been started. + */ + break; +#else + /* HFCLK started should be used only if tune operation is done implicitly. */ + case NRFX_CLOCK_XO_EVT_HFCLK_STARTED: + { + /* Check needed due to anomaly 201: + * HFCLKSTARTED may be generated twice. + */ + if (GET_STATUS(((xo_data_t*)dev->data)->flags) + == CLOCK_CONTROL_STATUS_STARTING) { + clkstarted_handle(dev); + } + + break; + } +#endif + +#if NRF_CLOCK_HAS_PLL + case NRFX_CLOCK_XO_EVT_PLL_STARTED: + /* No processing needed. */ + break; +#endif + default: + __ASSERT_NO_MSG(0); + break; + } +} + +static void blocking_start_callback(const struct device *dev, + clock_control_subsys_t subsys, + void *user_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(subsys); + + struct k_sem *sem = user_data; + + k_sem_give(sem); +} + +void z_nrf_clock_bt_ctlr_hf_request(void) +{ + if (atomic_or(&xo_users, XO_USER_BT) & XO_USER_GENERIC) { + /* generic request already activated clock. */ + return; + } + + xo_start(); +} + +void z_nrf_clock_bt_ctlr_hf_release(void) +{ + /* It's not enough to use only atomic_and() here for synchronization, + * see the explanation in generic_hfclk_stop(). + */ + unsigned int key = irq_lock(); + + xo_users &= ~XO_USER_BT; + /* Skip stopping if generic is still requesting the clock. */ + if (!(xo_users & XO_USER_GENERIC)) { + + /* State needs to be set to OFF as BT API does not call stop API which + * normally setting this state. + */ + ((xo_data_t*)CLOCK_DEVICE_XO->data)->flags = CLOCK_CONTROL_STATUS_OFF; + xo_stop(); + } + + irq_unlock(key); +} + +#if DT_NODE_EXISTS(DT_NODELABEL(hfxo)) +uint32_t z_nrf_clock_bt_ctlr_hf_get_startup_time_us(void) +{ + return DT_PROP(DT_NODELABEL(hfxo), startup_time_us); +} +#endif + +static int api_start(const struct device *dev, clock_control_subsys_t subsys, + clock_control_cb_t cb, void *user_data) +{ + ARG_UNUSED(subsys); + + return async_start(dev, cb, user_data, CTX_API); +} + +static int api_blocking_start(const struct device *dev, + clock_control_subsys_t subsys) +{ + struct k_sem sem = Z_SEM_INITIALIZER(sem, 0, 1); + int err; + + if (!IS_ENABLED(CONFIG_MULTITHREADING)) { + return -ENOTSUP; + } + + err = api_start(dev, subsys, blocking_start_callback, &sem); + if (err < 0) { + return err; + } + + return k_sem_take(&sem, K_MSEC(500)); +} + +static int api_stop(const struct device *dev, clock_control_subsys_t subsys) +{ + ARG_UNUSED(subsys); + + return stop(dev, CTX_API); +} + +static enum clock_control_status api_get_status(const struct device *dev, + clock_control_subsys_t subsys) +{ + return GET_STATUS(((xo_data_t *)dev->data)->flags); +} + +static int api_request(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + xo_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_request(&dev_data->mgr, cli); +} + +static int api_release(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + xo_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_release(&dev_data->mgr); +} + +static int api_cancel_or_release(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + xo_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_cancel_or_release(&dev_data->mgr, cli); +} + +static int clk_init(const struct device *dev) +{ + nrfx_err_t nrfx_err; + int err; + static const struct onoff_transitions transitions = { + .start = onoff_start, + .stop = onoff_stop + }; + + clock_control_nrf_common_connect_irq(); + + nrfx_err = nrfx_clock_xo_init(clock_event_handler); + if (nrfx_err != NRFX_SUCCESS) { + return -EIO; + } + + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { + xo_data_t *data = ((xo_data_t*)dev->data); + + z_nrf_clock_calibration_init(&data->mgr); + } + + err = onoff_manager_init(&((xo_data_t*)dev->data)->mgr, + &transitions); + if (err < 0) { + return err; + } + + ((xo_data_t*)dev->data)->flags = CLOCK_CONTROL_STATUS_OFF; + + return 0; +} + +CLOCK_CONTROL_NRF_IRQ_HANDLERS_ITERABLE(clock_control_nrf_xo, + &nrfx_clock_xo_irq_handler); + +static DEVICE_API(nrf_clock_control, clock_control_api) = { + .std_api = { + .on = api_blocking_start, + .off = api_stop, + .async_on = api_start, + .get_status = api_get_status, + }, + .request = api_request, + .release = api_release, + .cancel_or_release = api_cancel_or_release, +}; + +static xo_data_t data; + +static const xo_config_t config = { + .start = generic_xo_start, + .stop = generic_xo_stop, + IF_ENABLED(CONFIG_LOG, (.name = "xo",)) +}; + +DEVICE_DT_DEFINE(DT_NODELABEL(xo), clk_init, NULL, + &data, &config, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &clock_control_api); diff --git a/modules/hal_nordic/nrfx/CMakeLists.txt b/modules/hal_nordic/nrfx/CMakeLists.txt index 53b7d95f500..6011fa5eda7 100644 --- a/modules/hal_nordic/nrfx/CMakeLists.txt +++ b/modules/hal_nordic/nrfx/CMakeLists.txt @@ -139,6 +139,7 @@ zephyr_library_sources_ifdef(CONFIG_NRFX_PRS ${SRC_DIR}/prs/nrfx_prs.c) zephyr_library_sources_ifdef(CONFIG_NRFX_ADC ${SRC_DIR}/nrfx_adc.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_hfclk.c) +zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_xo.c) zephyr_library_sources_ifdef(CONFIG_NRFX_COMP ${SRC_DIR}/nrfx_comp.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CRACEN ${SRC_DIR}/nrfx_cracen.c) zephyr_library_sources_ifdef(CONFIG_NRFX_DPPI ${SRC_DIR}/nrfx_dppi.c) diff --git a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h index 904d74a6cea..5b48d96aa32 100644 --- a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h +++ b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h @@ -7,15 +7,8 @@ #include "device_subsys.h" #include -#if NRF_CLOCK_HAS_XO || !defined(CONFIG_SOC_NRF52832) -static const struct device_subsys_data subsys_data[] = { -#if NRF_CLOCK_HAS_XO - { - .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, - .startup_us = CONFIG_TEST_NRF_HF_STARTUP_TIME_US - }, -#endif /* NRF_CLOCK_HAS_XO */ #ifndef CONFIG_SOC_NRF52832 +static const struct device_subsys_data subsys_data[] = { /* On nrf52832 LF clock cannot be stopped because it leads * to RTC COUNTER register reset and that is unexpected by * system clock which is disrupted and may hang in the test. @@ -25,9 +18,8 @@ static const struct device_subsys_data subsys_data[] = { .startup_us = (CLOCK_CONTROL_NRF_K32SRC == NRF_CLOCK_LFCLK_RC) ? 1000 : 500000 } -#endif /* !CONFIG_SOC_NRF52832 */ }; -#endif /* NRF_CLOCK_HAS_XO || !defined(CONFIG_SOC_NRF52832) */ +#endif /* !CONFIG_SOC_NRF52832 */ static const struct device_subsys_data subsys_data_hfclk[] = { { @@ -36,6 +28,13 @@ static const struct device_subsys_data subsys_data_hfclk[] = { } }; +static const struct device_subsys_data subsys_data_xo[] = { + { + .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, + .startup_us = CONFIG_TEST_NRF_HF_STARTUP_TIME_US + } +}; + static const struct device_data devices[] = { #if NRF_CLOCK_HAS_HFCLK { @@ -44,11 +43,18 @@ static const struct device_data devices[] = { .subsys_cnt = ARRAY_SIZE(subsys_data_hfclk) }, #endif /* NRF_CLOCK_HAS_HFCLK */ -#if NRF_CLOCK_HAS_XO || !defined(CONFIG_SOC_NRF52832) +#if NRF_CLOCK_HAS_XO + { + .dev = DEVICE_DT_GET_ONE(nordic_nrf_clock_xo), + .subsys_data = subsys_data_xo, + .subsys_cnt = ARRAY_SIZE(subsys_data_xo) + }, +#endif /* NRF_CLOCK_HAS_XO */ +#if !defined(CONFIG_SOC_NRF52832) { .dev = DEVICE_DT_GET_ONE(nordic_nrf_clock), .subsys_data = subsys_data, .subsys_cnt = ARRAY_SIZE(subsys_data) } -#endif /* NRF_CLOCK_HAS_XO || !defined(CONFIG_SOC_NRF52832) */ +#endif /* !defined(CONFIG_SOC_NRF52832) */ }; From 58e5145ab7639a8eae30c15f9112753bbd675236 Mon Sep 17 00:00:00 2001 From: Michal Frankiewicz Date: Mon, 13 Oct 2025 11:49:59 +0200 Subject: [PATCH 3/4] [nrf fromlist] drivers: clock_control: Separated nrf lfclk shim from nrf clock shim. Separated clock_control_nrf_lfclk shim from clock_control_nrf shim. Upstream PR #: 97199 Signed-off-by: Michal Frankiewicz --- .../nrf54l_10_15_cpuapp_common.dtsi | 4 + .../bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi | 4 + .../nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts | 4 + .../nrf54lm20bsim_nrf54lm20a_cpuapp.dts | 4 + .../nrf54l_05_10_15_cpuapp_common.dtsi | 4 + boards/nordic/nrf54l15dk/pre_dt_board.cmake | 6 + .../nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi | 4 + .../panb611evb_nrf54l15_cpuapp_common.dtsi | 4 + .../raytac_an54l15q_db_cpuapp_common.dtsi | 4 + .../ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts | 4 + drivers/clock_control/CMakeLists.txt | 3 +- drivers/clock_control/Kconfig.nrf | 16 +- drivers/clock_control/clock_control_nrf.c | 123 --- .../clock_control/clock_control_nrf_common.c | 7 +- .../clock_control/clock_control_nrf_lfclk.c | 732 ++++++++++-------- .../clock_control/clock_control_nrfs_lfclk.c | 436 +++++++++++ dts/arm/nordic/nrf51822.dtsi | 7 + dts/arm/nordic/nrf52805.dtsi | 7 + dts/arm/nordic/nrf52810.dtsi | 7 + dts/arm/nordic/nrf52811.dtsi | 7 + dts/arm/nordic/nrf52820.dtsi | 7 + dts/arm/nordic/nrf52832.dtsi | 7 + dts/arm/nordic/nrf52833.dtsi | 7 + dts/arm/nordic/nrf52840.dtsi | 7 + .../nordic/nrf5340_cpuapp_peripherals.dtsi | 7 + dts/arm/nordic/nrf5340_cpunet.dtsi | 7 + dts/arm/nordic/nrf91_peripherals.dtsi | 7 + .../clock/nordic,nrf-clock-lfclk.yaml | 15 + dts/bindings/clock/nordic,nrf-lfclk.yaml | 2 +- dts/vendor/nordic/nrf54h20.dtsi | 5 +- dts/vendor/nordic/nrf54l_05_10_15.dtsi | 7 + dts/vendor/nordic/nrf54lm20a.dtsi | 7 + modules/hal_nordic/nrfx/CMakeLists.txt | 1 + .../clock_control_api/src/nrf_device_subsys.h | 40 +- .../src/test_clock_control.c | 16 +- .../nrf_clock_control/src/main.c | 4 +- 36 files changed, 1048 insertions(+), 485 deletions(-) create mode 100644 boards/nordic/nrf54l15dk/pre_dt_board.cmake create mode 100644 drivers/clock_control/clock_control_nrfs_lfclk.c create mode 100644 dts/bindings/clock/nordic,nrf-clock-lfclk.yaml diff --git a/boards/ezurio/bl54l15_dvk/nrf54l_10_15_cpuapp_common.dtsi b/boards/ezurio/bl54l15_dvk/nrf54l_10_15_cpuapp_common.dtsi index 3e2b19c68a1..eeeff6b7ec0 100644 --- a/boards/ezurio/bl54l15_dvk/nrf54l_10_15_cpuapp_common.dtsi +++ b/boards/ezurio/bl54l15_dvk/nrf54l_10_15_cpuapp_common.dtsi @@ -96,6 +96,10 @@ status = "okay"; }; +&lfclk { + status = "okay"; +}; + &spi00 { status = "okay"; cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; diff --git a/boards/ezurio/bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi b/boards/ezurio/bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi index fd78f2d0899..1c0bae28007 100644 --- a/boards/ezurio/bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi +++ b/boards/ezurio/bl54l15u_dvk/nrf54l15_cpuapp_common.dtsi @@ -96,6 +96,10 @@ status = "okay"; }; +&lfclk { + status = "okay"; +}; + &spi00 { status = "okay"; cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; diff --git a/boards/native/nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts b/boards/native/nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts index 0640c6b2adc..a1c2f4bd4d1 100644 --- a/boards/native/nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts +++ b/boards/native/nrf_bsim/nrf54l15bsim_nrf54l15_cpuapp.dts @@ -139,3 +139,7 @@ &xo { status = "okay"; }; + +&lfclk { + status = "okay"; +}; diff --git a/boards/native/nrf_bsim/nrf54lm20bsim_nrf54lm20a_cpuapp.dts b/boards/native/nrf_bsim/nrf54lm20bsim_nrf54lm20a_cpuapp.dts index e65262bae05..19efa0cdbfe 100644 --- a/boards/native/nrf_bsim/nrf54lm20bsim_nrf54lm20a_cpuapp.dts +++ b/boards/native/nrf_bsim/nrf54lm20bsim_nrf54lm20a_cpuapp.dts @@ -146,3 +146,7 @@ &xo { status = "okay"; }; + +&lfclk { + status = "okay"; +}; diff --git a/boards/nordic/nrf54l15dk/nrf54l_05_10_15_cpuapp_common.dtsi b/boards/nordic/nrf54l15dk/nrf54l_05_10_15_cpuapp_common.dtsi index dcae050111a..4b7409cbeaf 100644 --- a/boards/nordic/nrf54l15dk/nrf54l_05_10_15_cpuapp_common.dtsi +++ b/boards/nordic/nrf54l15dk/nrf54l_05_10_15_cpuapp_common.dtsi @@ -105,6 +105,10 @@ status = "okay"; }; +&lfclk { + status = "okay"; +}; + &gpregret1 { status = "okay"; diff --git a/boards/nordic/nrf54l15dk/pre_dt_board.cmake b/boards/nordic/nrf54l15dk/pre_dt_board.cmake new file mode 100644 index 00000000000..d675eacdcb1 --- /dev/null +++ b/boards/nordic/nrf54l15dk/pre_dt_board.cmake @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +# Suppress "unique_unit_address_if_enabled" to handle the following overlaps: +# - power@X010e000 & clock@X010e000 & xo@X010e000 & lfclk@X010e000 +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") diff --git a/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi b/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi index 70ee2d89a05..99877b8aa85 100644 --- a/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi +++ b/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi @@ -134,6 +134,10 @@ status = "okay"; }; +&lfclk { + status = "okay"; +}; + &ieee802154 { status = "okay"; }; diff --git a/boards/panasonic/panb611evb/panb611evb_nrf54l15_cpuapp_common.dtsi b/boards/panasonic/panb611evb/panb611evb_nrf54l15_cpuapp_common.dtsi index a4e0b7add0f..1a6b2f6050c 100644 --- a/boards/panasonic/panb611evb/panb611evb_nrf54l15_cpuapp_common.dtsi +++ b/boards/panasonic/panb611evb/panb611evb_nrf54l15_cpuapp_common.dtsi @@ -95,6 +95,10 @@ status = "okay"; }; +&lfclk { + status = "okay"; +}; + &spi00 { status = "okay"; cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; diff --git a/boards/raytac/an54l15q_db/raytac_an54l15q_db_cpuapp_common.dtsi b/boards/raytac/an54l15q_db/raytac_an54l15q_db_cpuapp_common.dtsi index bf42a5b3a1f..680329d3f6d 100644 --- a/boards/raytac/an54l15q_db/raytac_an54l15q_db_cpuapp_common.dtsi +++ b/boards/raytac/an54l15q_db/raytac_an54l15q_db_cpuapp_common.dtsi @@ -96,6 +96,10 @@ status = "okay"; }; +&lfclk { + status = "okay"; +}; + &spi00 { status = "okay"; cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; diff --git a/boards/we/ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts b/boards/we/ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts index ef7455d3a5d..15abb061895 100644 --- a/boards/we/ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts +++ b/boards/we/ophelia4ev/ophelia4ev_nrf54l15_cpuapp.dts @@ -103,6 +103,10 @@ status = "okay"; }; +&lfclk { + status = "okay"; +}; + &spi00 { status = "okay"; diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 9c016180b26..1fc767aa2fd 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -58,10 +58,11 @@ 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_IRON_HSFLL_LOCAL clock_control_nrf_iron_hsfll_local.c) -zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_LFCLK clock_control_nrf_lfclk.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRFS_LFCLK clock_control_nrfs_lfclk.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_AUXPLL clock_control_nrf_auxpll.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_HFCLK clock_control_nrf_hfclk.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_XO clock_control_nrf_xo.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_LFCLK clock_control_nrf_lfclk.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_COMMON clock_control_nrf_common.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL60X clock_control_bl60x.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL61X clock_control_bl61x.c) diff --git a/drivers/clock_control/Kconfig.nrf b/drivers/clock_control/Kconfig.nrf index f8622eab5ab..8497368b4c1 100644 --- a/drivers/clock_control/Kconfig.nrf +++ b/drivers/clock_control/Kconfig.nrf @@ -307,21 +307,21 @@ config CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL_DVFS_TIMEOUT_MS endif # CLOCK_CONTROL_NRF_IRON_HSFLL_LOCAL -config CLOCK_CONTROL_NRF_LFCLK +config CLOCK_CONTROL_NRFS_LFCLK bool "NRF LFCLK driver support" - depends on DT_HAS_NORDIC_NRF_LFCLK_ENABLED + depends on DT_HAS_NORDIC_NRFS_LFCLK_ENABLED select NRFS select NRFS_CLOCK_SERVICE_ENABLED select CLOCK_CONTROL_NRF2_COMMON default y -if CLOCK_CONTROL_NRF_LFCLK +if CLOCK_CONTROL_NRFS_LFCLK -config CLOCK_CONTROL_NRF_LFCLK_CLOCK_TIMEOUT_MS +config CLOCK_CONTROL_NRFS_LFCLK_CLOCK_TIMEOUT_MS int "Timeout waiting for nrfs clock service callback in milliseconds" default 1000 -endif # CLOCK_CONTROL_NRF_LFCLK +endif # CLOCK_CONTROL_NRFS_LFCLK config CLOCK_CONTROL_NRF_COMMON bool @@ -338,6 +338,12 @@ config CLOCK_CONTROL_NRF_XO select CLOCK_CONTROL_NRF_COMMON default y +config CLOCK_CONTROL_NRF_LFCLK + bool "NRF LFCLK driver support" + depends on DT_HAS_NORDIC_NRF_CLOCK_LFCLK_ENABLED + select CLOCK_CONTROL_NRF_COMMON + default y + config CLOCK_CONTROL_NRF_AUXPLL bool "nRF Auxiliary PLL driver" default y diff --git a/drivers/clock_control/clock_control_nrf.c b/drivers/clock_control/clock_control_nrf.c index 60500febf26..1b5badfb5eb 100644 --- a/drivers/clock_control/clock_control_nrf.c +++ b/drivers/clock_control/clock_control_nrf.c @@ -539,129 +539,6 @@ static void onoff_start(struct onoff_manager *mgr, } } -/** @brief Wait for LF clock availability or stability. - * - * If LF clock source is SYNTH or RC then there is no distinction between - * availability and stability. In case of XTAL source clock, system is initially - * starting RC and then seamlessly switches to XTAL. Running RC means clock - * availability and running target source means stability, That is because - * significant difference in startup time (<1ms vs >200ms). - * - * In order to get event/interrupt when RC is ready (allowing CPU sleeping) two - * stage startup sequence is used. Initially, LF source is set to RC and when - * LFSTARTED event is handled it is reconfigured to the target source clock. - * This approach is implemented in nrfx_clock driver and utilized here. - * - * @param mode Start mode. - */ -static void lfclk_spinwait(enum nrf_lfclk_start_mode mode) -{ - static const nrf_clock_domain_t d = NRF_CLOCK_DOMAIN_LFCLK; - static const nrf_clock_lfclk_t target_type = - /* For sources XTAL, EXT_LOW_SWING, and EXT_FULL_SWING, - * NRF_CLOCK_LFCLK_XTAL is returned as the type of running clock. - */ - (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL) || - IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_LOW_SWING) || - IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_FULL_SWING)) - ? NRF_CLOCK_LFCLK_XTAL - : CLOCK_CONTROL_NRF_K32SRC; - nrf_clock_lfclk_t type; - - if ((mode == CLOCK_CONTROL_NRF_LF_START_AVAILABLE) && - (target_type == NRF_CLOCK_LFCLK_XTAL) && - (nrf_clock_lf_srccopy_get(NRF_CLOCK) == CLOCK_CONTROL_NRF_K32SRC)) { - /* If target clock source is using XTAL then due to two-stage - * clock startup sequence, RC might already be running. - * It can be determined by checking current LFCLK source. If it - * is set to the target clock source then it means that RC was - * started. - */ - return; - } - - bool isr_mode = k_is_in_isr() || k_is_pre_kernel(); - int key = isr_mode ? irq_lock() : 0; - - if (!isr_mode) { - nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK); - } - - while (!(nrfx_clock_is_running(d, (void *)&type) - && ((type == target_type) - || (mode == CLOCK_CONTROL_NRF_LF_START_AVAILABLE)))) { - /* Synth source start is almost instant and LFCLKSTARTED may - * happen before calling idle. That would lead to deadlock. - */ - if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH)) { - if (isr_mode || !IS_ENABLED(CONFIG_MULTITHREADING)) { - k_cpu_atomic_idle(key); - } else { - k_msleep(1); - } - } - - /* Clock interrupt is locked, LFCLKSTARTED is handled here. */ - if ((target_type == NRF_CLOCK_LFCLK_XTAL) - && (nrf_clock_lf_src_get(NRF_CLOCK) == NRF_CLOCK_LFCLK_RC) - && nrf_clock_event_check(NRF_CLOCK, - NRF_CLOCK_EVENT_LFCLKSTARTED)) { - nrf_clock_event_clear(NRF_CLOCK, - NRF_CLOCK_EVENT_LFCLKSTARTED); - nrf_clock_lf_src_set(NRF_CLOCK, - CLOCK_CONTROL_NRF_K32SRC); - - /* Clear pending interrupt, otherwise new clock event - * would not wake up from idle. - */ - NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); - nrf_clock_task_trigger(NRF_CLOCK, - NRF_CLOCK_TASK_LFCLKSTART); - } - } - - if (isr_mode) { - irq_unlock(key); - } else { - nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK); - } -} - -void z_nrf_clock_control_lf_on(enum nrf_lfclk_start_mode start_mode) -{ - static atomic_t on; - static struct onoff_client cli; - - if (atomic_set(&on, 1) == 0) { - int err; - struct onoff_manager *mgr = - get_onoff_manager(CLOCK_DEVICE, - CLOCK_CONTROL_NRF_TYPE_LFCLK); - - sys_notify_init_spinwait(&cli.notify); - err = onoff_request(mgr, &cli); - __ASSERT_NO_MSG(err >= 0); - } - - /* In case of simulated board leave immediately. */ - if (IS_ENABLED(CONFIG_SOC_SERIES_BSIM_NRFXX)) { - return; - } - - switch (start_mode) { - case CLOCK_CONTROL_NRF_LF_START_AVAILABLE: - case CLOCK_CONTROL_NRF_LF_START_STABLE: - lfclk_spinwait(start_mode); - break; - - case CLOCK_CONTROL_NRF_LF_START_NOWAIT: - break; - - default: - __ASSERT_NO_MSG(false); - } -} - static void clock_event_handler(nrfx_clock_evt_type_t event) { const struct device *dev = CLOCK_DEVICE; diff --git a/drivers/clock_control/clock_control_nrf_common.c b/drivers/clock_control/clock_control_nrf_common.c index 0d379563d18..8c7b5aab17f 100644 --- a/drivers/clock_control/clock_control_nrf_common.c +++ b/drivers/clock_control/clock_control_nrf_common.c @@ -36,6 +36,9 @@ void clock_control_nrf_common_connect_irq(void) } irq_connected = true; - IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), - nrfx_isr, clock_irq_handler, 0); +#if NRF_LFRC_HAS_CALIBRATION + IRQ_CONNECT(LFRC_IRQn, DT_INST_IRQ(0, priority), nrfx_isr, clock_irq_handler, 0); +#endif + + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), nrfx_isr, clock_irq_handler, 0); } diff --git a/drivers/clock_control/clock_control_nrf_lfclk.c b/drivers/clock_control/clock_control_nrf_lfclk.c index 9551fb4e636..355cf6e9e17 100644 --- a/drivers/clock_control/clock_control_nrf_lfclk.c +++ b/drivers/clock_control/clock_control_nrf_lfclk.c @@ -1,436 +1,530 @@ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2016-2020 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * * SPDX-License-Identifier: Apache-2.0 */ -#define DT_DRV_COMPAT nordic_nrf_lfclk - -#include "clock_control_nrf2_common.h" -#include +#include +#include +#include #include -#include -#include - +#include "nrf_clock_calibration.h" +#include "clock_control_nrf_common.h" +#include #include -LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); - -BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, - "multiple instances not supported"); +#include +#include +#include + +LOG_MODULE_REGISTER(clock_control_lfclk, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +#define DT_DRV_COMPAT nordic_nrf_clock_lfclk + +#define CTX_ONOFF BIT(6) +#define CTX_API BIT(7) +#define CTX_MASK (CTX_ONOFF | CTX_API) + +#define STATUS_MASK 0x7 +#define GET_STATUS(flags) (flags & STATUS_MASK) +#define GET_CTX(flags) (flags & CTX_MASK) + +/* Helper logging macros which prepends subsys name to the log. */ +#ifdef CONFIG_LOG +#define CLOCK_LOG(lvl, dev, ...) \ + LOG_##lvl("%s: " GET_ARG_N(1, __VA_ARGS__), \ + "lfclk" \ + COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__),\ + (), (, GET_ARGS_LESS_N(1, __VA_ARGS__)))) +#else +#define CLOCK_LOG(...) +#endif + +#define ERR(dev, ...) CLOCK_LOG(ERR, dev, __VA_ARGS__) +#define WRN(dev, ...) CLOCK_LOG(WRN, dev, __VA_ARGS__) +#define INF(dev, ...) CLOCK_LOG(INF, dev, __VA_ARGS__) +#define DBG(dev, ...) CLOCK_LOG(DBG, dev, __VA_ARGS__) + +#define CLOCK_DEVICE_LFCLK DEVICE_DT_GET_ONE(nordic_nrf_clock_lfclk) +#if NRF_CLOCK_HAS_HFCLK +#define CLOCK_DEVICE_HF DEVICE_DT_GET_ONE(nordic_nrf_clock_hfclk) +#else // NRF_CLOCK_HAS_XO +#define CLOCK_DEVICE_HF DEVICE_DT_GET_ONE(nordic_nrf_clock_xo) +#endif + +typedef void (*clk_ctrl_func_t)(void); + +//TODO move nrf_clock_control_sub_config here +//TODO move subdata here +typedef struct { + struct onoff_manager mgr; + clock_control_cb_t cb; + void *user_data; + uint32_t flags; +} lfclk_data_t; + +typedef struct { + clk_ctrl_func_t start; /* Clock start function */ + clk_ctrl_func_t stop; /* Clock stop function */ +#ifdef CONFIG_LOG + const char *name; +#endif +} lfclk_config_t; + +#if CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH +/* Client to request HFXO to synthesize low frequency clock. */ +static struct onoff_client lfsynth_cli; +#endif + +static int set_off_state(uint32_t *flags, uint32_t ctx) +{ + int err = 0; + unsigned int key = irq_lock(); + uint32_t current_ctx = GET_CTX(*flags); -#define LFCLK_HFXO_NODE DT_INST_PHANDLE_BY_NAME(0, clocks, hfxo) + if ((current_ctx != 0) && (current_ctx != ctx)) { + err = -EPERM; + } else { + *flags = CLOCK_CONTROL_STATUS_OFF; + } -#define LFCLK_LFRC_ACCURACY DT_INST_PROP(0, lfrc_accuracy_ppm) -#define LFCLK_HFXO_ACCURACY DT_PROP(LFCLK_HFXO_NODE, accuracy_ppm) -#define LFCLK_LFLPRC_STARTUP_TIME_US DT_INST_PROP(0, lflprc_startup_time_us) -#define LFCLK_LFRC_STARTUP_TIME_US DT_INST_PROP(0, lfrc_startup_time_us) + irq_unlock(key); -#define LFCLK_MAX_OPTS 4 -#define LFCLK_DEF_OPTS 2 + return err; +} -#define NRFS_CLOCK_TIMEOUT K_MSEC(CONFIG_CLOCK_CONTROL_NRF_LFCLK_CLOCK_TIMEOUT_MS) +static int set_starting_state(uint32_t *flags, uint32_t ctx) +{ + int err = 0; + unsigned int key = irq_lock(); + uint32_t current_ctx = GET_CTX(*flags); + + if ((*flags & (STATUS_MASK)) == CLOCK_CONTROL_STATUS_OFF) { + *flags = CLOCK_CONTROL_STATUS_STARTING | ctx; + } else if (current_ctx != ctx) { + err = -EPERM; + } else { + err = -EALREADY; + } -#define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr)) + irq_unlock(key); -/* Clock options sorted from highest to lowest power consumption. - * - Clock synthesized from a high frequency clock - * - Internal RC oscillator - * - External clock. These are inserted into the list at driver initialization. - * Set to one of the following: - * - XTAL. Low or High precision - * - External sine or square wave - */ -static struct clock_options { - uint16_t accuracy : 15; - uint16_t precision : 1; - nrfs_clock_src_t src; -} clock_options[LFCLK_MAX_OPTS] = { - { - /* NRFS will request FLL16M use HFXO in bypass mode if SYNTH src is used */ - .accuracy = LFCLK_HFXO_ACCURACY, - .precision = 1, - .src = NRFS_CLOCK_SRC_LFCLK_SYNTH, - }, - { - .accuracy = LFCLK_LFRC_ACCURACY, - .precision = 0, - .src = NRFS_CLOCK_SRC_LFCLK_LFRC, - }, - /* Remaining options are populated on lfclk_init */ -}; + return err; +} -struct lfclk_dev_data { - STRUCT_CLOCK_CONFIG(lfclk, ARRAY_SIZE(clock_options)) clk_cfg; - struct k_timer timer; - uint16_t max_accuracy; - uint8_t clock_options_cnt; - uint32_t hfxo_startup_time_us; - uint32_t lfxo_startup_time_us; -}; +static void set_on_state(uint32_t *flags) +{ + unsigned int key = irq_lock(); -struct lfclk_dev_config { - uint32_t fixed_frequency; -}; + *flags = CLOCK_CONTROL_STATUS_ON | GET_CTX(*flags); + irq_unlock(key); +} -static int lfosc_get_accuracy(uint16_t *accuracy) +static void clkstarted_handle(const struct device *dev) { - 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; + clock_control_cb_t callback = ((lfclk_data_t*)dev->data)->cb; + void *user_data = ((lfclk_data_t*)dev->data)->user_data; + + ((lfclk_data_t*)dev->data)->cb = NULL; + set_on_state(&((lfclk_data_t*)dev->data)->flags); + DBG(dev, "Clock started"); + + if (callback) { + callback(dev, NULL, user_data); } +} - return 0; +static inline void anomaly_132_workaround(void) +{ +#if (CONFIG_NRF52_ANOMALY_132_DELAY_US - 0) + static bool once; + + if (!once) { + k_busy_wait(CONFIG_NRF52_ANOMALY_132_DELAY_US); + once = true; + } +#endif } -static void clock_evt_handler(nrfs_clock_evt_t const *p_evt, void *context) +static void lfclk_start(void) { - struct lfclk_dev_data *dev_data = context; - int status = 0; + if (IS_ENABLED(CONFIG_NRF52_ANOMALY_132_WORKAROUND)) { + anomaly_132_workaround(); + } + +#if CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH + sys_notify_init_spinwait(&lfsynth_cli.notify); - k_timer_stop(&dev_data->timer); + (void)nrf_clock_control_request(CLOCK_DEVICE_HF, NULL, &lfsynth_cli); +#endif + + nrfx_clock_lfclk_start(); +} - if (p_evt->type == NRFS_CLOCK_EVT_REJECT) { - status = -ENXIO; +static void lfclk_stop(void) +{ + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { + z_nrf_clock_calibration_lfclk_stopped(); } - clock_config_update_end(&dev_data->clk_cfg, status); + nrfx_clock_lfclk_stop(); + +#if CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH + + (void)nrf_clock_control_cancel_or_release(CLOCK_DEVICE_HF, NULL, &lfsynth_cli); +#endif } -static void lfclk_update_timeout_handler(struct k_timer *timer) +static int stop(const struct device *dev, uint32_t ctx) { - struct lfclk_dev_data *dev_data = - CONTAINER_OF(timer, struct lfclk_dev_data, timer); + int err; - clock_config_update_end(&dev_data->clk_cfg, -ETIMEDOUT); + err = set_off_state(&((lfclk_data_t*)dev->data)->flags, ctx); + if (err < 0) { + return err; + } + + ((lfclk_config_t*)dev->config)->stop(); + + return 0; } -static void lfclk_work_handler(struct k_work *work) +static void blocking_start_callback(const struct device *dev, + clock_control_subsys_t subsys, + void *user_data) { - struct lfclk_dev_data *dev_data = - CONTAINER_OF(work, struct lfclk_dev_data, clk_cfg.work); - uint8_t to_activate_idx; - nrfs_err_t err; + struct k_sem *sem = user_data; - to_activate_idx = clock_config_update_begin(work); + k_sem_give(sem); +} - err = nrfs_clock_lfclk_src_set(clock_options[to_activate_idx].src, - dev_data); - if (err != NRFS_SUCCESS) { - clock_config_update_end(&dev_data->clk_cfg, -EIO); - } else { - k_timer_start(&dev_data->timer, NRFS_CLOCK_TIMEOUT, K_NO_WAIT); +static int async_start(const struct device *dev, clock_control_cb_t cb, + void *user_data, uint32_t ctx) +{ + int err; + + err = set_starting_state(&((lfclk_data_t*)dev->data)->flags, ctx); + if (err < 0) { + return err; } + + ((lfclk_data_t*)dev->data)->cb = cb; + ((lfclk_data_t*)dev->data)->user_data = user_data; + + ((lfclk_config_t*)dev->config)->start(); + + return 0; } -static int lfclk_resolve_spec_to_idx(const struct device *dev, - const struct nrf_clock_spec *req_spec) +/** @brief Wait for LF clock availability or stability. + * + * If LF clock source is SYNTH or RC then there is no distinction between + * availability and stability. In case of XTAL source clock, system is initially + * starting RC and then seamlessly switches to XTAL. Running RC means clock + * availability and running target source means stability, That is because + * significant difference in startup time (<1ms vs >200ms). + * + * In order to get event/interrupt when RC is ready (allowing CPU sleeping) two + * stage startup sequence is used. Initially, LF source is set to RC and when + * LFSTARTED event is handled it is reconfigured to the target source clock. + * This approach is implemented in nrfx_clock driver and utilized here. + * + * @param mode Start mode. + */ +static void lfclk_spinwait(enum nrf_lfclk_start_mode mode) { - struct lfclk_dev_data *dev_data = dev->data; - const struct lfclk_dev_config *dev_config = dev->config; - uint16_t req_accuracy; - - if (req_spec->frequency > dev_config->fixed_frequency) { - LOG_ERR("invalid frequency"); - return -EINVAL; + static const nrf_clock_lfclk_t target_type = + /* For sources XTAL, EXT_LOW_SWING, and EXT_FULL_SWING, + * NRF_CLOCK_LFCLK_XTAL is returned as the type of running clock. + */ + (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL) || + IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_LOW_SWING) || + IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_FULL_SWING)) + ? NRF_CLOCK_LFCLK_XTAL + : CLOCK_CONTROL_NRF_K32SRC; + nrf_clock_lfclk_t type; + + if ((mode == CLOCK_CONTROL_NRF_LF_START_AVAILABLE) && + (target_type == NRF_CLOCK_LFCLK_XTAL) && + (nrf_clock_lf_srccopy_get(NRF_CLOCK) == CLOCK_CONTROL_NRF_K32SRC)) { + /* If target clock source is using XTAL then due to two-stage + * clock startup sequence, RC might already be running. + * It can be determined by checking current LFCLK source. If it + * is set to the target clock source then it means that RC was + * started. + */ + return; } - req_accuracy = req_spec->accuracy == NRF_CLOCK_CONTROL_ACCURACY_MAX - ? dev_data->max_accuracy - : req_spec->accuracy; + bool isr_mode = k_is_in_isr() || k_is_pre_kernel(); + int key = isr_mode ? irq_lock() : 0; + + if (!isr_mode) { + nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK); + } - for (int i = dev_data->clock_options_cnt - 1; i >= 0; --i) { - /* Iterate to a more power hungry and accurate clock source - * If the requested accuracy is higher (lower ppm) than what - * the clock source can provide. - * - * In case of an accuracy of 0 (don't care), do not check accuracy. + while (!(nrfx_clock_lfclk_running_check((void *)&type) + && ((type == target_type) + || (mode == CLOCK_CONTROL_NRF_LF_START_AVAILABLE)))) { + /* Synth source start is almost instant and LFCLKSTARTED may + * happen before calling idle. That would lead to deadlock. */ - if ((req_accuracy != 0 && req_accuracy < clock_options[i].accuracy) || - (req_spec->precision > clock_options[i].precision)) { - continue; + if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH)) { + if (isr_mode || !IS_ENABLED(CONFIG_MULTITHREADING)) { + k_cpu_atomic_idle(key); + } else { + k_msleep(1); + } } - return i; + /* Clock interrupt is locked, LFCLKSTARTED is handled here. */ + if ((target_type == NRF_CLOCK_LFCLK_XTAL) + && (nrf_clock_lf_src_get(NRF_CLOCK) == NRF_CLOCK_LFCLK_RC) + && nrf_clock_event_check(NRF_CLOCK, + NRF_CLOCK_EVENT_LFCLKSTARTED)) { + nrf_clock_event_clear(NRF_CLOCK, + NRF_CLOCK_EVENT_LFCLKSTARTED); + nrf_clock_lf_src_set(NRF_CLOCK, + CLOCK_CONTROL_NRF_K32SRC); + + /* Clear pending interrupt, otherwise new clock event + * would not wake up from idle. + */ + NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); + nrf_clock_task_trigger(NRF_CLOCK, + NRF_CLOCK_TASK_LFCLKSTART); + } } - LOG_ERR("invalid accuracy or precision"); - return -EINVAL; + if (isr_mode) { + irq_unlock(key); + } else { + nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK); + } } -static void lfclk_get_spec_by_idx(const struct device *dev, - uint8_t idx, - struct nrf_clock_spec *spec) +static void clock_event_handler(nrfx_clock_lfclk_evt_type_t event) //TODO { - const struct lfclk_dev_config *dev_config = dev->config; + const struct device *dev = CLOCK_DEVICE_LFCLK; - spec->frequency = dev_config->fixed_frequency; - spec->accuracy = clock_options[idx].accuracy; - spec->precision = clock_options[idx].precision; + switch (event) { + case NRFX_CLOCK_LFCLK_EVT_LFCLK_STARTED: + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { + z_nrf_clock_calibration_lfclk_started(); + } + clkstarted_handle(dev); + break; +#if NRF_CLOCK_HAS_CALIBRATION || NRF_LFRC_HAS_CALIBRATION + case NRFX_CLOCK_LFCLK_EVT_CAL_DONE: + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { + z_nrf_clock_calibration_done_handler(); + } else { + /* Should not happen when calibration is disabled. */ + __ASSERT_NO_MSG(false); + } + break; +#endif + default: + __ASSERT_NO_MSG(0); + break; + } } -static struct onoff_manager *lfclk_get_mgr_by_idx(const struct device *dev, uint8_t idx) +static void onoff_started_callback(const struct device *dev, + clock_control_subsys_t sys, + void *user_data) { - struct lfclk_dev_data *dev_data = dev->data; + onoff_notify_fn notify = user_data; - return &dev_data->clk_cfg.onoff[idx].mgr; + notify(&((lfclk_data_t*)dev->data)->mgr, 0); } -static int lfclk_get_startup_time_by_idx(const struct device *dev, - uint8_t idx, - uint32_t *startup_time_us) +static void onoff_start(struct onoff_manager *mgr, + onoff_notify_fn notify) { - struct lfclk_dev_data *dev_data = dev->data; - nrfs_clock_src_t src = clock_options[idx].src; - - switch (src) { - case NRFS_CLOCK_SRC_LFCLK_LFLPRC: - *startup_time_us = LFCLK_LFLPRC_STARTUP_TIME_US; - return 0; - - case NRFS_CLOCK_SRC_LFCLK_LFRC: - *startup_time_us = LFCLK_LFRC_STARTUP_TIME_US; - return 0; - - case NRFS_CLOCK_SRC_LFCLK_XO_PIXO: - case NRFS_CLOCK_SRC_LFCLK_XO_PIERCE: - case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE: - case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE: - case NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP: - case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP: - *startup_time_us = dev_data->lfxo_startup_time_us; - return 0; - - case NRFS_CLOCK_SRC_LFCLK_SYNTH: - *startup_time_us = dev_data->hfxo_startup_time_us; - return 0; + int err; - default: - break; + err = async_start(CLOCK_DEVICE_LFCLK, onoff_started_callback, + notify, CTX_ONOFF); + if (err < 0) { + notify(mgr, err); } - - return -EINVAL; } -static struct onoff_manager *lfclk_find_mgr_by_spec(const struct device *dev, - const struct nrf_clock_spec *spec) +static void onoff_stop(struct onoff_manager *mgr, + onoff_notify_fn notify) { - int idx; + int res; - if (!spec) { - return lfclk_get_mgr_by_idx(dev, 0); - } - - idx = lfclk_resolve_spec_to_idx(dev, spec); - return idx < 0 ? NULL : lfclk_get_mgr_by_idx(dev, idx); + res = stop(CLOCK_DEVICE_LFCLK, CTX_ONOFF); + notify(mgr, res); } -static int api_request_lfclk(const struct device *dev, - const struct nrf_clock_spec *spec, - struct onoff_client *cli) +void z_nrf_clock_control_lf_on(enum nrf_lfclk_start_mode start_mode) { - struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec); + static atomic_t on; + static struct onoff_client cli; + + if (atomic_set(&on, 1) == 0) { + int err; + struct onoff_manager *mgr = &((lfclk_data_t *)CLOCK_DEVICE_LFCLK->data)->mgr; - if (mgr) { - return clock_config_request(mgr, cli); + sys_notify_init_spinwait(&cli.notify); + err = onoff_request(mgr, &cli); + __ASSERT_NO_MSG(err >= 0); } - return -EINVAL; -} + /* In case of simulated board leave immediately. */ + if (IS_ENABLED(CONFIG_SOC_SERIES_BSIM_NRFXX)) { + return; + } -static int api_release_lfclk(const struct device *dev, - const struct nrf_clock_spec *spec) -{ - struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec); + switch (start_mode) { + case CLOCK_CONTROL_NRF_LF_START_AVAILABLE: + case CLOCK_CONTROL_NRF_LF_START_STABLE: + lfclk_spinwait(start_mode); + break; - if (mgr) { - return onoff_release(mgr); + case CLOCK_CONTROL_NRF_LF_START_NOWAIT: + break; + + default: + __ASSERT_NO_MSG(false); } +} - return -EINVAL; +static int api_start(const struct device *dev, clock_control_subsys_t subsys, + clock_control_cb_t cb, void *user_data) +{ + ARG_UNUSED(subsys); + + return async_start(dev, cb, user_data, CTX_API); } -static int api_cancel_or_release_lfclk(const struct device *dev, - const struct nrf_clock_spec *spec, - struct onoff_client *cli) +static int api_blocking_start(const struct device *dev, + clock_control_subsys_t subsys) { - struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec); + struct k_sem sem = Z_SEM_INITIALIZER(sem, 0, 1); + int err; - if (mgr) { - return onoff_cancel_or_release(mgr, cli); + if (!IS_ENABLED(CONFIG_MULTITHREADING)) { + return -ENOTSUP; } - return -EINVAL; -} + err = api_start(dev, subsys, blocking_start_callback, &sem); + if (err < 0) { + return err; + } + return k_sem_take(&sem, K_MSEC(500)); +} -static int api_resolve(const struct device *dev, - const struct nrf_clock_spec *req_spec, - struct nrf_clock_spec *res_spec) +static int api_stop(const struct device *dev, clock_control_subsys_t subsys) { - int idx; + ARG_UNUSED(subsys); - idx = lfclk_resolve_spec_to_idx(dev, req_spec); - if (idx < 0) { - return -EINVAL; - } + return stop(dev, CTX_API); +} - lfclk_get_spec_by_idx(dev, idx, res_spec); - return 0; +static enum clock_control_status api_get_status(const struct device *dev, + clock_control_subsys_t subsys) +{ + ARG_UNUSED(subsys); + + return GET_STATUS(((lfclk_data_t*)dev->data)->flags); } -static int api_get_startup_time(const struct device *dev, - const struct nrf_clock_spec *spec, - uint32_t *startup_time_us) +static int api_request(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) { - int idx; + lfclk_data_t *dev_data = dev->data; - idx = lfclk_resolve_spec_to_idx(dev, spec); - if (idx < 0) { - return -EINVAL; - } + ARG_UNUSED(spec); - return lfclk_get_startup_time_by_idx(dev, idx, startup_time_us); + return onoff_request(&dev_data->mgr, cli); } -static int api_get_rate_lfclk(const struct device *dev, - clock_control_subsys_t sys, - uint32_t *rate) +static int api_release(const struct device *dev, + const struct nrf_clock_spec *spec) { - ARG_UNUSED(sys); + lfclk_data_t *dev_data = dev->data; - const struct lfclk_dev_config *dev_config = dev->config; + ARG_UNUSED(spec); - *rate = dev_config->fixed_frequency; - - return 0; + return onoff_release(&dev_data->mgr); } -static int lfclk_init(const struct device *dev) +static int api_cancel_or_release(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) { - struct lfclk_dev_data *dev_data = dev->data; - nrf_bicr_lfosc_mode_t lfosc_mode; - nrfs_err_t res; + lfclk_data_t *dev_data = dev->data; - res = nrfs_clock_init(clock_evt_handler); - if (res != NRFS_SUCCESS) { - return -EIO; - } + ARG_UNUSED(spec); - dev_data->clock_options_cnt = LFCLK_DEF_OPTS; + return onoff_cancel_or_release(&dev_data->mgr, cli); +} - lfosc_mode = nrf_bicr_lfosc_mode_get(BICR); +static int clk_init(const struct device *dev) +{ + nrfx_err_t nrfx_err; + int err; + static const struct onoff_transitions transitions = { + .start = onoff_start, + .stop = onoff_stop + }; - if (lfosc_mode == NRF_BICR_LFOSC_MODE_UNCONFIGURED || - lfosc_mode == NRF_BICR_LFOSC_MODE_DISABLED) { - dev_data->max_accuracy = LFCLK_HFXO_ACCURACY; - } else { - int ret; + clock_control_nrf_common_connect_irq(); - ret = lfosc_get_accuracy(&dev_data->max_accuracy); - if (ret < 0) { - LOG_ERR("LFOSC enabled with invalid accuracy"); - return ret; - } + nrfx_err = nrfx_clock_lfclk_init(clock_event_handler); + if (nrfx_err != NRFX_SUCCESS) { + return -EIO; + } - switch (lfosc_mode) { - case NRF_BICR_LFOSC_MODE_CRYSTAL: - clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy; - clock_options[LFCLK_MAX_OPTS - 1].precision = 0; - clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE; - - clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; - clock_options[LFCLK_MAX_OPTS - 2].precision = 1; - clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP; - - dev_data->clock_options_cnt += 2; - break; - case NRF_BICR_LFOSC_MODE_EXTSINE: - clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy; - clock_options[LFCLK_MAX_OPTS - 1].precision = 0; - clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE; - - clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; - clock_options[LFCLK_MAX_OPTS - 2].precision = 1; - clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP; - - dev_data->clock_options_cnt += 2; - break; - case NRF_BICR_LFOSC_MODE_EXTSQUARE: - clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; - clock_options[LFCLK_MAX_OPTS - 2].precision = 0; - clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE; - - dev_data->clock_options_cnt += 1; - break; - default: - LOG_ERR("Unexpected LFOSC mode"); - return -EINVAL; - } + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { + lfclk_data_t *data = ((lfclk_data_t*)dev->data); - dev_data->lfxo_startup_time_us = nrf_bicr_lfosc_startup_time_ms_get(BICR) - * USEC_PER_MSEC; - if (dev_data->lfxo_startup_time_us == NRF_BICR_LFOSC_STARTUP_TIME_UNCONFIGURED) { - LOG_ERR("BICR LFXO startup time invalid"); - return -ENODEV; - } + z_nrf_clock_calibration_init(&data->mgr); } - dev_data->hfxo_startup_time_us = nrf_bicr_hfxo_startup_time_us_get(BICR); - if (dev_data->hfxo_startup_time_us == NRF_BICR_HFXO_STARTUP_TIME_UNCONFIGURED) { - LOG_ERR("BICR HFXO startup time invalid"); - return -ENODEV; - } + err = onoff_manager_init(&((lfclk_data_t*)dev->data)->mgr, + &transitions); + if (err < 0) { + return err; + } - k_timer_init(&dev_data->timer, lfclk_update_timeout_handler, NULL); + ((lfclk_data_t*)dev->data)->flags = CLOCK_CONTROL_STATUS_OFF; - return clock_config_init(&dev_data->clk_cfg, - ARRAY_SIZE(dev_data->clk_cfg.onoff), - lfclk_work_handler); + return 0; } -static DEVICE_API(nrf_clock_control, lfclk_drv_api) = { +CLOCK_CONTROL_NRF_IRQ_HANDLERS_ITERABLE(clock_control_nrf_lfclk, + &nrfx_clock_lfclk_irq_handler); + +static DEVICE_API(nrf_clock_control, clock_control_api) = { .std_api = { - .on = api_nosys_on_off, - .off = api_nosys_on_off, - .get_rate = api_get_rate_lfclk, + .on = api_blocking_start, + .off = api_stop, + .async_on = api_start, + .get_status = api_get_status, }, - .request = api_request_lfclk, - .release = api_release_lfclk, - .cancel_or_release = api_cancel_or_release_lfclk, - .resolve = api_resolve, - .get_startup_time = api_get_startup_time, + .request = api_request, + .release = api_release, + .cancel_or_release = api_cancel_or_release, }; -static struct lfclk_dev_data lfclk_data; +static lfclk_data_t data; + +static const lfclk_config_t config = { + + .start = lfclk_start, + .stop = lfclk_stop, + IF_ENABLED(CONFIG_LOG, (.name = "lfclk",)) -static const struct lfclk_dev_config lfclk_config = { - .fixed_frequency = DT_INST_PROP(0, clock_frequency), }; -DEVICE_DT_INST_DEFINE(0, lfclk_init, NULL, - &lfclk_data, &lfclk_config, - PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, - &lfclk_drv_api); +DEVICE_DT_DEFINE(DT_NODELABEL(lfclk), clk_init, NULL, + &data, &config, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &clock_control_api); diff --git a/drivers/clock_control/clock_control_nrfs_lfclk.c b/drivers/clock_control/clock_control_nrfs_lfclk.c new file mode 100644 index 00000000000..02f91678802 --- /dev/null +++ b/drivers/clock_control/clock_control_nrfs_lfclk.c @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nordic_nrfs_lfclk + +#include "clock_control_nrf2_common.h" +#include +#include +#include +#include + +#include +LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, + "multiple instances not supported"); + +#define LFCLK_HFXO_NODE DT_INST_PHANDLE_BY_NAME(0, clocks, hfxo) + +#define LFCLK_LFRC_ACCURACY DT_INST_PROP(0, lfrc_accuracy_ppm) +#define LFCLK_HFXO_ACCURACY DT_PROP(LFCLK_HFXO_NODE, accuracy_ppm) +#define LFCLK_LFLPRC_STARTUP_TIME_US DT_INST_PROP(0, lflprc_startup_time_us) +#define LFCLK_LFRC_STARTUP_TIME_US DT_INST_PROP(0, lfrc_startup_time_us) + +#define LFCLK_MAX_OPTS 4 +#define LFCLK_DEF_OPTS 2 + +#define NRFS_CLOCK_TIMEOUT K_MSEC(CONFIG_CLOCK_CONTROL_NRFS_LFCLK_CLOCK_TIMEOUT_MS) + +#define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr)) + +/* Clock options sorted from highest to lowest power consumption. + * - Clock synthesized from a high frequency clock + * - Internal RC oscillator + * - External clock. These are inserted into the list at driver initialization. + * Set to one of the following: + * - XTAL. Low or High precision + * - External sine or square wave + */ +static struct clock_options { + uint16_t accuracy : 15; + uint16_t precision : 1; + nrfs_clock_src_t src; +} clock_options[LFCLK_MAX_OPTS] = { + { + /* NRFS will request FLL16M use HFXO in bypass mode if SYNTH src is used */ + .accuracy = LFCLK_HFXO_ACCURACY, + .precision = 1, + .src = NRFS_CLOCK_SRC_LFCLK_SYNTH, + }, + { + .accuracy = LFCLK_LFRC_ACCURACY, + .precision = 0, + .src = NRFS_CLOCK_SRC_LFCLK_LFRC, + }, + /* Remaining options are populated on lfclk_init */ +}; + +struct lfclk_dev_data { + STRUCT_CLOCK_CONFIG(lfclk, ARRAY_SIZE(clock_options)) clk_cfg; + struct k_timer timer; + uint16_t max_accuracy; + uint8_t clock_options_cnt; + uint32_t hfxo_startup_time_us; + uint32_t lfxo_startup_time_us; +}; + +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; + int status = 0; + + k_timer_stop(&dev_data->timer); + + if (p_evt->type == NRFS_CLOCK_EVT_REJECT) { + status = -ENXIO; + } + + clock_config_update_end(&dev_data->clk_cfg, status); +} + +static void lfclk_update_timeout_handler(struct k_timer *timer) +{ + struct lfclk_dev_data *dev_data = + CONTAINER_OF(timer, struct lfclk_dev_data, timer); + + clock_config_update_end(&dev_data->clk_cfg, -ETIMEDOUT); +} + +static void lfclk_work_handler(struct k_work *work) +{ + struct lfclk_dev_data *dev_data = + CONTAINER_OF(work, struct lfclk_dev_data, clk_cfg.work); + uint8_t to_activate_idx; + nrfs_err_t err; + + to_activate_idx = clock_config_update_begin(work); + + err = nrfs_clock_lfclk_src_set(clock_options[to_activate_idx].src, + dev_data); + if (err != NRFS_SUCCESS) { + clock_config_update_end(&dev_data->clk_cfg, -EIO); + } else { + k_timer_start(&dev_data->timer, NRFS_CLOCK_TIMEOUT, K_NO_WAIT); + } +} + +static int lfclk_resolve_spec_to_idx(const struct device *dev, + const struct nrf_clock_spec *req_spec) +{ + struct lfclk_dev_data *dev_data = dev->data; + const struct lfclk_dev_config *dev_config = dev->config; + uint16_t req_accuracy; + + if (req_spec->frequency > dev_config->fixed_frequency) { + LOG_ERR("invalid frequency"); + return -EINVAL; + } + + req_accuracy = req_spec->accuracy == NRF_CLOCK_CONTROL_ACCURACY_MAX + ? dev_data->max_accuracy + : req_spec->accuracy; + + for (int i = dev_data->clock_options_cnt - 1; i >= 0; --i) { + /* Iterate to a more power hungry and accurate clock source + * If the requested accuracy is higher (lower ppm) than what + * the clock source can provide. + * + * In case of an accuracy of 0 (don't care), do not check accuracy. + */ + if ((req_accuracy != 0 && req_accuracy < clock_options[i].accuracy) || + (req_spec->precision > clock_options[i].precision)) { + continue; + } + + return i; + } + + LOG_ERR("invalid accuracy or precision"); + return -EINVAL; +} + +static void lfclk_get_spec_by_idx(const struct device *dev, + uint8_t idx, + struct nrf_clock_spec *spec) +{ + const struct lfclk_dev_config *dev_config = dev->config; + + spec->frequency = dev_config->fixed_frequency; + spec->accuracy = clock_options[idx].accuracy; + spec->precision = clock_options[idx].precision; +} + +static struct onoff_manager *lfclk_get_mgr_by_idx(const struct device *dev, uint8_t idx) +{ + struct lfclk_dev_data *dev_data = dev->data; + + return &dev_data->clk_cfg.onoff[idx].mgr; +} + +static int lfclk_get_startup_time_by_idx(const struct device *dev, + uint8_t idx, + uint32_t *startup_time_us) +{ + struct lfclk_dev_data *dev_data = dev->data; + nrfs_clock_src_t src = clock_options[idx].src; + + switch (src) { + case NRFS_CLOCK_SRC_LFCLK_LFLPRC: + *startup_time_us = LFCLK_LFLPRC_STARTUP_TIME_US; + return 0; + + case NRFS_CLOCK_SRC_LFCLK_LFRC: + *startup_time_us = LFCLK_LFRC_STARTUP_TIME_US; + return 0; + + case NRFS_CLOCK_SRC_LFCLK_XO_PIXO: + case NRFS_CLOCK_SRC_LFCLK_XO_PIERCE: + case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE: + case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE: + case NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP: + case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP: + *startup_time_us = dev_data->lfxo_startup_time_us; + return 0; + + case NRFS_CLOCK_SRC_LFCLK_SYNTH: + *startup_time_us = dev_data->hfxo_startup_time_us; + return 0; + + default: + break; + } + + return -EINVAL; +} + +static struct onoff_manager *lfclk_find_mgr_by_spec(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + int idx; + + if (!spec) { + return lfclk_get_mgr_by_idx(dev, 0); + } + + idx = lfclk_resolve_spec_to_idx(dev, spec); + return idx < 0 ? NULL : lfclk_get_mgr_by_idx(dev, idx); +} + +static int api_request_lfclk(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec); + + if (mgr) { + return clock_config_request(mgr, cli); + } + + return -EINVAL; +} + +static int api_release_lfclk(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec); + + if (mgr) { + return onoff_release(mgr); + } + + return -EINVAL; +} + +static int api_cancel_or_release_lfclk(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec); + + if (mgr) { + return onoff_cancel_or_release(mgr, cli); + } + + return -EINVAL; +} + + +static int api_resolve(const struct device *dev, + const struct nrf_clock_spec *req_spec, + struct nrf_clock_spec *res_spec) +{ + int idx; + + idx = lfclk_resolve_spec_to_idx(dev, req_spec); + if (idx < 0) { + return -EINVAL; + } + + lfclk_get_spec_by_idx(dev, idx, res_spec); + return 0; +} + +static int api_get_startup_time(const struct device *dev, + const struct nrf_clock_spec *spec, + uint32_t *startup_time_us) +{ + int idx; + + idx = lfclk_resolve_spec_to_idx(dev, spec); + if (idx < 0) { + return -EINVAL; + } + + return lfclk_get_startup_time_by_idx(dev, idx, startup_time_us); +} + +static int api_get_rate_lfclk(const struct device *dev, + clock_control_subsys_t sys, + uint32_t *rate) +{ + ARG_UNUSED(sys); + + const struct lfclk_dev_config *dev_config = dev->config; + + *rate = dev_config->fixed_frequency; + + return 0; +} + +static int lfclk_init(const struct device *dev) +{ + struct lfclk_dev_data *dev_data = dev->data; + nrf_bicr_lfosc_mode_t lfosc_mode; + nrfs_err_t res; + + res = nrfs_clock_init(clock_evt_handler); + if (res != NRFS_SUCCESS) { + return -EIO; + } + + dev_data->clock_options_cnt = LFCLK_DEF_OPTS; + + lfosc_mode = nrf_bicr_lfosc_mode_get(BICR); + + if (lfosc_mode == NRF_BICR_LFOSC_MODE_UNCONFIGURED || + lfosc_mode == NRF_BICR_LFOSC_MODE_DISABLED) { + dev_data->max_accuracy = LFCLK_HFXO_ACCURACY; + } else { + int ret; + + ret = lfosc_get_accuracy(&dev_data->max_accuracy); + if (ret < 0) { + LOG_ERR("LFOSC enabled with invalid accuracy"); + return ret; + } + + switch (lfosc_mode) { + case NRF_BICR_LFOSC_MODE_CRYSTAL: + clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 1].precision = 0; + clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE; + + clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 2].precision = 1; + clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP; + + dev_data->clock_options_cnt += 2; + break; + case NRF_BICR_LFOSC_MODE_EXTSINE: + clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 1].precision = 0; + clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE; + + clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 2].precision = 1; + clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP; + + dev_data->clock_options_cnt += 2; + break; + case NRF_BICR_LFOSC_MODE_EXTSQUARE: + clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy; + clock_options[LFCLK_MAX_OPTS - 2].precision = 0; + clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE; + + dev_data->clock_options_cnt += 1; + break; + default: + LOG_ERR("Unexpected LFOSC mode"); + return -EINVAL; + } + + dev_data->lfxo_startup_time_us = nrf_bicr_lfosc_startup_time_ms_get(BICR) + * USEC_PER_MSEC; + if (dev_data->lfxo_startup_time_us == NRF_BICR_LFOSC_STARTUP_TIME_UNCONFIGURED) { + LOG_ERR("BICR LFXO startup time invalid"); + return -ENODEV; + } + } + + dev_data->hfxo_startup_time_us = nrf_bicr_hfxo_startup_time_us_get(BICR); + if (dev_data->hfxo_startup_time_us == NRF_BICR_HFXO_STARTUP_TIME_UNCONFIGURED) { + LOG_ERR("BICR HFXO startup time invalid"); + return -ENODEV; + } + + k_timer_init(&dev_data->timer, lfclk_update_timeout_handler, NULL); + + return clock_config_init(&dev_data->clk_cfg, + ARRAY_SIZE(dev_data->clk_cfg.onoff), + lfclk_work_handler); +} + +static DEVICE_API(nrf_clock_control, lfclk_drv_api) = { + .std_api = { + .on = api_nosys_on_off, + .off = api_nosys_on_off, + .get_rate = api_get_rate_lfclk, + }, + .request = api_request_lfclk, + .release = api_release_lfclk, + .cancel_or_release = api_cancel_or_release_lfclk, + .resolve = api_resolve, + .get_startup_time = api_get_startup_time, +}; + +static struct lfclk_dev_data lfclk_data; + +static const struct lfclk_dev_config lfclk_config = { + .fixed_frequency = DT_INST_PROP(0, clock_frequency), +}; + +DEVICE_DT_INST_DEFINE(0, lfclk_init, NULL, + &lfclk_data, &lfclk_config, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &lfclk_drv_api); diff --git a/dts/arm/nordic/nrf51822.dtsi b/dts/arm/nordic/nrf51822.dtsi index 1d2bce6ed8d..cdeb0853b50 100644 --- a/dts/arm/nordic/nrf51822.dtsi +++ b/dts/arm/nordic/nrf51822.dtsi @@ -80,6 +80,13 @@ status = "okay"; }; + lfclk: lfclk@40000000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x40000000 0x1000>; + interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + nrf_mpu: nrf-mpu@40000000 { compatible = "nordic,nrf-mpu"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52805.dtsi b/dts/arm/nordic/nrf52805.dtsi index a0f37359d56..dc5fbce7895 100644 --- a/dts/arm/nordic/nrf52805.dtsi +++ b/dts/arm/nordic/nrf52805.dtsi @@ -68,6 +68,13 @@ status = "okay"; }; + lfclk: lfclk@40000000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52810.dtsi b/dts/arm/nordic/nrf52810.dtsi index 32315026121..425d5132b8b 100644 --- a/dts/arm/nordic/nrf52810.dtsi +++ b/dts/arm/nordic/nrf52810.dtsi @@ -72,6 +72,13 @@ status = "okay"; }; + lfclk: lfclk@40000000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52811.dtsi b/dts/arm/nordic/nrf52811.dtsi index 91258f52ea1..1f779fc6720 100644 --- a/dts/arm/nordic/nrf52811.dtsi +++ b/dts/arm/nordic/nrf52811.dtsi @@ -76,6 +76,13 @@ status = "okay"; }; + lfclk: lfclk@40000000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52820.dtsi b/dts/arm/nordic/nrf52820.dtsi index 8f68df4776e..1ccab1d279e 100644 --- a/dts/arm/nordic/nrf52820.dtsi +++ b/dts/arm/nordic/nrf52820.dtsi @@ -76,6 +76,13 @@ status = "okay"; }; + lfclk: lfclk@40000000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52832.dtsi b/dts/arm/nordic/nrf52832.dtsi index 70c0b4a8fb7..d7b3451a17f 100644 --- a/dts/arm/nordic/nrf52832.dtsi +++ b/dts/arm/nordic/nrf52832.dtsi @@ -72,6 +72,13 @@ status = "okay"; }; + lfclk: lfclk@40000000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52833.dtsi b/dts/arm/nordic/nrf52833.dtsi index 1b77bf4f94b..268ba9270c6 100644 --- a/dts/arm/nordic/nrf52833.dtsi +++ b/dts/arm/nordic/nrf52833.dtsi @@ -76,6 +76,13 @@ status = "okay"; }; + lfclk: lfclk@40000000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf52840.dtsi b/dts/arm/nordic/nrf52840.dtsi index 78e07e8beb3..e31b21fb402 100644 --- a/dts/arm/nordic/nrf52840.dtsi +++ b/dts/arm/nordic/nrf52840.dtsi @@ -72,6 +72,13 @@ status = "okay"; }; + lfclk: lfclk@40000000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x40000000 0x1000>; + interrupts = <0 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@40000000 { compatible = "nordic,nrf-power"; reg = <0x40000000 0x1000>; diff --git a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi index 9c51df186c2..1f47bc27664 100644 --- a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi +++ b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi @@ -75,6 +75,13 @@ hfclk: hfclk@5000 { status = "okay"; }; +lfclk: lfclk@5000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x5000 0x1000>; + interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; +}; + power: power@5000 { compatible = "nordic,nrf-power"; reg = <0x5000 0x1000>; diff --git a/dts/arm/nordic/nrf5340_cpunet.dtsi b/dts/arm/nordic/nrf5340_cpunet.dtsi index 92d33340952..054184fb6c4 100644 --- a/dts/arm/nordic/nrf5340_cpunet.dtsi +++ b/dts/arm/nordic/nrf5340_cpunet.dtsi @@ -69,6 +69,13 @@ status = "okay"; }; + lfclk: lfclk@41005000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x41005000 0x1000>; + interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; + }; + power: power@41005000 { compatible = "nordic,nrf-power"; reg = <0x41005000 0x1000>; diff --git a/dts/arm/nordic/nrf91_peripherals.dtsi b/dts/arm/nordic/nrf91_peripherals.dtsi index 66b817599e4..969c0925dc5 100644 --- a/dts/arm/nordic/nrf91_peripherals.dtsi +++ b/dts/arm/nordic/nrf91_peripherals.dtsi @@ -351,6 +351,13 @@ hfclk: hfclk@5000 { status = "okay"; }; +lfclk: lfclk@5000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x5000 0x1000>; + interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; +}; + power: power@5000 { compatible = "nordic,nrf-power"; reg = <0x5000 0x1000>; diff --git a/dts/bindings/clock/nordic,nrf-clock-lfclk.yaml b/dts/bindings/clock/nordic,nrf-clock-lfclk.yaml new file mode 100644 index 00000000000..78dca8e9c7c --- /dev/null +++ b/dts/bindings/clock/nordic,nrf-clock-lfclk.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Nordic nRF lfclk clock control node + +compatible: "nordic,nrf-clock-lfclk" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/dts/bindings/clock/nordic,nrf-lfclk.yaml b/dts/bindings/clock/nordic,nrf-lfclk.yaml index a0f95d6c927..b01ee2e699a 100644 --- a/dts/bindings/clock/nordic,nrf-lfclk.yaml +++ b/dts/bindings/clock/nordic,nrf-lfclk.yaml @@ -27,7 +27,7 @@ description: | clock-names = "hfxo", "lfxo"; }; -compatible: "nordic,nrf-lfclk" +compatible: "nordic,nrfs-lfclk" include: fixed-clock.yaml diff --git a/dts/vendor/nordic/nrf54h20.dtsi b/dts/vendor/nordic/nrf54h20.dtsi index 4644cb383ec..602c56a99dc 100644 --- a/dts/vendor/nordic/nrf54h20.dtsi +++ b/dts/vendor/nordic/nrf54h20.dtsi @@ -192,11 +192,10 @@ }; lfclk: lfclk { - compatible = "nordic,nrf-lfclk"; - status = "disabled"; + compatible = "nordic,nrfs-lfclk"; + status = "disabled"; #clock-cells = <0>; clock-frequency = <32768>; - status = "okay"; lfrc-accuracy-ppm = <500>; lflprc-accuracy-ppm = <1000>; lfrc-startup-time-us = <200>; /* To be measured */ diff --git a/dts/vendor/nordic/nrf54l_05_10_15.dtsi b/dts/vendor/nordic/nrf54l_05_10_15.dtsi index 65fb42a5621..68696c5f23c 100644 --- a/dts/vendor/nordic/nrf54l_05_10_15.dtsi +++ b/dts/vendor/nordic/nrf54l_05_10_15.dtsi @@ -646,6 +646,13 @@ status = "disabled"; }; + lfclk: lfclk@10e000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x10e000 0x1000>; + interrupts = <261 NRF_DEFAULT_IRQ_PRIORITY>; + status = "disabled"; + }; + power: power@10e000 { compatible = "nordic,nrf-power"; reg = <0x10e000 0x1000>; diff --git a/dts/vendor/nordic/nrf54lm20a.dtsi b/dts/vendor/nordic/nrf54lm20a.dtsi index 02268bcdbab..d9caefeef10 100644 --- a/dts/vendor/nordic/nrf54lm20a.dtsi +++ b/dts/vendor/nordic/nrf54lm20a.dtsi @@ -798,6 +798,13 @@ status = "disabled"; }; + lfclk: lfclk@10e000 { + compatible = "nordic,nrf-clock-lfclk"; + reg = <0x10e000 0x1000>; + interrupts = <261 NRF_DEFAULT_IRQ_PRIORITY>; + status = "disabled"; + }; + power: power@10e000 { compatible = "nordic,nrf-power"; reg = <0x10e000 0x1000>; diff --git a/modules/hal_nordic/nrfx/CMakeLists.txt b/modules/hal_nordic/nrfx/CMakeLists.txt index 6011fa5eda7..dc9bdd9d0a3 100644 --- a/modules/hal_nordic/nrfx/CMakeLists.txt +++ b/modules/hal_nordic/nrfx/CMakeLists.txt @@ -140,6 +140,7 @@ zephyr_library_sources_ifdef(CONFIG_NRFX_ADC ${SRC_DIR}/nrfx_adc.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_hfclk.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_xo.c) +zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_lfclk.c) zephyr_library_sources_ifdef(CONFIG_NRFX_COMP ${SRC_DIR}/nrfx_comp.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CRACEN ${SRC_DIR}/nrfx_cracen.c) zephyr_library_sources_ifdef(CONFIG_NRFX_DPPI ${SRC_DIR}/nrfx_dppi.c) diff --git a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h index 5b48d96aa32..a4f254cf00b 100644 --- a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h +++ b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h @@ -7,33 +7,31 @@ #include "device_subsys.h" #include -#ifndef CONFIG_SOC_NRF52832 -static const struct device_subsys_data subsys_data[] = { - /* On nrf52832 LF clock cannot be stopped because it leads - * to RTC COUNTER register reset and that is unexpected by - * system clock which is disrupted and may hang in the test. - */ - { - .subsys = CLOCK_CONTROL_NRF_SUBSYS_LF, - .startup_us = (CLOCK_CONTROL_NRF_K32SRC == - NRF_CLOCK_LFCLK_RC) ? 1000 : 500000 - } -}; -#endif /* !CONFIG_SOC_NRF52832 */ - +#if NRF_CLOCK_HAS_HFCLK static const struct device_subsys_data subsys_data_hfclk[] = { { .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, .startup_us = CONFIG_TEST_NRF_HF_STARTUP_TIME_US } }; - +#endif /* NRF_CLOCK_HAS_HFCLK */ +#if NRF_CLOCK_HAS_XO static const struct device_subsys_data subsys_data_xo[] = { { .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, .startup_us = CONFIG_TEST_NRF_HF_STARTUP_TIME_US } }; +#endif /* NRF_CLOCK_HAS_XO */ +#if !defined(CONFIG_SOC_NRF52832) +static const struct device_subsys_data subsys_data_lfclk[] = { + { + .subsys = CLOCK_CONTROL_NRF_SUBSYS_LF, + .startup_us = (CLOCK_CONTROL_NRF_K32SRC == + NRF_CLOCK_LFCLK_RC) ? 1000 : 500000 + } +}; +#endif /* !defined(CONFIG_SOC_NRF52832) */ static const struct device_data devices[] = { #if NRF_CLOCK_HAS_HFCLK @@ -51,10 +49,14 @@ static const struct device_data devices[] = { }, #endif /* NRF_CLOCK_HAS_XO */ #if !defined(CONFIG_SOC_NRF52832) + /* On nrf52832 LF clock cannot be stopped because it leads + * to RTC COUNTER register reset and that is unexpected by + * system clock which is disrupted and may hang in the test. + */ { - .dev = DEVICE_DT_GET_ONE(nordic_nrf_clock), - .subsys_data = subsys_data, - .subsys_cnt = ARRAY_SIZE(subsys_data) - } + .dev = DEVICE_DT_GET_ONE(nordic_nrf_clock_lfclk), + .subsys_data = subsys_data_lfclk, + .subsys_cnt = ARRAY_SIZE(subsys_data_lfclk) + }, #endif /* !defined(CONFIG_SOC_NRF52832) */ }; diff --git a/tests/drivers/clock_control/clock_control_api/src/test_clock_control.c b/tests/drivers/clock_control/clock_control_api/src/test_clock_control.c index 91af56bd3d2..1e490f26265 100644 --- a/tests/drivers/clock_control/clock_control_api/src/test_clock_control.c +++ b/tests/drivers/clock_control/clock_control_api/src/test_clock_control.c @@ -33,10 +33,7 @@ static void setup_instance(const struct device *dev, clock_control_subsys_t subs err = clock_control_off(dev, subsys); #if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock) if (err == -EPERM) { - struct onoff_manager *mgr = - z_nrf_clock_control_get_onoff(subsys); - - err = onoff_release(mgr); + err = nrf_clock_control_release(dev, NULL); if (err >= 0) { break; } @@ -45,28 +42,27 @@ static void setup_instance(const struct device *dev, clock_control_subsys_t subs } while (clock_control_get_status(dev, subsys) != CLOCK_CONTROL_STATUS_OFF); - LOG_INF("setup done"); + LOG_INF("setup done: %s", dev->name); } static void tear_down_instance(const struct device *dev, clock_control_subsys_t subsys) { -#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock) +#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_clock_lfclk) /* Turn on LF clock using onoff service if it is disabled. */ - const struct device *const clk = DEVICE_DT_GET_ONE(nordic_nrf_clock); + const struct device *const clk = DEVICE_DT_GET_ONE(nordic_nrf_clock_lfclk); struct onoff_client cli; - struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF); int err; zassert_true(device_is_ready(clk), "Clock dev is not ready"); - if (clock_control_get_status(clk, CLOCK_CONTROL_NRF_SUBSYS_LF) != + if (clock_control_get_status(clk, NULL) != CLOCK_CONTROL_STATUS_OFF) { return; } sys_notify_init_spinwait(&cli.notify); - err = onoff_request(mgr, &cli); + err = nrf_clock_control_request(clk, NULL, &cli); zassert_true(err >= 0, ""); while (sys_notify_fetch_result(&cli.notify, &err) < 0) { 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 83fdc98ca19..914c5be46bb 100644 --- a/tests/drivers/clock_control/nrf_clock_control/src/main.c +++ b/tests/drivers/clock_control/nrf_clock_control/src/main.c @@ -128,7 +128,7 @@ static const struct test_clk_context global_hsfll_test_clk_contexts[] = { }; #endif -#if defined(CONFIG_CLOCK_CONTROL_NRF_LFCLK) +#if defined(CONFIG_CLOCK_CONTROL_NRFS_LFCLK) const struct nrf_clock_spec test_clk_specs_lfclk[] = { { .frequency = 32768, @@ -351,7 +351,7 @@ ZTEST(nrf2_clock_control, test_global_hsfll_control) } #endif -#if defined(CONFIG_CLOCK_CONTROL_NRF_LFCLK) +#if defined(CONFIG_CLOCK_CONTROL_NRFS_LFCLK) ZTEST(nrf2_clock_control, test_lfclk_control) { TC_PRINT("LFCLK test\n"); From 8f98fdba809bbd24a1757e5a1b12cd10c2a57272 Mon Sep 17 00:00:00 2001 From: Michal Frankiewicz Date: Mon, 13 Oct 2025 11:50:14 +0200 Subject: [PATCH 4/4] [nrf fromlist] drivers: clock_control: Separated nrf hfclk192m shim from nrf clock shim. Separated clock_control_nrf_hfclk192m shim from clock_control_nrf shim. Upstream PR #: 97372 Signed-off-by: Michal Frankiewicz --- drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/Kconfig.nrf | 6 + .../clock_control_nrf_hfclk192m.c | 341 ++++++++++++++++++ .../nordic/nrf5340_cpuapp_peripherals.dtsi | 7 + .../clock/nordic,nrf-clock-hfclk192m.yaml | 15 + modules/hal_nordic/nrfx/CMakeLists.txt | 1 + .../clock_control_api/src/nrf_device_subsys.h | 15 + .../clock_control_api/testcase.yaml | 1 + 8 files changed, 387 insertions(+) create mode 100644 drivers/clock_control/clock_control_nrf_hfclk192m.c create mode 100644 dts/bindings/clock/nordic,nrf-clock-hfclk192m.yaml diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 1fc767aa2fd..db7880f06fc 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -63,6 +63,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_AUXPLL clock_cont zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_HFCLK clock_control_nrf_hfclk.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_XO clock_control_nrf_xo.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_LFCLK clock_control_nrf_lfclk.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_HFCLK192M clock_control_nrf_hfclk192m.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_COMMON clock_control_nrf_common.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL60X clock_control_bl60x.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BOUFFALOLAB_BL61X clock_control_bl61x.c) diff --git a/drivers/clock_control/Kconfig.nrf b/drivers/clock_control/Kconfig.nrf index 8497368b4c1..3a411c55b76 100644 --- a/drivers/clock_control/Kconfig.nrf +++ b/drivers/clock_control/Kconfig.nrf @@ -344,6 +344,12 @@ config CLOCK_CONTROL_NRF_LFCLK select CLOCK_CONTROL_NRF_COMMON default y +config CLOCK_CONTROL_NRF_HFCLK192M + bool "NRF HFCLK192M driver support" + depends on DT_HAS_NORDIC_NRF_CLOCK_HFCLK192M_ENABLED + select CLOCK_CONTROL_NRF_COMMON + default y + config CLOCK_CONTROL_NRF_AUXPLL bool "nRF Auxiliary PLL driver" default y diff --git a/drivers/clock_control/clock_control_nrf_hfclk192m.c b/drivers/clock_control/clock_control_nrf_hfclk192m.c new file mode 100644 index 00000000000..29d7ae84a02 --- /dev/null +++ b/drivers/clock_control/clock_control_nrf_hfclk192m.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2016-2020 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "nrf_clock_calibration.h" +#include "clock_control_nrf_common.h" +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(clock_control_hfclk192m, CONFIG_CLOCK_CONTROL_LOG_LEVEL); +//TODO check all defines if they are used +#define DT_DRV_COMPAT nordic_nrf_clock_hfclk192m + +#define CLOCK_DEVICE_HFCLK192M DEVICE_DT_GET(DT_NODELABEL(hfclk192m)) + +#define CTX_ONOFF BIT(6) +#define CTX_API BIT(7) +#define CTX_MASK (CTX_ONOFF | CTX_API) + +#define STATUS_MASK 0x7 +#define GET_STATUS(flags) (flags & STATUS_MASK) +#define GET_CTX(flags) (flags & CTX_MASK) + +/* Helper logging macros. */ +#ifdef CONFIG_LOG +#define CLOCK_LOG(lvl, dev, ...) \ + LOG_##lvl("%s: " GET_ARG_N(1, __VA_ARGS__), \ + "hfclk192m" \ + COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__),\ + (), (, GET_ARGS_LESS_N(1, __VA_ARGS__)))) +#else +#define CLOCK_LOG(...) +#endif + +#define ERR(dev, ...) CLOCK_LOG(ERR, dev, __VA_ARGS__) +#define WRN(dev, ...) CLOCK_LOG(WRN, dev, __VA_ARGS__) +#define INF(dev, ...) CLOCK_LOG(INF, dev, __VA_ARGS__) +#define DBG(dev, ...) CLOCK_LOG(DBG, dev, __VA_ARGS__) + +typedef void (*clk_ctrl_func_t)(void); + +typedef struct { + struct onoff_manager mgr; + clock_control_cb_t cb; + void *user_data; + uint32_t flags; +} hfclk192m_data_t; + +typedef struct { + clk_ctrl_func_t start; /* Clock start function */ + clk_ctrl_func_t stop; /* Clock stop function */ +#ifdef CONFIG_LOG + const char *name; +#endif +} hfclk192m_config_t; + +static int set_off_state(uint32_t *flags, uint32_t ctx) +{ + int err = 0; + unsigned int key = irq_lock(); + uint32_t current_ctx = GET_CTX(*flags); + + if ((current_ctx != 0) && (current_ctx != ctx)) { + err = -EPERM; + } else { + *flags = CLOCK_CONTROL_STATUS_OFF; + } + + irq_unlock(key); + + return err; +} + +static int set_starting_state(uint32_t *flags, uint32_t ctx) +{ + int err = 0; + unsigned int key = irq_lock(); + uint32_t current_ctx = GET_CTX(*flags); + + if ((*flags & (STATUS_MASK)) == CLOCK_CONTROL_STATUS_OFF) { + *flags = CLOCK_CONTROL_STATUS_STARTING | ctx; + } else if (current_ctx != ctx) { + err = -EPERM; + } else { + err = -EALREADY; + } + + irq_unlock(key); + + return err; +} + +static void set_on_state(uint32_t *flags) +{ + unsigned int key = irq_lock(); + + *flags = CLOCK_CONTROL_STATUS_ON | GET_CTX(*flags); + irq_unlock(key); +} + +static void clkstarted_handle(const struct device *dev) +{ + clock_control_cb_t callback = ((hfclk192m_data_t *)dev->data)->cb; + + ((hfclk192m_data_t *)dev->data)->cb = NULL; + set_on_state(&((hfclk192m_data_t *)dev->data)->flags); + DBG(dev, "Clock started"); + + if (callback) { + callback(dev, NULL, (hfclk192m_data_t *)dev->data)->user_data); + } +} + +static void hfclk192m_start(void) +{ + nrfx_clock_hfclk192m_start(); +} + +static void hfclk192m_stop(void) +{ + nrfx_clock_hfclk192m_stop(); +} + +static int stop(const struct device *dev, uint32_t ctx) +{ + int err; + + err = set_off_state(&((hfclk192m_data_t *)dev->data)->flags, ctx); + if (err < 0) { + return err; + } + + ((hfclk192m_config_t *)dev->config)->stop(); + + return 0; +} + +static int async_start(const struct device *dev, clock_control_cb_t cb, void *user_data, + uint32_t ctx) +{ + int err; + + err = set_starting_state(&((hfclk192m_data_t *)dev->data)->flags, ctx); + if (err < 0) { + return err; + } + + ((hfclk192m_data_t *)dev->data)->cb = cb; + ((hfclk192m_data_t *)dev->data)->user_data = user_data; + + ((hfclk192m_config_t *)dev->config)->start(); + + return 0; +} + +static void blocking_start_callback(const struct device *dev, + clock_control_subsys_t subsys, + void *user_data) +{ + ARG_UNUSED(subsys); + + struct k_sem *sem = user_data; + + k_sem_give(sem); +} + +static void onoff_stop(struct onoff_manager *mgr, + onoff_notify_fn notify) +{ + int res; + + res = stop(CLOCK_DEVICE_HFCLK192M, CTX_ONOFF); + notify(mgr, res); +} + +static void onoff_started_callback(const struct device *dev, + clock_control_subsys_t sys, + void *user_data) +{ + ARG_UNUSED(sys); + + onoff_notify_fn notify = user_data; + + notify(&((hfclk192m_data_t *)dev->data)->mgr, 0); +} + +static void onoff_start(struct onoff_manager *mgr, + onoff_notify_fn notify) +{ + int err; + + err = async_start(CLOCK_DEVICE_HFCLK192M, onoff_started_callback, notify, CTX_ONOFF); + if (err < 0) { + notify(mgr, err); + } +} + +static void clock_event_handler(void) +{ + const struct device *dev = CLOCK_DEVICE_HFCLK192M; + + clkstarted_handle(dev); +} + +static int api_start(const struct device *dev, clock_control_subsys_t subsys, + clock_control_cb_t cb, void *user_data) +{ + ARG_UNUSED(subsys); + + return async_start(dev, cb, user_data, CTX_API); +} + +static int api_blocking_start(const struct device *dev, + clock_control_subsys_t subsys) +{ + struct k_sem sem = Z_SEM_INITIALIZER(sem, 0, 1); + int err; + + if (!IS_ENABLED(CONFIG_MULTITHREADING)) { + return -ENOTSUP; + } + + err = api_start(dev, subsys, blocking_start_callback, &sem); + if (err < 0) { + return err; + } + + return k_sem_take(&sem, K_MSEC(500)); +} + +static int api_stop(const struct device *dev, clock_control_subsys_t subsys) +{ + ARG_UNUSED(subsys); + + return stop(dev, CTX_API); +} + +static enum clock_control_status api_get_status(const struct device *dev, + clock_control_subsys_t subsys) +{ + ARG_UNUSED(subsys); + + return GET_STATUS(((hfclk192m_data_t *)dev->data)->flags); +} + +static int api_request(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + hfclk192m_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_request(&dev_data->mgr, cli); +} + +static int api_release(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + hfclk192m_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_release(&dev_data->mgr); +} + +static int api_cancel_or_release(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + hfclk192m_data_t *dev_data = dev->data; + + ARG_UNUSED(spec); + + return onoff_cancel_or_release(&dev_data->mgr, cli); +} + +static int clk_init(const struct device *dev) +{ + nrfx_err_t nrfx_err; + int err; + static const struct onoff_transitions transitions = { + .start = onoff_start, + .stop = onoff_stop + }; + + clock_control_nrf_common_connect_irq(); + + nrfx_err = nrfx_clock_hfclk192m_init(clock_event_handler); + if (nrfx_err != NRFX_SUCCESS) { + return -EIO; + } + + err = onoff_manager_init(&((hfclk192m_data_t *)dev->data)->mgr, + &transitions); + if (err < 0) { + return err; + } + + ((hfclk192m_data_t *)dev->data)->flags = CLOCK_CONTROL_STATUS_OFF; + + return 0; +} + +CLOCK_CONTROL_NRF_IRQ_HANDLERS_ITERABLE(clock_control_nrf_hfclk192m, + &nrfx_clock_hfclk192m_irq_handler); + +static DEVICE_API(nrf_clock_control, clock_control_api) = { + .std_api = { + .on = api_blocking_start, + .off = api_stop, + .async_on = api_start, + .get_status = api_get_status, + }, + .request = api_request, + .release = api_release, + .cancel_or_release = api_cancel_or_release, +}; + +static hfclk192m_data_t data; + +static const hfclk192m_config_t config = { + .start = hfclk192m_start, + .stop = hfclk192m_stop, + IF_ENABLED(CONFIG_LOG, (.name = "hfclk192m",)) +}; + +DEVICE_DT_DEFINE(DT_NODELABEL(hfclk192m), clk_init, NULL, + &data, &config, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &clock_control_api); diff --git a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi index 1f47bc27664..1cb15d053e5 100644 --- a/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi +++ b/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi @@ -82,6 +82,13 @@ lfclk: lfclk@5000 { status = "okay"; }; +hfclk192m: hfclk192m@5000 { + compatible = "nordic,nrf-clock-hfclk192m"; + reg = <0x5000 0x1000>; + interrupts = <5 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; +}; + power: power@5000 { compatible = "nordic,nrf-power"; reg = <0x5000 0x1000>; diff --git a/dts/bindings/clock/nordic,nrf-clock-hfclk192m.yaml b/dts/bindings/clock/nordic,nrf-clock-hfclk192m.yaml new file mode 100644 index 00000000000..beba070cdee --- /dev/null +++ b/dts/bindings/clock/nordic,nrf-clock-hfclk192m.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Nordic nRF hfclk192m clock control node + +compatible: "nordic,nrf-clock-hfclk192m" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/modules/hal_nordic/nrfx/CMakeLists.txt b/modules/hal_nordic/nrfx/CMakeLists.txt index dc9bdd9d0a3..90552a4b6e6 100644 --- a/modules/hal_nordic/nrfx/CMakeLists.txt +++ b/modules/hal_nordic/nrfx/CMakeLists.txt @@ -141,6 +141,7 @@ zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_hfclk.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_xo.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_lfclk.c) +zephyr_library_sources_ifdef(CONFIG_NRFX_CLOCK ${SRC_DIR}/nrfx_clock_hfclk192m.c) zephyr_library_sources_ifdef(CONFIG_NRFX_COMP ${SRC_DIR}/nrfx_comp.c) zephyr_library_sources_ifdef(CONFIG_NRFX_CRACEN ${SRC_DIR}/nrfx_cracen.c) zephyr_library_sources_ifdef(CONFIG_NRFX_DPPI ${SRC_DIR}/nrfx_dppi.c) diff --git a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h index a4f254cf00b..29ec39598c0 100644 --- a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h +++ b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h @@ -32,6 +32,14 @@ static const struct device_subsys_data subsys_data_lfclk[] = { } }; #endif /* !defined(CONFIG_SOC_NRF52832) */ +#if NRF_CLOCK_HAS_HFCLK192M +static const struct device_subsys_data subsys_data_hfclk192m[] = { + { + .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF192M, + .startup_us = 5 + } +}; +#endif /* NRF_CLOCK_HAS_HFCLK192M */ static const struct device_data devices[] = { #if NRF_CLOCK_HAS_HFCLK @@ -59,4 +67,11 @@ static const struct device_data devices[] = { .subsys_cnt = ARRAY_SIZE(subsys_data_lfclk) }, #endif /* !defined(CONFIG_SOC_NRF52832) */ +#if NRF_CLOCK_HAS_HFCLK192M + { + .dev = DEVICE_DT_GET_ONE(nordic_nrf_clock_hfclk192m), + .subsys_data = subsys_data_hfclk192m, + .subsys_cnt = ARRAY_SIZE(subsys_data_hfclk192m) + }, +#endif /* NRF_CLOCK_HAS_HFCLK192M */ }; diff --git a/tests/drivers/clock_control/clock_control_api/testcase.yaml b/tests/drivers/clock_control/clock_control_api/testcase.yaml index d3c2669641f..9935de19458 100644 --- a/tests/drivers/clock_control/clock_control_api/testcase.yaml +++ b/tests/drivers/clock_control/clock_control_api/testcase.yaml @@ -21,6 +21,7 @@ tests: - nrf51dk/nrf51822 - nrf52dk/nrf52832 - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp - nrf9160dk/nrf9160 - nrf54l15dk/nrf54l15/cpuapp - nrf54lm20dk/nrf54lm20a/cpuapp