The new GRTC reading manner of the SYSCOUNTER uses hardware mechanism which allows to keep it alive when any of CPUs is not sleeping. Otherwise the SYSCOUNTER goes into sleep mode. Thus there is no longer need to maintain the `CONFIG_NRF_GRTC_SLEEP_ALLOWED` symbol, however if the user wants to have the SYSCOUNTER enabled all the time the `CONFIG_NRF_GRTC_ALWAYS_ON` can be used instead. The nrfx_grtc driver no longer provides the `wakeup-read-sleep` reading manner. Also setting the GRTC clock source is performed by the nrfx_grtc driver so it has been removed from the `sys_clock_driver_init()` function. Signed-off-by: Adam Kondraciuk <adam.kondraciuk@nordicsemi.no>
546 lines
14 KiB
C
546 lines
14 KiB
C
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/irq.h>
|
|
#if defined(CONFIG_CLOCK_CONTROL_NRF)
|
|
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
|
|
#endif
|
|
#include <zephyr/drivers/timer/system_timer.h>
|
|
#include <zephyr/drivers/timer/nrf_grtc_timer.h>
|
|
#include <nrfx_grtc.h>
|
|
#include <zephyr/sys/math_extras.h>
|
|
|
|
#define GRTC_NODE DT_NODELABEL(grtc)
|
|
|
|
/* Ensure that GRTC properties in devicetree are defined correctly. */
|
|
#if !DT_NODE_HAS_PROP(GRTC_NODE, owned_channels)
|
|
#error GRTC owned-channels DT property is not defined
|
|
#endif
|
|
#define OWNED_CHANNELS_MASK NRFX_CONFIG_MASK_DT(GRTC_NODE, owned_channels)
|
|
#define CHILD_OWNED_CHANNELS_MASK NRFX_CONFIG_MASK_DT(GRTC_NODE, child_owned_channels)
|
|
#if ((OWNED_CHANNELS_MASK | CHILD_OWNED_CHANNELS_MASK) != OWNED_CHANNELS_MASK)
|
|
#error GRTC child-owned-channels DT property must be a subset of owned-channels
|
|
#endif
|
|
|
|
#define CHAN_COUNT NRFX_GRTC_CONFIG_NUM_OF_CC_CHANNELS
|
|
#define EXT_CHAN_COUNT (CHAN_COUNT - 1)
|
|
|
|
#ifndef GRTC_SYSCOUNTERL_VALUE_Msk
|
|
#define GRTC_SYSCOUNTERL_VALUE_Msk GRTC_SYSCOUNTER_SYSCOUNTERL_VALUE_Msk
|
|
#endif
|
|
|
|
#ifndef GRTC_SYSCOUNTERH_VALUE_Msk
|
|
#define GRTC_SYSCOUNTERH_VALUE_Msk GRTC_SYSCOUNTER_SYSCOUNTERH_VALUE_Msk
|
|
#endif
|
|
|
|
#define MAX_CC_LATCH_WAIT_TIME_US 77
|
|
|
|
#define CYC_PER_TICK \
|
|
((uint64_t)sys_clock_hw_cycles_per_sec() / (uint64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC)
|
|
|
|
#define COUNTER_SPAN (GRTC_SYSCOUNTERL_VALUE_Msk | ((uint64_t)GRTC_SYSCOUNTERH_VALUE_Msk << 32))
|
|
#define MAX_TICKS \
|
|
(((COUNTER_SPAN / CYC_PER_TICK) > INT_MAX) ? INT_MAX : (COUNTER_SPAN / CYC_PER_TICK))
|
|
|
|
#define MAX_CYCLES (MAX_TICKS * CYC_PER_TICK)
|
|
|
|
#define LFCLK_FREQUENCY_HZ 32768
|
|
|
|
#if defined(CONFIG_TEST)
|
|
const int32_t z_sys_timer_irq_for_test = DT_IRQN(GRTC_NODE);
|
|
#endif
|
|
|
|
static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context);
|
|
|
|
static struct k_spinlock lock;
|
|
static uint64_t last_count; /* Time (SYSCOUNTER value) @last sys_clock_announce() */
|
|
static atomic_t int_mask;
|
|
static uint8_t ext_channels_allocated;
|
|
static nrfx_grtc_channel_t system_clock_channel_data = {
|
|
.handler = sys_clock_timeout_handler,
|
|
.p_context = NULL,
|
|
.channel = (uint8_t)-1,
|
|
};
|
|
|
|
#define IS_CHANNEL_ALLOWED_ASSERT(chan) \
|
|
__ASSERT_NO_MSG((NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK & (1UL << (chan))) && \
|
|
((chan) != system_clock_channel_data.channel))
|
|
|
|
static inline uint64_t counter_sub(uint64_t a, uint64_t b)
|
|
{
|
|
return (a - b);
|
|
}
|
|
|
|
static inline uint64_t counter(void)
|
|
{
|
|
uint64_t now;
|
|
nrfx_grtc_syscounter_get(&now);
|
|
return now;
|
|
}
|
|
|
|
static inline int get_comparator(uint32_t chan, uint64_t *cc)
|
|
{
|
|
nrfx_err_t result;
|
|
|
|
result = nrfx_grtc_syscounter_cc_value_read(chan, cc);
|
|
if (result != NRFX_SUCCESS) {
|
|
if (result != NRFX_ERROR_INVALID_PARAM) {
|
|
return -EAGAIN;
|
|
}
|
|
return -EPERM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Program a new callback <value> microseconds in the future
|
|
*/
|
|
static void system_timeout_set_relative(uint64_t value)
|
|
{
|
|
if (value <= NRF_GRTC_SYSCOUNTER_CCADD_MASK) {
|
|
nrfx_grtc_syscounter_cc_relative_set(&system_clock_channel_data, value, true,
|
|
NRFX_GRTC_CC_RELATIVE_SYSCOUNTER);
|
|
} else {
|
|
nrfx_grtc_syscounter_cc_absolute_set(&system_clock_channel_data, value + counter(),
|
|
true);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Program a new callback in the absolute time given by <value>
|
|
*/
|
|
static void system_timeout_set_abs(uint64_t value)
|
|
{
|
|
nrfx_grtc_syscounter_cc_absolute_set(&system_clock_channel_data, value,
|
|
true);
|
|
}
|
|
|
|
static bool compare_int_lock(int32_t chan)
|
|
{
|
|
atomic_val_t prev = atomic_and(&int_mask, ~BIT(chan));
|
|
|
|
nrfx_grtc_syscounter_cc_int_disable(chan);
|
|
|
|
return prev & BIT(chan);
|
|
}
|
|
|
|
static void compare_int_unlock(int32_t chan, bool key)
|
|
{
|
|
if (key) {
|
|
atomic_or(&int_mask, BIT(chan));
|
|
nrfx_grtc_syscounter_cc_int_enable(chan);
|
|
}
|
|
}
|
|
|
|
static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context)
|
|
{
|
|
ARG_UNUSED(id);
|
|
ARG_UNUSED(p_context);
|
|
uint64_t dticks;
|
|
uint64_t now = counter();
|
|
|
|
if (unlikely(now < cc_val)) {
|
|
return;
|
|
}
|
|
|
|
dticks = counter_sub(cc_val, last_count) / CYC_PER_TICK;
|
|
|
|
last_count += dticks * CYC_PER_TICK;
|
|
|
|
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
/* protection is not needed because we are in the GRTC interrupt
|
|
* so it won't get preempted by the interrupt.
|
|
*/
|
|
system_timeout_set_abs(last_count + CYC_PER_TICK);
|
|
}
|
|
|
|
sys_clock_announce((int32_t)dticks);
|
|
}
|
|
|
|
int32_t z_nrf_grtc_timer_chan_alloc(void)
|
|
{
|
|
uint8_t chan;
|
|
nrfx_err_t err_code;
|
|
|
|
/* Prevent allocating all available channels - one must be left for system purposes. */
|
|
if (ext_channels_allocated >= EXT_CHAN_COUNT) {
|
|
return -ENOMEM;
|
|
}
|
|
err_code = nrfx_grtc_channel_alloc(&chan);
|
|
if (err_code != NRFX_SUCCESS) {
|
|
return -ENOMEM;
|
|
}
|
|
ext_channels_allocated++;
|
|
return (int32_t)chan;
|
|
}
|
|
|
|
void z_nrf_grtc_timer_chan_free(int32_t chan)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
nrfx_err_t err_code = nrfx_grtc_channel_free(chan);
|
|
|
|
if (err_code == NRFX_SUCCESS) {
|
|
ext_channels_allocated--;
|
|
}
|
|
}
|
|
|
|
bool z_nrf_grtc_timer_compare_evt_check(int32_t chan)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
uint32_t event_address = nrfx_grtc_event_compare_address_get(chan);
|
|
|
|
return *(volatile uint32_t *)event_address != 0;
|
|
}
|
|
|
|
uint32_t z_nrf_grtc_timer_compare_evt_address_get(int32_t chan)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
return nrfx_grtc_event_compare_address_get(chan);
|
|
}
|
|
|
|
uint32_t z_nrf_grtc_timer_capture_task_address_get(int32_t chan)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
return nrfx_grtc_capture_task_address_get(chan);
|
|
}
|
|
|
|
uint64_t z_nrf_grtc_timer_read(void)
|
|
{
|
|
return counter();
|
|
}
|
|
|
|
bool z_nrf_grtc_timer_compare_int_lock(int32_t chan)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
return compare_int_lock(chan);
|
|
}
|
|
|
|
void z_nrf_grtc_timer_compare_int_unlock(int32_t chan, bool key)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
compare_int_unlock(chan, key);
|
|
}
|
|
|
|
int z_nrf_grtc_timer_compare_read(int32_t chan, uint64_t *val)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
return get_comparator(chan, val);
|
|
}
|
|
|
|
static int compare_set_nolocks(int32_t chan, uint64_t target_time,
|
|
z_nrf_grtc_timer_compare_handler_t handler, void *user_data)
|
|
{
|
|
nrfx_err_t result;
|
|
|
|
__ASSERT_NO_MSG(target_time < COUNTER_SPAN);
|
|
nrfx_grtc_channel_t user_channel_data = {
|
|
.handler = handler,
|
|
.p_context = user_data,
|
|
.channel = chan,
|
|
};
|
|
result = nrfx_grtc_syscounter_cc_absolute_set(&user_channel_data, target_time, true);
|
|
if (result != NRFX_SUCCESS) {
|
|
return -EPERM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int compare_set(int32_t chan, uint64_t target_time,
|
|
z_nrf_grtc_timer_compare_handler_t handler, void *user_data)
|
|
{
|
|
bool key = compare_int_lock(chan);
|
|
int ret = compare_set_nolocks(chan, target_time, handler, user_data);
|
|
|
|
compare_int_unlock(chan, key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int z_nrf_grtc_timer_set(int32_t chan, uint64_t target_time,
|
|
z_nrf_grtc_timer_compare_handler_t handler, void *user_data)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
return compare_set(chan, target_time, (nrfx_grtc_cc_handler_t)handler, user_data);
|
|
}
|
|
|
|
void z_nrf_grtc_timer_abort(int32_t chan)
|
|
{
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
bool key = compare_int_lock(chan);
|
|
(void)nrfx_grtc_syscounter_cc_disable(chan);
|
|
compare_int_unlock(chan, key);
|
|
}
|
|
|
|
uint64_t z_nrf_grtc_timer_get_ticks(k_timeout_t t)
|
|
{
|
|
uint64_t curr_time;
|
|
int64_t curr_tick;
|
|
int64_t result;
|
|
int64_t abs_ticks;
|
|
int64_t grtc_ticks;
|
|
|
|
curr_time = counter();
|
|
curr_tick = sys_clock_tick_get();
|
|
|
|
grtc_ticks = t.ticks * CYC_PER_TICK;
|
|
abs_ticks = Z_TICK_ABS(t.ticks);
|
|
if (abs_ticks < 0) {
|
|
/* relative timeout */
|
|
return (grtc_ticks > (int64_t)COUNTER_SPAN) ?
|
|
-EINVAL : (curr_time + grtc_ticks);
|
|
}
|
|
|
|
/* absolute timeout */
|
|
result = (abs_ticks - curr_tick) * CYC_PER_TICK;
|
|
|
|
if (result > (int64_t)COUNTER_SPAN) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return curr_time + result;
|
|
}
|
|
|
|
int z_nrf_grtc_timer_capture_prepare(int32_t chan)
|
|
{
|
|
nrfx_grtc_channel_t user_channel_data = {
|
|
.handler = NULL,
|
|
.p_context = NULL,
|
|
.channel = chan,
|
|
};
|
|
nrfx_err_t result;
|
|
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
/* Set the CC value to mark channel as not triggered and also to enable it
|
|
* (makes CCEN=1). COUNTER_SPAN is used so as not to fire an event unnecessarily
|
|
* - it can be assumed that such a large value will never be reached.
|
|
*/
|
|
result = nrfx_grtc_syscounter_cc_absolute_set(&user_channel_data, COUNTER_SPAN, false);
|
|
|
|
if (result != NRFX_SUCCESS) {
|
|
return -EPERM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int z_nrf_grtc_timer_capture_read(int32_t chan, uint64_t *captured_time)
|
|
{
|
|
/* TODO: The implementation should probably go to nrfx_grtc and this
|
|
* should be just a wrapper for some nrfx_grtc_syscounter_capture_read.
|
|
*/
|
|
|
|
uint64_t capt_time;
|
|
nrfx_err_t result;
|
|
|
|
IS_CHANNEL_ALLOWED_ASSERT(chan);
|
|
|
|
/* TODO: Use `nrfy_grtc_sys_counter_enable_check` when available (NRFX-2480) */
|
|
if (NRF_GRTC->CC[chan].CCEN == GRTC_CC_CCEN_ACTIVE_Enable) {
|
|
/* If the channel is enabled (.CCEN), it means that there was no capture
|
|
* triggering event.
|
|
*/
|
|
return -EBUSY;
|
|
}
|
|
result = nrfx_grtc_syscounter_cc_value_read(chan, &capt_time);
|
|
if (result != NRFX_SUCCESS) {
|
|
return -EPERM;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(capt_time < COUNTER_SPAN);
|
|
|
|
*captured_time = capt_time;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_POWEROFF) && defined(CONFIG_NRF_GRTC_START_SYSCOUNTER)
|
|
int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us)
|
|
{
|
|
nrfx_err_t err_code;
|
|
static uint8_t systemoff_channel;
|
|
uint64_t now = counter();
|
|
nrfx_grtc_sleep_config_t sleep_cfg;
|
|
/* Minimum time that ensures valid execution of system-off procedure. */
|
|
uint32_t minimum_latency_us;
|
|
uint32_t chan;
|
|
int ret;
|
|
|
|
nrfx_grtc_sleep_configuration_get(&sleep_cfg);
|
|
minimum_latency_us = (sleep_cfg.waketime + sleep_cfg.timeout) *
|
|
USEC_PER_SEC / LFCLK_FREQUENCY_HZ +
|
|
CONFIG_NRF_GRTC_SYSCOUNTER_SLEEP_MINIMUM_LATENCY;
|
|
sleep_cfg.auto_mode = false;
|
|
nrfx_grtc_sleep_configure(&sleep_cfg);
|
|
|
|
if (minimum_latency_us > wake_time_us) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
k_spinlock_key_t key = k_spin_lock(&lock);
|
|
|
|
err_code = nrfx_grtc_channel_alloc(&systemoff_channel);
|
|
if (err_code != NRFX_SUCCESS) {
|
|
k_spin_unlock(&lock, key);
|
|
return -ENOMEM;
|
|
}
|
|
(void)nrfx_grtc_syscounter_cc_int_disable(systemoff_channel);
|
|
ret = compare_set(systemoff_channel,
|
|
now + wake_time_us * sys_clock_hw_cycles_per_sec() / USEC_PER_SEC, NULL,
|
|
NULL);
|
|
if (ret < 0) {
|
|
k_spin_unlock(&lock, key);
|
|
return ret;
|
|
}
|
|
|
|
for (uint32_t grtc_chan_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK;
|
|
grtc_chan_mask > 0; grtc_chan_mask &= ~BIT(chan)) {
|
|
/* Clear all GRTC channels except the systemoff_channel. */
|
|
chan = u32_count_trailing_zeros(grtc_chan_mask);
|
|
if (chan != systemoff_channel) {
|
|
nrfx_grtc_syscounter_cc_disable(chan);
|
|
}
|
|
}
|
|
|
|
/* Make sure that wake_time_us was not triggered yet. */
|
|
if (nrfx_grtc_syscounter_compare_event_check(systemoff_channel)) {
|
|
k_spin_unlock(&lock, key);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* This mechanism ensures that stored CC value is latched. */
|
|
uint32_t wait_time =
|
|
nrfy_grtc_timeout_get(NRF_GRTC) * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 32768 +
|
|
MAX_CC_LATCH_WAIT_TIME_US;
|
|
k_busy_wait(wait_time);
|
|
#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lfxo)) && NRF_GRTC_HAS_CLKSEL
|
|
nrfx_grtc_clock_source_set(NRF_GRTC_CLKSEL_LFXO);
|
|
#endif
|
|
k_spin_unlock(&lock, key);
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_POWEROFF */
|
|
|
|
uint32_t sys_clock_cycle_get_32(void)
|
|
{
|
|
k_spinlock_key_t key = k_spin_lock(&lock);
|
|
uint32_t ret = (uint32_t)counter();
|
|
|
|
k_spin_unlock(&lock, key);
|
|
return ret;
|
|
}
|
|
|
|
uint64_t sys_clock_cycle_get_64(void)
|
|
{
|
|
k_spinlock_key_t key = k_spin_lock(&lock);
|
|
uint64_t ret = counter();
|
|
|
|
k_spin_unlock(&lock, key);
|
|
return ret;
|
|
}
|
|
|
|
uint32_t sys_clock_elapsed(void)
|
|
{
|
|
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
return 0;
|
|
}
|
|
|
|
return (uint32_t)(counter_sub(counter(), last_count) / CYC_PER_TICK);
|
|
}
|
|
|
|
static int sys_clock_driver_init(void)
|
|
{
|
|
nrfx_err_t err_code;
|
|
|
|
IRQ_CONNECT(DT_IRQN(GRTC_NODE), DT_IRQ(GRTC_NODE, priority), nrfx_isr,
|
|
nrfx_grtc_irq_handler, 0);
|
|
|
|
err_code = nrfx_grtc_init(0);
|
|
if (err_code != NRFX_SUCCESS) {
|
|
return -EPERM;
|
|
}
|
|
|
|
#if defined(CONFIG_NRF_GRTC_START_SYSCOUNTER)
|
|
err_code = nrfx_grtc_syscounter_start(true, &system_clock_channel_data.channel);
|
|
if (err_code != NRFX_SUCCESS) {
|
|
return err_code == NRFX_ERROR_NO_MEM ? -ENOMEM : -EPERM;
|
|
}
|
|
#else
|
|
err_code = nrfx_grtc_channel_alloc(&system_clock_channel_data.channel);
|
|
if (err_code != NRFX_SUCCESS) {
|
|
return -ENOMEM;
|
|
}
|
|
#endif /* CONFIG_NRF_GRTC_START_SYSCOUNTER */
|
|
|
|
int_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK;
|
|
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
system_timeout_set_relative(CYC_PER_TICK);
|
|
}
|
|
|
|
#if defined(CONFIG_CLOCK_CONTROL_NRF)
|
|
static const enum nrf_lfclk_start_mode mode =
|
|
IS_ENABLED(CONFIG_SYSTEM_CLOCK_NO_WAIT)
|
|
? CLOCK_CONTROL_NRF_LF_START_NOWAIT
|
|
: (IS_ENABLED(CONFIG_SYSTEM_CLOCK_WAIT_FOR_AVAILABILITY)
|
|
? CLOCK_CONTROL_NRF_LF_START_AVAILABLE
|
|
: CLOCK_CONTROL_NRF_LF_START_STABLE);
|
|
|
|
z_nrf_clock_control_lf_on(mode);
|
|
#endif
|
|
|
|
#if defined(CONFIG_NRF_GRTC_TIMER_CLOCK_MANAGEMENT) && \
|
|
DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lfxo)) && NRF_GRTC_HAS_CLKSEL
|
|
/* Switch to LFXO as the low-frequency clock source. */
|
|
nrfx_grtc_clock_source_set(NRF_GRTC_CLKSEL_LFXO);
|
|
#endif
|
|
|
|
#if defined(CONFIG_NRF_GRTC_ALWAYS_ON)
|
|
nrfx_grtc_active_request_set(true);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void sys_clock_set_timeout(int32_t ticks, bool idle)
|
|
{
|
|
ARG_UNUSED(idle);
|
|
|
|
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
return;
|
|
}
|
|
|
|
ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : MIN(MAX_TICKS, MAX(ticks, 0));
|
|
|
|
uint64_t delta_time = ticks * CYC_PER_TICK;
|
|
|
|
uint64_t target_time = counter() + delta_time;
|
|
|
|
/* Rounded down target_time to the tick boundary
|
|
* (but not less than one tick after the last)
|
|
*/
|
|
target_time = MAX((target_time - last_count)/CYC_PER_TICK, 1)*CYC_PER_TICK + last_count;
|
|
|
|
system_timeout_set_abs(target_time);
|
|
}
|
|
|
|
#if defined(CONFIG_NRF_GRTC_TIMER_APP_DEFINED_INIT)
|
|
int nrf_grtc_timer_clock_driver_init(void)
|
|
{
|
|
return sys_clock_driver_init();
|
|
}
|
|
#else
|
|
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
|
|
#endif
|