1 /*
2 * Copyright (c) 2024 Silicon Laboratories Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10
11 #include <zephyr/init.h>
12 #include <zephyr/device.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/sys_clock.h>
15 #include <zephyr/drivers/timer/system_timer.h>
16 #include <zephyr/logging/log.h>
17
18 #include <sl_sleeptimer.h>
19
20 LOG_MODULE_REGISTER(silabs_sleeptimer_timer);
21
22 /* Maximum time interval between timer interrupts (in hw_cycles) */
23 #define MAX_TIMEOUT_CYC (UINT32_MAX >> 1)
24
25 #define DT_RTC DT_CHOSEN(silabs_sleeptimer)
26
27 /* Ensure interrupt names don't expand to register interface struct pointers */
28 #undef RTCC
29
30 /* With CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME, this global variable holds the clock frequency,
31 * and must be written by the driver at init.
32 */
33 extern unsigned int z_clock_hw_cycles_per_sec;
34
35 /* Global timer state */
36 struct sleeptimer_timer_data {
37 uint32_t cyc_per_tick; /* Number of hw_cycles per 1 kernel tick */
38 uint32_t max_timeout_ticks; /* MAX_TIMEOUT_CYC expressed as ticks */
39 atomic_t last_count; /* Value of counter when the previous tick was announced */
40 struct k_spinlock lock; /* Spinlock to sync between ISR and updating the timeout */
41 bool initialized; /* Set to true when timer is initialized */
42 sl_sleeptimer_timer_handle_t handle; /* Timer handle for system timer */
43 };
44 static struct sleeptimer_timer_data g_sleeptimer_timer_data = {0};
45
sleeptimer_cb(sl_sleeptimer_timer_handle_t * handle,void * data)46 static void sleeptimer_cb(sl_sleeptimer_timer_handle_t *handle, void *data)
47 {
48 ARG_UNUSED(handle);
49 struct sleeptimer_timer_data *timer = data;
50
51 uint32_t curr = sl_sleeptimer_get_tick_count();
52 uint32_t prev = atomic_get(&timer->last_count);
53 uint32_t pending = curr - prev;
54
55 /* Number of unannounced ticks since the last announcement */
56 uint32_t unannounced = pending / timer->cyc_per_tick;
57
58 atomic_set(&timer->last_count, prev + unannounced * timer->cyc_per_tick);
59
60 sys_clock_announce(unannounced);
61 }
62
sleeptimer_clock_set_timeout(int32_t ticks,struct sleeptimer_timer_data * timer)63 static void sleeptimer_clock_set_timeout(int32_t ticks, struct sleeptimer_timer_data *timer)
64 {
65 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
66 return;
67 }
68
69 ticks = (ticks == K_TICKS_FOREVER) ? timer->max_timeout_ticks : ticks;
70 ticks = CLAMP(ticks, 0, timer->max_timeout_ticks);
71
72 k_spinlock_key_t key = k_spin_lock(&timer->lock);
73
74 uint32_t curr = sl_sleeptimer_get_tick_count();
75 uint32_t pending = curr % timer->cyc_per_tick;
76 uint32_t next = ticks * timer->cyc_per_tick;
77
78 /* Next timeout is N ticks in the future, minus the current progress
79 * into the tick if using multiple cycles per tick. The HAL API must
80 * be called with a timeout of at least one cycle.
81 */
82 if (next == 0) {
83 next = timer->cyc_per_tick;
84 }
85 next -= pending;
86
87 sl_sleeptimer_restart_timer(&timer->handle, next, sleeptimer_cb, timer, 0, 0);
88 k_spin_unlock(&timer->lock, key);
89 }
90
sleeptimer_clock_elapsed(struct sleeptimer_timer_data * timer)91 static uint32_t sleeptimer_clock_elapsed(struct sleeptimer_timer_data *timer)
92 {
93 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL) || !timer->initialized) {
94 /* No unannounced ticks can have elapsed if not in tickless mode */
95 return 0;
96 } else {
97 return (sl_sleeptimer_get_tick_count() - atomic_get(&timer->last_count)) /
98 timer->cyc_per_tick;
99 }
100 }
101
sys_clock_set_timeout(int32_t ticks,bool idle)102 void sys_clock_set_timeout(int32_t ticks, bool idle)
103 {
104 ARG_UNUSED(idle);
105
106 sleeptimer_clock_set_timeout(ticks, &g_sleeptimer_timer_data);
107 }
108
sys_clock_elapsed(void)109 uint32_t sys_clock_elapsed(void)
110 {
111 return sleeptimer_clock_elapsed(&g_sleeptimer_timer_data);
112 }
113
sys_clock_cycle_get_32(void)114 uint32_t sys_clock_cycle_get_32(void)
115 {
116 return g_sleeptimer_timer_data.initialized ? sl_sleeptimer_get_tick_count() : 0;
117 }
118
sleeptimer_init(void)119 static int sleeptimer_init(void)
120 {
121 sl_status_t status = SL_STATUS_OK;
122 struct sleeptimer_timer_data *timer = &g_sleeptimer_timer_data;
123
124 IRQ_CONNECT(DT_IRQ(DT_RTC, irq), DT_IRQ(DT_RTC, priority),
125 CONCAT(DT_STRING_UPPER_TOKEN_BY_IDX(DT_RTC, interrupt_names, 0), _IRQHandler),
126 0, 0);
127
128 sl_sleeptimer_init();
129
130 z_clock_hw_cycles_per_sec = sl_sleeptimer_get_timer_frequency();
131
132 BUILD_ASSERT(CONFIG_SYS_CLOCK_TICKS_PER_SEC > 0,
133 "Invalid CONFIG_SYS_CLOCK_TICKS_PER_SEC value");
134
135 timer->cyc_per_tick = z_clock_hw_cycles_per_sec / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
136
137 timer->max_timeout_ticks = MAX_TIMEOUT_CYC / timer->cyc_per_tick;
138 timer->initialized = true;
139
140 atomic_set(&timer->last_count,
141 ROUND_DOWN(sl_sleeptimer_get_tick_count(), timer->cyc_per_tick));
142
143 /* Start the timer and announce 1 kernel tick */
144 if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
145 status = sl_sleeptimer_start_timer(&timer->handle, timer->cyc_per_tick,
146 sleeptimer_cb, timer, 0, 0);
147 } else {
148 status = sl_sleeptimer_start_periodic_timer(&timer->handle, timer->cyc_per_tick,
149 sleeptimer_cb, timer, 0, 0);
150 }
151 if (status != SL_STATUS_OK) {
152 return -ENODEV;
153 }
154
155 return 0;
156 }
157
158 SYS_INIT(sleeptimer_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
159