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) */ };