1 /*
2 * Copyright (c) 2019, Piotr Mienkowski
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_gecko_rtcc
8
9 #include <stddef.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/device.h>
14 #include <soc.h>
15 #include <em_cmu.h>
16 #include <em_rtcc.h>
17 #include <zephyr/drivers/counter.h>
18
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(counter_gecko, CONFIG_COUNTER_LOG_LEVEL);
21
22 #define RTCC_MAX_VALUE (_RTCC_CNT_MASK)
23 #define RTCC_ALARM_NUM 2
24
25 struct counter_gecko_config {
26 struct counter_config_info info;
27 void (*irq_config)(void);
28 uint32_t prescaler;
29 };
30
31 struct counter_gecko_alarm_data {
32 counter_alarm_callback_t callback;
33 void *user_data;
34 };
35
36 struct counter_gecko_data {
37 struct counter_gecko_alarm_data alarm[RTCC_ALARM_NUM];
38 counter_top_callback_t top_callback;
39 void *top_user_data;
40 };
41
42 #ifdef CONFIG_SOC_GECKO_HAS_ERRATA_RTCC_E201
43 #define ERRATA_RTCC_E201_MESSAGE \
44 "Errata RTCC_E201: In case RTCC prescaler != 1 the module does not " \
45 "reset the counter value on CCV1 compare."
46 #endif
47
48 /* Map channel id to CC channel provided by the RTCC module */
chan_id2cc_idx(uint8_t chan_id)49 static uint8_t chan_id2cc_idx(uint8_t chan_id)
50 {
51 uint8_t cc_idx;
52
53 switch (chan_id) {
54 case 0:
55 cc_idx = 2;
56 break;
57 default:
58 cc_idx = 0;
59 break;
60 }
61 return cc_idx;
62 }
63
counter_gecko_start(const struct device * dev)64 static int counter_gecko_start(const struct device *dev)
65 {
66 ARG_UNUSED(dev);
67
68 RTCC_Enable(true);
69
70 return 0;
71 }
72
counter_gecko_stop(const struct device * dev)73 static int counter_gecko_stop(const struct device *dev)
74 {
75 ARG_UNUSED(dev);
76
77 RTCC_Enable(false);
78
79 return 0;
80 }
81
counter_gecko_get_value(const struct device * dev,uint32_t * ticks)82 static int counter_gecko_get_value(const struct device *dev, uint32_t *ticks)
83 {
84 ARG_UNUSED(dev);
85
86 *ticks = RTCC_CounterGet();
87 return 0;
88 }
89
counter_gecko_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)90 static int counter_gecko_set_top_value(const struct device *dev,
91 const struct counter_top_cfg *cfg)
92 {
93 struct counter_gecko_data *const dev_data = dev->data;
94 uint32_t ticks;
95 uint32_t flags;
96 int err = 0;
97
98 #ifdef CONFIG_SOC_GECKO_HAS_ERRATA_RTCC_E201
99 const struct counter_gecko_config *const dev_cfg = dev->config;
100
101 if (dev_cfg->prescaler != 1) {
102 LOG_ERR(ERRATA_RTCC_E201_MESSAGE);
103 return -EINVAL;
104 }
105 #endif
106
107 /* Counter top value can only be changed when all alarms are disabled */
108 for (int i = 0; i < RTCC_ALARM_NUM; i++) {
109 if (dev_data->alarm[i].callback) {
110 return -EBUSY;
111 }
112 }
113
114 RTCC_IntClear(RTCC_IF_CC1);
115
116 dev_data->top_callback = cfg->callback;
117 dev_data->top_user_data = cfg->user_data;
118 ticks = cfg->ticks;
119 flags = cfg->flags;
120
121 if (!(flags & COUNTER_TOP_CFG_DONT_RESET)) {
122 RTCC_CounterSet(0);
123 }
124
125 RTCC_ChannelCCVSet(1, ticks);
126
127 LOG_DBG("set top value: %u", ticks);
128
129 if ((flags & COUNTER_TOP_CFG_DONT_RESET) &&
130 RTCC_CounterGet() > ticks) {
131 err = -ETIME;
132 if (flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) {
133 RTCC_CounterSet(0);
134 }
135 }
136
137 /* Enable the compare interrupt */
138 RTCC_IntEnable(RTCC_IF_CC1);
139
140 return err;
141 }
142
counter_gecko_get_top_value(const struct device * dev)143 static uint32_t counter_gecko_get_top_value(const struct device *dev)
144 {
145 ARG_UNUSED(dev);
146
147 return RTCC_ChannelCCVGet(1);
148 }
149
counter_gecko_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)150 static int counter_gecko_set_alarm(const struct device *dev, uint8_t chan_id,
151 const struct counter_alarm_cfg *alarm_cfg)
152 {
153 uint32_t count = RTCC_CounterGet();
154 struct counter_gecko_data *const dev_data = dev->data;
155 uint32_t top_value = counter_gecko_get_top_value(dev);
156 uint32_t ccv;
157
158 if ((top_value != 0) && (alarm_cfg->ticks > top_value)) {
159 return -EINVAL;
160 }
161 if (dev_data->alarm[chan_id].callback != NULL) {
162 return -EBUSY;
163 }
164
165 if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) != 0) {
166 ccv = alarm_cfg->ticks;
167 } else {
168 if (top_value == 0) {
169 ccv = count + alarm_cfg->ticks;
170 } else {
171 uint64_t ccv64 = count + alarm_cfg->ticks;
172
173 ccv = (uint32_t)(ccv64 % top_value);
174 }
175 }
176
177 uint8_t cc_idx = chan_id2cc_idx(chan_id);
178
179 RTCC_IntClear(RTCC_IF_CC0 << cc_idx);
180
181 dev_data->alarm[chan_id].callback = alarm_cfg->callback;
182 dev_data->alarm[chan_id].user_data = alarm_cfg->user_data;
183
184 RTCC_ChannelCCVSet(cc_idx, ccv);
185
186 LOG_DBG("set alarm: channel %u, count %u", chan_id, ccv);
187
188 /* Enable the compare interrupt */
189 RTCC_IntEnable(RTCC_IF_CC0 << cc_idx);
190
191 return 0;
192 }
193
counter_gecko_cancel_alarm(const struct device * dev,uint8_t chan_id)194 static int counter_gecko_cancel_alarm(const struct device *dev,
195 uint8_t chan_id)
196 {
197 struct counter_gecko_data *const dev_data = dev->data;
198
199 uint8_t cc_idx = chan_id2cc_idx(chan_id);
200
201 /* Disable the compare interrupt */
202 RTCC_IntDisable(RTCC_IF_CC0 << cc_idx);
203 RTCC_IntClear(RTCC_IF_CC0 << cc_idx);
204
205 dev_data->alarm[chan_id].callback = NULL;
206 dev_data->alarm[chan_id].user_data = NULL;
207
208 RTCC_ChannelCCVSet(cc_idx, 0);
209
210 LOG_DBG("cancel alarm: channel %u", chan_id);
211
212 return 0;
213 }
214
counter_gecko_get_pending_int(const struct device * dev)215 static uint32_t counter_gecko_get_pending_int(const struct device *dev)
216 {
217 ARG_UNUSED(dev);
218
219 return 0;
220 }
221
counter_gecko_init(const struct device * dev)222 static int counter_gecko_init(const struct device *dev)
223 {
224 const struct counter_gecko_config *const dev_cfg = dev->config;
225
226 RTCC_Init_TypeDef rtcc_config = {
227 false, /* Don't start counting */
228 false, /* Disable RTC during debug halt. */
229 false, /* Don't wrap prescaler on CCV0 */
230 true, /* Counter wrap on CCV1 */
231 #if defined(_SILICON_LABS_32B_SERIES_2)
232 (RTCC_CntPresc_TypeDef)(31UL - __CLZ(dev_cfg->prescaler)),
233 #else
234 (RTCC_CntPresc_TypeDef)CMU_DivToLog2(dev_cfg->prescaler),
235 #endif
236 rtccCntTickPresc, /* Count according to prescaler value */
237 #if defined(_RTCC_CTRL_BUMODETSEN_MASK)
238 false, /* Don't store RTCC counter value in
239 * RTCC_CCV2 upon backup mode entry.
240 */
241 #endif
242 #if defined(_RTCC_CTRL_OSCFDETEN_MASK)
243 false, /* Don't enable LFXO fail detection */
244 #endif
245 #if defined (_RTCC_CTRL_CNTMODE_MASK)
246 rtccCntModeNormal, /* Use RTCC in normal mode */
247 #endif
248 #if defined (_RTCC_CTRL_LYEARCORRDIS_MASK)
249 false /* No leap year correction. */
250 #endif
251 };
252
253 RTCC_CCChConf_TypeDef rtcc_channel_config = {
254 rtccCapComChModeCompare, /* Use compare mode */
255 rtccCompMatchOutActionPulse,/* Don't care */
256 rtccPRSCh0, /* PRS is not used */
257 rtccInEdgeNone, /* Capture input is not used */
258 rtccCompBaseCnt, /* Compare with base CNT register */
259 #if defined (_RTCC_CC_CTRL_COMPMASK_MASK)
260 0, /* Compare mask */
261 #endif
262 #if defined (_RTCC_CC_CTRL_DAYCC_MASK)
263 rtccDayCompareModeMonth, /* Don't care */
264 #endif
265 };
266
267 #if defined(cmuClock_CORELE)
268 /* Ensure LE modules are clocked. */
269 CMU_ClockEnable(cmuClock_CORELE, true);
270 #endif
271
272 #if defined(CMU_LFECLKEN0_RTCC)
273 /* Enable LFECLK in CMU (will also enable oscillator if not enabled). */
274 CMU_ClockSelectSet(cmuClock_LFE, cmuSelect_LFXO);
275 #elif defined(_SILICON_LABS_32B_SERIES_2)
276 CMU_ClockSelectSet(cmuClock_RTCC, cmuSelect_LFXO);
277 #else
278 /* Enable LFACLK in CMU (will also enable oscillator if not enabled). */
279 CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
280 #endif
281
282 /* Enable RTCC module clock */
283 CMU_ClockEnable(cmuClock_RTCC, true);
284
285 /* Initialize RTCC */
286 RTCC_Init(&rtcc_config);
287
288 /* Set up compare channels */
289 RTCC_ChannelInit(0, &rtcc_channel_config);
290 RTCC_ChannelInit(1, &rtcc_channel_config);
291 RTCC_ChannelInit(2, &rtcc_channel_config);
292
293 /* Disable module's internal interrupt sources */
294 RTCC_IntDisable(_RTCC_IF_MASK);
295 RTCC_IntClear(_RTCC_IF_MASK);
296
297 /* Clear the counter */
298 RTCC->CNT = 0;
299
300 /* Configure & enable module interrupts */
301 dev_cfg->irq_config();
302
303 LOG_INF("Device %s initialized", dev->name);
304
305 return 0;
306 }
307
308 static const struct counter_driver_api counter_gecko_driver_api = {
309 .start = counter_gecko_start,
310 .stop = counter_gecko_stop,
311 .get_value = counter_gecko_get_value,
312 .set_alarm = counter_gecko_set_alarm,
313 .cancel_alarm = counter_gecko_cancel_alarm,
314 .set_top_value = counter_gecko_set_top_value,
315 .get_pending_int = counter_gecko_get_pending_int,
316 .get_top_value = counter_gecko_get_top_value,
317 };
318
319 /* RTCC0 */
320
ISR_DIRECT_DECLARE(counter_gecko_isr_0)321 ISR_DIRECT_DECLARE(counter_gecko_isr_0)
322 {
323 const struct device *const dev = DEVICE_DT_INST_GET(0);
324 struct counter_gecko_data *const dev_data = dev->data;
325 counter_alarm_callback_t alarm_callback;
326 uint32_t count = RTCC_CounterGet();
327 uint32_t flags = RTCC_IntGetEnabled();
328
329 RTCC_IntClear(flags);
330
331 if (flags & RTCC_IF_CC1) {
332 if (dev_data->top_callback) {
333 dev_data->top_callback(dev, dev_data->top_user_data);
334 }
335 }
336 for (int i = 0; i < RTCC_ALARM_NUM; i++) {
337 uint8_t cc_idx = chan_id2cc_idx(i);
338
339 if (flags & (RTCC_IF_CC0 << cc_idx)) {
340 if (dev_data->alarm[i].callback) {
341 alarm_callback = dev_data->alarm[i].callback;
342 dev_data->alarm[i].callback = NULL;
343 alarm_callback(dev, i, count,
344 dev_data->alarm[i].user_data);
345 }
346 }
347 }
348
349 ISR_DIRECT_PM();
350
351 return 1;
352 }
353
354 BUILD_ASSERT((DT_INST_PROP(0, prescaler) > 0U) &&
355 (DT_INST_PROP(0, prescaler) <= 32768U));
356
counter_gecko_0_irq_config(void)357 static void counter_gecko_0_irq_config(void)
358 {
359 IRQ_DIRECT_CONNECT(DT_INST_IRQN(0),
360 DT_INST_IRQ(0, priority),
361 counter_gecko_isr_0, 0);
362 irq_enable(DT_INST_IRQN(0));
363 }
364
365 static const struct counter_gecko_config counter_gecko_0_config = {
366 .info = {
367 .max_top_value = RTCC_MAX_VALUE,
368 .freq = DT_INST_PROP(0, clock_frequency) /
369 DT_INST_PROP(0, prescaler),
370 .flags = COUNTER_CONFIG_INFO_COUNT_UP,
371 .channels = RTCC_ALARM_NUM,
372 },
373 .irq_config = counter_gecko_0_irq_config,
374 .prescaler = DT_INST_PROP(0, prescaler),
375 };
376
377 static struct counter_gecko_data counter_gecko_0_data;
378
379 DEVICE_DT_INST_DEFINE(0, counter_gecko_init, NULL,
380 &counter_gecko_0_data, &counter_gecko_0_config,
381 PRE_KERNEL_1, CONFIG_COUNTER_INIT_PRIORITY,
382 &counter_gecko_driver_api);
383