1 /*
2 * Copyright (c) 2021, Sateesh Kotapati
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_gecko_stimer
8
9 #include <errno.h>
10 #include <stddef.h>
11 #include <string.h>
12
13 #include <zephyr/device.h>
14 #include <zephyr/drivers/counter.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/logging/log.h>
17
18 #include <em_cmu.h>
19 #include <sl_atomic.h>
20 #include <sl_sleeptimer.h>
21 #include <sli_sleeptimer_hal.h>
22
23 LOG_MODULE_REGISTER(counter_gecko, CONFIG_COUNTER_LOG_LEVEL);
24
25 #if SL_SLEEPTIMER_PERIPHERAL == SL_SLEEPTIMER_PERIPHERAL_RTCC
26 #define STIMER_IRQ_HANDLER RTCC_IRQHandler
27 #define STIMER_MAX_VALUE _RTCC_CNT_MASK
28 #elif SL_SLEEPTIMER_PERIPHERAL == SL_SLEEPTIMER_PERIPHERAL_SYSRTC
29 #define STIMER_IRQ_HANDLER SYSRTC_APP_IRQHandler
30 #define STIMER_MAX_VALUE _SYSRTC_CNT_MASK
31 #else
32 #error "Unsupported sleep timer peripheral"
33 #endif
34
35 #define STIMER_ALARM_NUM 2
36
37 struct counter_gecko_config {
38 struct counter_config_info info;
39 void (*irq_config)(void);
40 uint32_t prescaler;
41 };
42
43 struct counter_gecko_alarm_data {
44 counter_alarm_callback_t callback;
45 uint8_t chan_id;
46 uint32_t ticks;
47 struct device *dev;
48 void *user_data;
49 };
50
51 struct counter_gecko_top_data {
52 counter_top_callback_t callback;
53 uint32_t ticks;
54 struct device *dev;
55 void *user_data;
56 };
57
58 struct counter_gecko_data {
59 struct counter_gecko_alarm_data alarm[STIMER_ALARM_NUM];
60 struct counter_gecko_top_data top_data;
61 };
62
63 static sl_sleeptimer_timer_handle_t alarm_timer[STIMER_ALARM_NUM];
64 static sl_sleeptimer_timer_handle_t top_timer;
65
66 #ifdef CONFIG_SOC_GECKO_HAS_ERRATA_RTCC_E201
67 #define ERRATA_RTCC_E201_MESSAGE \
68 "Errata RTCC_E201: In case RTCC prescaler != 1 the module does not " \
69 "reset the counter value on CCV1 compare."
70 #endif
71
alarm_callback(sl_sleeptimer_timer_handle_t * handle,void * data)72 static void alarm_callback(sl_sleeptimer_timer_handle_t *handle, void *data)
73 {
74 struct counter_gecko_alarm_data *alarm_data = (struct counter_gecko_alarm_data *)data;
75 uint32_t count =
76 ((sl_sleeptimer_get_tick_count()) %
77 (((struct counter_gecko_data *const)(alarm_data->dev)->data)->top_data.ticks));
78
79 if (alarm_data->callback != NULL) {
80 alarm_data->callback(
81 alarm_data->dev, alarm_data->chan_id, count,
82 ((struct counter_alarm_cfg *)(alarm_data->user_data))->user_data);
83 }
84 }
85
top_callback(sl_sleeptimer_timer_handle_t * handle,void * data)86 static void top_callback(sl_sleeptimer_timer_handle_t *handle, void *data)
87 {
88 struct counter_gecko_top_data *top_data = (struct counter_gecko_top_data *)data;
89
90 if (top_data->callback != NULL) {
91 top_data->callback(top_data->dev,
92 ((struct counter_top_cfg *)(top_data->user_data))->user_data);
93 }
94 }
95
counter_gecko_get_value(const struct device * dev,uint32_t * ticks)96 static int counter_gecko_get_value(const struct device *dev, uint32_t *ticks)
97 {
98 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
99
100 *ticks = ((sl_sleeptimer_get_tick_count()) % (dev_data->top_data.ticks));
101
102 return 0;
103 }
104
counter_gecko_start(const struct device * dev)105 static int counter_gecko_start(const struct device *dev)
106 {
107 ARG_UNUSED(dev);
108
109 sl_status_t error_code;
110 bool is_top_timer_running = false;
111
112 error_code = sl_sleeptimer_is_timer_running(&top_timer, &is_top_timer_running);
113 if ((error_code == SL_STATUS_OK) && (is_top_timer_running == true)) {
114 return 0;
115 }
116 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
117
118 error_code = sl_sleeptimer_start_timer(&top_timer, dev_data->top_data.ticks, top_callback,
119 (void *)&dev_data->top_data, 0, 0);
120 return error_code;
121 }
122
counter_gecko_stop(const struct device * dev)123 static int counter_gecko_stop(const struct device *dev)
124 {
125 ARG_UNUSED(dev);
126
127 sl_status_t error_code;
128 bool is_top_timer_running = false;
129
130 error_code = sl_sleeptimer_is_timer_running(&top_timer, &is_top_timer_running);
131 if ((error_code == SL_STATUS_OK) && (is_top_timer_running == true)) {
132 sl_sleeptimer_stop_timer(&top_timer);
133 }
134 return error_code;
135 }
136
counter_gecko_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)137 static int counter_gecko_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg)
138 {
139 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
140 sl_status_t error_code;
141 bool is_top_timer_running = false;
142
143 #ifdef CONFIG_SOC_GECKO_HAS_ERRATA_RTCC_E201
144 const struct counter_gecko_config *const dev_cfg =
145 (const struct counter_gecko_config *const)(dev)->config;
146
147 if (dev_cfg->prescaler != 1) {
148 LOG_ERR(ERRATA_RTCC_E201_MESSAGE);
149 return -EINVAL;
150 }
151 #endif
152
153 error_code = sl_sleeptimer_is_timer_running(&top_timer, &is_top_timer_running);
154 if ((error_code == SL_STATUS_OK) && (is_top_timer_running == true)) {
155 sl_sleeptimer_stop_timer(&top_timer);
156 }
157
158 dev_data->top_data.callback = cfg->callback;
159 dev_data->top_data.ticks = cfg->ticks;
160 dev_data->top_data.dev = (struct device *)dev;
161 dev_data->top_data.user_data = (struct counter_top_cfg *)cfg;
162
163 error_code = sl_sleeptimer_start_periodic_timer(&top_timer, cfg->ticks, top_callback,
164 (void *)&dev_data->top_data, 0, cfg->flags);
165
166 return error_code;
167 }
168
counter_gecko_get_top_value(const struct device * dev)169 static uint32_t counter_gecko_get_top_value(const struct device *dev)
170 {
171 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
172
173 return dev_data->top_data.ticks;
174 }
175
counter_gecko_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)176 static int counter_gecko_set_alarm(const struct device *dev, uint8_t chan_id,
177 const struct counter_alarm_cfg *alarm_cfg)
178 {
179 bool is_alarm_timer_running = false;
180 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
181 sl_status_t error_code;
182 uint32_t now_ticks = 0;
183 uint32_t top_val = counter_gecko_get_top_value(dev);
184
185 if ((top_val != 0) && (alarm_cfg->ticks > top_val)) {
186 return -EINVAL;
187 }
188
189 if (chan_id >= STIMER_ALARM_NUM) {
190 printk("Alarm timer count exceeded\n");
191 return -EINVAL;
192 }
193
194 error_code = sl_sleeptimer_is_timer_running(&alarm_timer[chan_id], &is_alarm_timer_running);
195 if ((error_code == SL_STATUS_OK) && (is_alarm_timer_running == true)) {
196 sl_sleeptimer_stop_timer(&alarm_timer[chan_id]);
197 }
198
199 if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) != 0) {
200 /* Absolute */
201 error_code = counter_gecko_get_value(dev, &now_ticks);
202 if (now_ticks < alarm_cfg->ticks) {
203 dev_data->alarm[chan_id].ticks = top_val + (alarm_cfg->ticks - now_ticks);
204 } else {
205 dev_data->alarm[chan_id].ticks =
206 (top_val - (now_ticks - alarm_cfg->ticks)) % top_val;
207 }
208
209 } else {
210 /* Relative */
211 dev_data->alarm[chan_id].ticks = alarm_cfg->ticks;
212 }
213
214 dev_data->alarm[chan_id].callback = alarm_cfg->callback;
215 dev_data->alarm[chan_id].chan_id = chan_id;
216 dev_data->alarm[chan_id].dev = (struct device *)dev;
217 dev_data->alarm[chan_id].user_data = (struct counter_alarm_cfg *)alarm_cfg;
218
219 error_code =
220 sl_sleeptimer_start_timer(&alarm_timer[chan_id], dev_data->alarm[chan_id].ticks,
221 alarm_callback, (void *)&dev_data->alarm[chan_id], 0, 0);
222
223 return 0;
224 }
225
counter_gecko_cancel_alarm(const struct device * dev,uint8_t chan_id)226 static int counter_gecko_cancel_alarm(const struct device *dev, uint8_t chan_id)
227 {
228 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
229
230 if (chan_id >= STIMER_ALARM_NUM) {
231 LOG_DBG("Alarm timer count exceeded\n");
232 return -EINVAL;
233 }
234
235 sl_sleeptimer_stop_timer(&alarm_timer[chan_id]);
236
237 dev_data->alarm[chan_id].callback = NULL;
238 dev_data->alarm[chan_id].user_data = NULL;
239
240 LOG_DBG("cancel alarm: channel %u", chan_id);
241
242 return 0;
243 }
244
counter_gecko_get_pending_int(const struct device * dev)245 static uint32_t counter_gecko_get_pending_int(const struct device *dev)
246 {
247 ARG_UNUSED(dev);
248
249 return 0;
250 }
251
counter_gecko_init(const struct device * dev)252 static int counter_gecko_init(const struct device *dev)
253 {
254 const struct counter_gecko_config *const dev_cfg =
255 (const struct counter_gecko_config *const)(dev)->config;
256 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
257
258 sl_sleeptimer_init();
259 dev_data->top_data.ticks = STIMER_MAX_VALUE;
260
261 /* Configure & enable module interrupts */
262 dev_cfg->irq_config();
263
264 LOG_INF("Device %s initialized", (dev)->name);
265
266 return 0;
267 }
268
269 static DEVICE_API(counter, counter_gecko_driver_api) = {
270 .start = counter_gecko_start,
271 .stop = counter_gecko_stop,
272 .get_value = counter_gecko_get_value,
273 .set_alarm = counter_gecko_set_alarm,
274 .cancel_alarm = counter_gecko_cancel_alarm,
275 .set_top_value = counter_gecko_set_top_value,
276 .get_pending_int = counter_gecko_get_pending_int,
277 .get_top_value = counter_gecko_get_top_value,
278 };
279
280 BUILD_ASSERT((DT_INST_PROP(0, prescaler) > 0U) && (DT_INST_PROP(0, prescaler) <= 32768U));
281
counter_gecko_0_irq_config(void)282 static void counter_gecko_0_irq_config(void)
283 {
284 IRQ_DIRECT_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), STIMER_IRQ_HANDLER, 0);
285 irq_enable(DT_INST_IRQN(0));
286 }
287
288 static const struct counter_gecko_config counter_gecko_0_config = {
289 .info = {
290 .max_top_value = STIMER_MAX_VALUE,
291 .freq = DT_INST_PROP(0, clock_frequency) / DT_INST_PROP(0, prescaler),
292 .flags = COUNTER_CONFIG_INFO_COUNT_UP,
293 .channels = STIMER_ALARM_NUM,
294 },
295 .irq_config = counter_gecko_0_irq_config,
296 .prescaler = DT_INST_PROP(0, prescaler),
297 };
298
299 static struct counter_gecko_data counter_gecko_0_data;
300
301 DEVICE_DT_INST_DEFINE(0, counter_gecko_init, NULL, &counter_gecko_0_data, &counter_gecko_0_config,
302 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &counter_gecko_driver_api);
303