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