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 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
116
117 error_code = sl_sleeptimer_start_timer(&top_timer, dev_data->top_data.ticks, top_callback,
118 (void *)&dev_data->top_data, 0, 0);
119 return error_code;
120 }
121
counter_gecko_stop(const struct device * dev)122 static int counter_gecko_stop(const struct device *dev)
123 {
124 ARG_UNUSED(dev);
125
126 sl_status_t error_code;
127 bool is_top_timer_running = false;
128
129 error_code = sl_sleeptimer_is_timer_running(&top_timer, &is_top_timer_running);
130 if ((error_code == SL_STATUS_OK) && (is_top_timer_running == true)) {
131 sl_sleeptimer_stop_timer(&top_timer);
132 }
133 return error_code;
134 }
135
counter_gecko_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)136 static int counter_gecko_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg)
137 {
138 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
139 sl_status_t error_code;
140 bool is_top_timer_running = false;
141
142 #ifdef CONFIG_SOC_GECKO_HAS_ERRATA_RTCC_E201
143 const struct counter_gecko_config *const dev_cfg =
144 (const struct counter_gecko_config *const)(dev)->config;
145
146 if (dev_cfg->prescaler != 1) {
147 LOG_ERR(ERRATA_RTCC_E201_MESSAGE);
148 return -EINVAL;
149 }
150 #endif
151
152 error_code = sl_sleeptimer_is_timer_running(&top_timer, &is_top_timer_running);
153 if ((error_code == SL_STATUS_OK) && (is_top_timer_running == true)) {
154 sl_sleeptimer_stop_timer(&top_timer);
155 }
156
157 dev_data->top_data.callback = cfg->callback;
158 dev_data->top_data.ticks = cfg->ticks;
159 dev_data->top_data.dev = (struct device *)dev;
160 dev_data->top_data.user_data = (struct counter_top_cfg *)cfg;
161
162 error_code = sl_sleeptimer_start_periodic_timer(&top_timer, cfg->ticks, top_callback,
163 (void *)&dev_data->top_data, 0, cfg->flags);
164
165 return error_code;
166 }
167
counter_gecko_get_top_value(const struct device * dev)168 static uint32_t counter_gecko_get_top_value(const struct device *dev)
169 {
170 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
171
172 return dev_data->top_data.ticks;
173 }
174
counter_gecko_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)175 static int counter_gecko_set_alarm(const struct device *dev, uint8_t chan_id,
176 const struct counter_alarm_cfg *alarm_cfg)
177 {
178 bool is_alarm_timer_running = false;
179 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
180 sl_status_t error_code;
181 uint32_t now_ticks = 0;
182 uint32_t top_val = counter_gecko_get_top_value(dev);
183
184 if ((top_val != 0) && (alarm_cfg->ticks > top_val)) {
185 return -EINVAL;
186 }
187
188 if (chan_id >= STIMER_ALARM_NUM) {
189 printk("Alarm timer count exceeded\n");
190 return -EINVAL;
191 }
192
193 error_code = sl_sleeptimer_is_timer_running(&alarm_timer[chan_id], &is_alarm_timer_running);
194 if ((error_code == SL_STATUS_OK) && (is_alarm_timer_running == true)) {
195 sl_sleeptimer_stop_timer(&alarm_timer[chan_id]);
196 }
197
198 if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) != 0) {
199 /* Absolute */
200 error_code = counter_gecko_get_value(dev, &now_ticks);
201 if (now_ticks < alarm_cfg->ticks) {
202 dev_data->alarm[chan_id].ticks = top_val + (alarm_cfg->ticks - now_ticks);
203 } else {
204 dev_data->alarm[chan_id].ticks =
205 (top_val - (now_ticks - alarm_cfg->ticks)) % top_val;
206 }
207
208 } else {
209 /* Relative */
210 dev_data->alarm[chan_id].ticks = alarm_cfg->ticks;
211 }
212
213 dev_data->alarm[chan_id].callback = alarm_cfg->callback;
214 dev_data->alarm[chan_id].chan_id = chan_id;
215 dev_data->alarm[chan_id].dev = (struct device *)dev;
216 dev_data->alarm[chan_id].user_data = (struct counter_alarm_cfg *)alarm_cfg;
217
218 error_code =
219 sl_sleeptimer_start_timer(&alarm_timer[chan_id], dev_data->alarm[chan_id].ticks,
220 alarm_callback, (void *)&dev_data->alarm[chan_id], 0, 0);
221
222 return 0;
223 }
224
counter_gecko_cancel_alarm(const struct device * dev,uint8_t chan_id)225 static int counter_gecko_cancel_alarm(const struct device *dev, uint8_t chan_id)
226 {
227 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
228
229 if (chan_id >= STIMER_ALARM_NUM) {
230 LOG_DBG("Alarm timer count exceeded\n");
231 return -EINVAL;
232 }
233
234 sl_sleeptimer_stop_timer(&alarm_timer[chan_id]);
235
236 dev_data->alarm[chan_id].callback = NULL;
237 dev_data->alarm[chan_id].user_data = NULL;
238
239 LOG_DBG("cancel alarm: channel %u", chan_id);
240
241 return 0;
242 }
243
counter_gecko_get_pending_int(const struct device * dev)244 static uint32_t counter_gecko_get_pending_int(const struct device *dev)
245 {
246 ARG_UNUSED(dev);
247
248 return 0;
249 }
250
counter_gecko_init(const struct device * dev)251 static int counter_gecko_init(const struct device *dev)
252 {
253 const struct counter_gecko_config *const dev_cfg =
254 (const struct counter_gecko_config *const)(dev)->config;
255 struct counter_gecko_data *const dev_data = (struct counter_gecko_data *const)(dev)->data;
256
257 sl_sleeptimer_init();
258 dev_data->top_data.ticks = STIMER_MAX_VALUE;
259
260 /* Configure & enable module interrupts */
261 dev_cfg->irq_config();
262
263 LOG_INF("Device %s initialized", (dev)->name);
264
265 return 0;
266 }
267
268 static const struct counter_driver_api counter_gecko_driver_api = {
269 .start = counter_gecko_start,
270 .stop = counter_gecko_stop,
271 .get_value = counter_gecko_get_value,
272 .set_alarm = counter_gecko_set_alarm,
273 .cancel_alarm = counter_gecko_cancel_alarm,
274 .set_top_value = counter_gecko_set_top_value,
275 .get_pending_int = counter_gecko_get_pending_int,
276 .get_top_value = counter_gecko_get_top_value,
277 };
278
279 BUILD_ASSERT((DT_INST_PROP(0, prescaler) > 0U) && (DT_INST_PROP(0, prescaler) <= 32768U));
280
counter_gecko_0_irq_config(void)281 static void counter_gecko_0_irq_config(void)
282 {
283 IRQ_DIRECT_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), STIMER_IRQ_HANDLER, 0);
284 irq_enable(DT_INST_IRQN(0));
285 }
286
287 static const struct counter_gecko_config counter_gecko_0_config = {
288 .info = {
289 .max_top_value = STIMER_MAX_VALUE,
290 .freq = DT_INST_PROP(0, clock_frequency) / DT_INST_PROP(0, prescaler),
291 .flags = COUNTER_CONFIG_INFO_COUNT_UP,
292 .channels = STIMER_ALARM_NUM,
293 },
294 .irq_config = counter_gecko_0_irq_config,
295 .prescaler = DT_INST_PROP(0, prescaler),
296 };
297
298 static struct counter_gecko_data counter_gecko_0_data;
299
300 DEVICE_DT_INST_DEFINE(0, counter_gecko_init, NULL, &counter_gecko_0_data, &counter_gecko_0_config,
301 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &counter_gecko_driver_api);
302