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