Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reapply nrf2 clock control #1981

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-common.dtsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "nrf54h20dk_nrf54h20-memory_map.dtsi"
#include "nrf54h20dk_nrf54h20-ipc_conf.dtsi"
#include "nrf54h20dk_nrf54h20-pinctrl.dtsi"

&hfxo {
status = "okay";
accuracy-ppm = <30>;
startup-time-us = <850>;
mode = "crystal";
};

&lfxo {
status = "okay";
accuracy-ppm = <20>;
startup-time-us = <600000>;
mode = "crystal";
};
4 changes: 1 addition & 3 deletions boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
/dts-v1/;

#include <nordic/nrf54h20_cpuapp.dtsi>
#include "nrf54h20dk_nrf54h20-memory_map.dtsi"
#include "nrf54h20dk_nrf54h20-ipc_conf.dtsi"
#include "nrf54h20dk_nrf54h20-pinctrl.dtsi"
#include "nrf54h20dk_nrf54h20-common.dtsi"
#include "nrf54h20dk_bicr.dtsi"

/delete-node/ &cpurad_cpusys_ipc;
Expand Down
4 changes: 1 addition & 3 deletions boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuppr.dts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
/dts-v1/;

#include <nordic/nrf54h20_cpuppr.dtsi>
#include "nrf54h20dk_nrf54h20-memory_map.dtsi"
#include "nrf54h20dk_nrf54h20-ipc_conf.dtsi"
#include "nrf54h20dk_nrf54h20-pinctrl.dtsi"
#include "nrf54h20dk_nrf54h20-common.dtsi"

/delete-node/ &cpuapp_cpurad_ipc;
/delete-node/ &cpuapp_cpusys_ipc;
Expand Down
4 changes: 1 addition & 3 deletions boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpurad.dts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
/dts-v1/;

#include <nordic/nrf54h20_cpurad.dtsi>
#include "nrf54h20dk_nrf54h20-memory_map.dtsi"
#include "nrf54h20dk_nrf54h20-ipc_conf.dtsi"
#include "nrf54h20dk_nrf54h20-pinctrl.dtsi"
#include "nrf54h20dk_nrf54h20-common.dtsi"

/delete-node/ &cpuapp_cpuppr_ipc;
/delete-node/ &cpuapp_cpusys_ipc;
Expand Down
7 changes: 7 additions & 0 deletions drivers/clock_control/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_AMBIQ clock_cont
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_PWM clock_control_pwm.c)
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RPI_PICO clock_control_rpi_pico.c)

if(CONFIG_CLOCK_CONTROL_NRF2)
zephyr_library_sources(clock_control_nrf2_common.c)
zephyr_library_sources(clock_control_nrf2_fll16m.c)
zephyr_library_sources(clock_control_nrf2_hfxo.c)
zephyr_library_sources(clock_control_nrf2_hsfll.c)
zephyr_library_sources(clock_control_nrf2_lfclk.c)
endif()

if(CONFIG_CLOCK_CONTROL_STM32_CUBE)
zephyr_library_sources_ifdef(CONFIG_CLOCK_STM32_MUX clock_stm32_mux.c)
Expand Down
21 changes: 21 additions & 0 deletions drivers/clock_control/Kconfig.nrf
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,24 @@ config CLOCK_CONTROL_NRF_ACCURACY
default 20 if CLOCK_CONTROL_NRF_K32SRC_20PPM

endif # CLOCK_CONTROL_NRF

config CLOCK_CONTROL_NRF2
bool "nRF clock control support"
depends on SOC_SERIES_NRF54HX && !RISCV_CORE_NORDIC_VPR
select ONOFF
select NRFS if HAS_NRFS
imply NRFS_LOCAL_DOMAIN_DVFS_SCALE_DOWN_AFTER_INIT if NRFS_DVFS_LOCAL_DOMAIN
help
Support for nRF clock control devices.

if CLOCK_CONTROL_NRF2

config CLOCK_CONTROL_NRF2_NRFS_DVFS_TIMEOUT_MS
int "Timeout waiting for nrfs dvfs service callback in milliseconds"
default 2000

config CLOCK_CONTROL_NRF2_NRFS_CLOCK_TIMEOUT_MS
int "Timeout waiting for nrfs clock service callback in milliseconds"
default 1000

endif # CLOCK_CONTROL_NRF2
198 changes: 198 additions & 0 deletions drivers/clock_control/clock_control_nrf2_common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/

#include "clock_control_nrf2_common.h"
#include <zephyr/kernel.h>
#include <hal/nrf_lrcconf.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL);

#define FLAG_UPDATE_IN_PROGRESS BIT(FLAGS_COMMON_BITS - 1)
#define FLAG_UPDATE_NEEDED BIT(FLAGS_COMMON_BITS - 2)

#define ONOFF_CNT_MAX (FLAGS_COMMON_BITS - 2)

#define CONTAINER_OF_ITEM(ptr, idx, type, array) \
(type *)((char *)ptr - \
(idx * sizeof(array[0])) - \
offsetof(type, array[0]))

/*
* Definition of `struct clock_config_generic`.
* Used to access `clock_config_*` structures in a common way.
*/
NRF2_STRUCT_CLOCK_CONFIG(generic, ONOFF_CNT_MAX);

static sys_slist_t poweron_main_list;
static struct k_spinlock poweron_main_lock;

static void update_config(struct clock_config_generic *cfg)
{
atomic_val_t prev_flags = atomic_or(&cfg->flags, FLAG_UPDATE_NEEDED);

/* If the update work is already scheduled (FLAG_UPDATE_NEEDED was
* set before the above OR operation) or is currently being executed,
* it is not to be submitted again. In the latter case, it will be
* submitted by nrf2_clock_config_update_end().
*/
if (prev_flags & (FLAG_UPDATE_NEEDED | FLAG_UPDATE_IN_PROGRESS)) {
return;
}

k_work_submit(&cfg->work);
}

static void onoff_start_option(struct onoff_manager *mgr,
onoff_notify_fn notify)
{
struct clock_onoff *onoff =
CONTAINER_OF(mgr, struct clock_onoff, mgr);
struct clock_config_generic *cfg =
CONTAINER_OF_ITEM(onoff, onoff->idx,
struct clock_config_generic, onoff);

onoff->notify = notify;

(void)atomic_or(&cfg->flags, BIT(onoff->idx));
update_config(cfg);
}

static void onoff_stop_option(struct onoff_manager *mgr,
onoff_notify_fn notify)
{
struct clock_onoff *onoff =
CONTAINER_OF(mgr, struct clock_onoff, mgr);
struct clock_config_generic *cfg =
CONTAINER_OF_ITEM(onoff, onoff->idx,
struct clock_config_generic, onoff);

(void)atomic_and(&cfg->flags, ~BIT(onoff->idx));
update_config(cfg);

notify(mgr, 0);
}

static inline uint8_t get_index_of_highest_bit(uint32_t value)
{
return value ? (uint8_t)(31 - __builtin_clz(value)) : 0;
}

int nrf2_clock_config_init(void *clk_cfg, uint8_t onoff_cnt,
k_work_handler_t update_work_handler)
{
struct clock_config_generic *cfg = clk_cfg;

__ASSERT_NO_MSG(onoff_cnt <= ONOFF_CNT_MAX);

for (int i = 0; i < onoff_cnt; ++i) {
static const struct onoff_transitions transitions = {
.start = onoff_start_option,
.stop = onoff_stop_option
};
int rc;

rc = onoff_manager_init(&cfg->onoff[i].mgr, &transitions);
if (rc < 0) {
return rc;
}

cfg->onoff[i].idx = (uint8_t)i;
}

cfg->onoff_cnt = onoff_cnt;

k_work_init(&cfg->work, update_work_handler);

return 0;
}

uint8_t nrf2_clock_config_update_begin(struct k_work *work)
{
struct clock_config_generic *cfg =
CONTAINER_OF(work, struct clock_config_generic, work);
uint32_t active_options;

(void)atomic_or(&cfg->flags, FLAG_UPDATE_IN_PROGRESS);
cfg->flags_snapshot = atomic_and(&cfg->flags, ~FLAG_UPDATE_NEEDED);

active_options = cfg->flags_snapshot & BIT_MASK(ONOFF_CNT_MAX);
return get_index_of_highest_bit(active_options);
}

void nrf2_clock_config_update_end(void *clk_cfg, int status)
{
struct clock_config_generic *cfg = clk_cfg;
atomic_val_t prev_flags;

prev_flags = atomic_and(&cfg->flags, ~FLAG_UPDATE_IN_PROGRESS);
if (!(prev_flags & FLAG_UPDATE_IN_PROGRESS)) {
return;
}

for (int i = 0; i < cfg->onoff_cnt; ++i) {
if (cfg->flags_snapshot & BIT(i)) {
onoff_notify_fn notify = cfg->onoff[i].notify;

if (notify) {
/* If an option was to be activated now
* (it is waiting for a notification) and
* the activation failed, this option's flag
* must be cleared (the option can no longer
* be considered active).
*/
if (status < 0) {
(void)atomic_and(&cfg->flags, ~BIT(i));
}

cfg->onoff[i].notify = NULL;
notify(&cfg->onoff[i].mgr, status);
}
}
}

if (prev_flags & FLAG_UPDATE_NEEDED) {
k_work_submit(&cfg->work);
}
}

int api_nosys_on_off(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(dev);
ARG_UNUSED(sys);

return -ENOSYS;
}

void nrf2_clock_request_lrcconf_poweron_main(struct nrf2_clock_lrcconf_sink *sink)
{
K_SPINLOCK(&poweron_main_lock) {
if (sys_slist_len(&poweron_main_list) == 0) {
LOG_DBG("%s forced on", "main domain");
NRF_LRCCONF010->POWERON &= ~LRCCONF_POWERON_MAIN_Msk;
NRF_LRCCONF010->POWERON |= LRCCONF_POWERON_MAIN_AlwaysOn;
}

sys_slist_find_and_remove(&poweron_main_list, &sink->node);
sys_slist_append(&poweron_main_list, &sink->node);
}
}

void nrf2_clock_release_lrcconf_poweron_main(struct nrf2_clock_lrcconf_sink *sink)
{
K_SPINLOCK(&poweron_main_lock) {
if (!sys_slist_find_and_remove(&poweron_main_list, &sink->node)) {
K_SPINLOCK_BREAK;
}

if (sys_slist_len(&poweron_main_list) > 0) {
K_SPINLOCK_BREAK;
}

LOG_DBG("%s automatic", "main domain");
NRF_LRCCONF010->POWERON &= ~LRCCONF_POWERON_MAIN_Msk;
NRF_LRCCONF010->POWERON |= LRCCONF_POWERON_MAIN_Automatic;
}
}
89 changes: 89 additions & 0 deletions drivers/clock_control/clock_control_nrf2_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_DRIVERS_CLOCK_CONTROL_NRF2_COMMON_H_
#define ZEPHYR_DRIVERS_CLOCK_CONTROL_NRF2_COMMON_H_

#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/onoff.h>

#define FLAGS_COMMON_BITS 10

struct clock_onoff {
struct onoff_manager mgr;
onoff_notify_fn notify;
uint8_t idx;
};

/**
* @brief Defines a type for specific clock configuration structure.
*
* @param type suffix added clock_config_ to form the type name.
* @param _onoff_cnt number of clock configuration options to be handled;
* for each one a separate onoff manager instance is used.
*/
#define NRF2_STRUCT_CLOCK_CONFIG(type, _onoff_cnt) \
struct clock_config_##type { \
atomic_t flags; \
uint32_t flags_snapshot; \
struct k_work work; \
uint8_t onoff_cnt; \
struct clock_onoff onoff[_onoff_cnt]; \
}

/**
* @brief Initializes a clock configuration structure.
*
* @param clk_cfg pointer to the structure to be initialized.
* @param onoff_cnt number of clock configuration options handled
* handled by the structure.
* @param update_work_handler function that performs configuration update,
* called from the system work queue.
*
* @return 0 on success, negative value when onoff initialization fails.
*/
int nrf2_clock_config_init(void *clk_cfg, uint8_t onoff_cnt,
k_work_handler_t update_work_handler);

/**
* @brief Starts a clock configuration update.
*
* This function is supposed to be called by a specific clock control driver
* from its update work handler.
*
* @param work pointer to the work item received by the update work handler.
*
* @return index of the clock configuration onoff option to be activated.
*/
uint8_t nrf2_clock_config_update_begin(struct k_work *work);

/**
* @brief Finalizes a clock configuration update.
*
* Notifies all relevant onoff managers about the update result.
* Only the first call after each nrf2_clock_config_update_begin() performs
* the actual operation. Any further calls are simply no-ops.
*
* @param clk_cfg pointer to the clock configuration structure.
* @param status result to be passed to onoff managers.
*/
void nrf2_clock_config_update_end(void *clk_cfg, int status);

int api_nosys_on_off(const struct device *dev, clock_control_subsys_t sys);

struct nrf2_clock_lrcconf_sink {
sys_snode_t node;
};

/**
* @brief Request or release lrcconf main power domain
*/
void nrf2_clock_request_lrcconf_poweron_main(struct nrf2_clock_lrcconf_sink *sink);
void nrf2_clock_release_lrcconf_poweron_main(struct nrf2_clock_lrcconf_sink *sink);

#endif /* ZEPHYR_DRIVERS_CLOCK_CONTROL_NRF2_COMMON_H_ */
Loading
Loading