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