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 #define MIN_DELAY_CYC (4U)
25
26 #define DT_RTC DT_COMPAT_GET_ANY_STATUS_OKAY(silabs_gecko_stimer)
27
28 /* Ensure interrupt names don't expand to register interface struct pointers */
29 #undef RTCC
30
31 /* With CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME, this global variable holds the clock frequency,
32 * and must be written by the driver at init.
33 */
34 extern int z_clock_hw_cycles_per_sec;
35
36 /* Global timer state */
37 struct sleeptimer_timer_data {
38 uint32_t cyc_per_tick; /* Number of hw_cycles per 1 kernel tick */
39 uint32_t max_timeout_ticks; /* MAX_TIMEOUT_CYC expressed as ticks */
40 atomic_t last_count; /* Value of counter when the previous tick was announced */
41 struct k_spinlock lock; /* Spinlock to sync between ISR and updating the timeout */
42 bool initialized; /* Set to true when timer is initialized */
43 sl_sleeptimer_timer_handle_t handle; /* Timer handle for system timer */
44 };
45 static struct sleeptimer_timer_data g_sleeptimer_timer_data = {0};
46
sleeptimer_cb(sl_sleeptimer_timer_handle_t * handle,void * data)47 static void sleeptimer_cb(sl_sleeptimer_timer_handle_t *handle, void *data)
48 {
49 ARG_UNUSED(handle);
50 struct sleeptimer_timer_data *timer = data;
51
52 uint32_t curr = sl_sleeptimer_get_tick_count();
53 uint32_t prev = atomic_get(&timer->last_count);
54 uint32_t pending = curr - prev;
55
56 /* Number of unannounced ticks since the last announcement */
57 uint32_t unannounced = pending / timer->cyc_per_tick;
58
59 atomic_set(&timer->last_count, prev + unannounced * timer->cyc_per_tick);
60
61 sys_clock_announce(unannounced);
62 }
63
sleeptimer_clock_set_timeout(int32_t ticks,struct sleeptimer_timer_data * timer)64 static void sleeptimer_clock_set_timeout(int32_t ticks, struct sleeptimer_timer_data *timer)
65 {
66 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
67 return;
68 }
69
70 ticks = (ticks == K_TICKS_FOREVER) ? timer->max_timeout_ticks : ticks;
71 ticks = CLAMP(ticks, 0, timer->max_timeout_ticks);
72
73 k_spinlock_key_t key = k_spin_lock(&timer->lock);
74
75 uint32_t curr = sl_sleeptimer_get_tick_count();
76 uint32_t prev = atomic_get(&timer->last_count);
77 uint32_t pending = curr - prev;
78 uint32_t next = ticks * timer->cyc_per_tick;
79
80 /* Next timeout is N ticks in the future, minus the current progress
81 * towards the timeout. If we are behind, set the timeout to the first
82 * possible upcoming tick.
83 */
84 while (next < (pending + MIN_DELAY_CYC)) {
85 next += timer->cyc_per_tick;
86 }
87 next -= pending;
88
89 sl_sleeptimer_restart_timer(&timer->handle, next, sleeptimer_cb, timer, 0, 0);
90 k_spin_unlock(&timer->lock, key);
91 }
92
sleeptimer_clock_elapsed(struct sleeptimer_timer_data * timer)93 static uint32_t sleeptimer_clock_elapsed(struct sleeptimer_timer_data *timer)
94 {
95 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL) || !timer->initialized) {
96 /* No unannounced ticks can have elapsed if not in tickless mode */
97 return 0;
98 } else {
99 return (sl_sleeptimer_get_tick_count() - atomic_get(&timer->last_count)) /
100 timer->cyc_per_tick;
101 }
102 }
103
sys_clock_set_timeout(int32_t ticks,bool idle)104 void sys_clock_set_timeout(int32_t ticks, bool idle)
105 {
106 ARG_UNUSED(idle);
107
108 sleeptimer_clock_set_timeout(ticks, &g_sleeptimer_timer_data);
109 }
110
sys_clock_elapsed(void)111 uint32_t sys_clock_elapsed(void)
112 {
113 return sleeptimer_clock_elapsed(&g_sleeptimer_timer_data);
114 }
115
sys_clock_cycle_get_32(void)116 uint32_t sys_clock_cycle_get_32(void)
117 {
118 return g_sleeptimer_timer_data.initialized ? sl_sleeptimer_get_tick_count() : 0;
119 }
120
sleeptimer_init(void)121 static int sleeptimer_init(void)
122 {
123 sl_status_t status = SL_STATUS_OK;
124 struct sleeptimer_timer_data *timer = &g_sleeptimer_timer_data;
125
126 IRQ_CONNECT(DT_IRQ(DT_RTC, irq), DT_IRQ(DT_RTC, priority),
127 CONCAT(DT_STRING_UPPER_TOKEN_BY_IDX(DT_RTC, interrupt_names, 0), _IRQHandler),
128 0, 0);
129
130 sl_sleeptimer_init();
131
132 z_clock_hw_cycles_per_sec = sl_sleeptimer_get_timer_frequency();
133
134 BUILD_ASSERT(CONFIG_SYS_CLOCK_TICKS_PER_SEC > 0,
135 "Invalid CONFIG_SYS_CLOCK_TICKS_PER_SEC value");
136
137 timer->cyc_per_tick = z_clock_hw_cycles_per_sec / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
138
139 __ASSERT(timer->cyc_per_tick >= MIN_DELAY_CYC,
140 "A tick of %u cycles is too short to be scheduled "
141 "(min is %u). Config: SYS_CLOCK_TICKS_PER_SEC is "
142 "%d and timer frequency is %u",
143 timer->cyc_per_tick, MIN_DELAY_CYC, CONFIG_SYS_CLOCK_TICKS_PER_SEC,
144 z_clock_hw_cycles_per_sec);
145
146 timer->max_timeout_ticks = MAX_TIMEOUT_CYC / timer->cyc_per_tick;
147 timer->initialized = true;
148
149 atomic_set(&timer->last_count, sl_sleeptimer_get_tick_count());
150
151 /* Start the timer and announce 1 kernel tick */
152 if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
153 status = sl_sleeptimer_start_timer(&timer->handle, timer->cyc_per_tick,
154 sleeptimer_cb, timer, 0, 0);
155 } else {
156 status = sl_sleeptimer_start_periodic_timer(&timer->handle, timer->cyc_per_tick,
157 sleeptimer_cb, timer, 0, 0);
158 }
159 if (status != SL_STATUS_OK) {
160 return -ENODEV;
161 }
162
163 return 0;
164 }
165
166 SYS_INIT(sleeptimer_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
167