1 /*
2  * Copyright (c) 2019 Derek Hageman <hageman@inthat.cloud>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT atmel_sam0_tc32
8 
9 #include <zephyr/drivers/counter.h>
10 #include <zephyr/drivers/pinctrl.h>
11 #include <zephyr/device.h>
12 #include <zephyr/irq.h>
13 #include <soc.h>
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(counter_sam0_tc32, CONFIG_COUNTER_LOG_LEVEL);
17 
18 struct counter_sam0_tc32_ch_data {
19 	counter_alarm_callback_t callback;
20 	void *user_data;
21 };
22 
23 struct counter_sam0_tc32_data {
24 	counter_top_callback_t top_cb;
25 	void *top_user_data;
26 
27 	struct counter_sam0_tc32_ch_data ch;
28 };
29 
30 struct counter_sam0_tc32_config {
31 	struct counter_config_info info;
32 	TcCount32 *regs;
33 	const struct pinctrl_dev_config *pcfg;
34 #ifdef MCLK
35 	volatile uint32_t *mclk;
36 	uint32_t mclk_mask;
37 	uint16_t gclk_id;
38 #else
39 	uint32_t pm_apbcmask;
40 	uint16_t gclk_clkctrl_id;
41 #endif
42 	uint16_t prescaler;
43 
44 	void (*irq_config_func)(const struct device *dev);
45 };
46 
wait_synchronization(TcCount32 * regs)47 static void wait_synchronization(TcCount32 *regs)
48 {
49 #if defined(TC_SYNCBUSY_MASK)
50 	/* SYNCBUSY is a register */
51 	while ((regs->SYNCBUSY.reg & TC_SYNCBUSY_MASK) != 0) {
52 	}
53 #elif defined(TC_STATUS_SYNCBUSY)
54 	/* SYNCBUSY is a bit */
55 	while ((regs->STATUS.reg & TC_STATUS_SYNCBUSY) != 0) {
56 	}
57 #else
58 #error Unsupported device
59 #endif
60 }
61 
read_synchronize_count(TcCount32 * regs)62 static void read_synchronize_count(TcCount32 *regs)
63 {
64 #if defined(TC_READREQ_RREQ)
65 	regs->READREQ.reg = TC_READREQ_RREQ |
66 			    TC_READREQ_ADDR(TC_COUNT32_COUNT_OFFSET);
67 	wait_synchronization(regs);
68 #elif defined(TC_CTRLBSET_CMD_READSYNC)
69 	regs->CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC;
70 	wait_synchronization(regs);
71 #else
72 	ARG_UNUSED(regs);
73 #endif
74 }
75 
counter_sam0_tc32_start(const struct device * dev)76 static int counter_sam0_tc32_start(const struct device *dev)
77 {
78 	const struct counter_sam0_tc32_config *const cfg = dev->config;
79 	TcCount32 *tc = cfg->regs;
80 
81 	/*
82 	 * This will also reset the current counter value if it's
83 	 * already running.
84 	 */
85 	tc->CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;
86 	wait_synchronization(tc);
87 	return 0;
88 }
89 
counter_sam0_tc32_stop(const struct device * dev)90 static int counter_sam0_tc32_stop(const struct device *dev)
91 {
92 	const struct counter_sam0_tc32_config *const cfg = dev->config;
93 	TcCount32 *tc = cfg->regs;
94 
95 	/*
96 	 * The older (pre SAML1x) manuals claim the counter retains its
97 	 * value on stop, but this doesn't actually seem to happen.
98 	 * The SAML1x manual says it resets, which is what the SAMD21
99 	 * counter actually appears to do.
100 	 */
101 	tc->CTRLBSET.reg = TC_CTRLBSET_CMD_STOP;
102 	wait_synchronization(tc);
103 	return 0;
104 }
105 
counter_sam0_tc32_read(const struct device * dev)106 static uint32_t counter_sam0_tc32_read(const struct device *dev)
107 {
108 	const struct counter_sam0_tc32_config *const cfg = dev->config;
109 	TcCount32 *tc = cfg->regs;
110 
111 	read_synchronize_count(tc);
112 	return tc->COUNT.reg;
113 }
114 
counter_sam0_tc32_get_value(const struct device * dev,uint32_t * ticks)115 static int counter_sam0_tc32_get_value(const struct device *dev,
116 				       uint32_t *ticks)
117 {
118 	*ticks = counter_sam0_tc32_read(dev);
119 	return 0;
120 }
121 
counter_sam0_tc32_relative_alarm(const struct device * dev,uint32_t ticks)122 static void counter_sam0_tc32_relative_alarm(const struct device *dev,
123 					     uint32_t ticks)
124 {
125 	struct counter_sam0_tc32_data *data = dev->data;
126 	const struct counter_sam0_tc32_config *const cfg = dev->config;
127 	TcCount32 *tc = cfg->regs;
128 	uint32_t before;
129 	uint32_t target;
130 	uint32_t after;
131 	uint32_t max;
132 
133 	read_synchronize_count(tc);
134 	before = tc->COUNT.reg;
135 
136 	target = before + ticks;
137 	max = tc->CC[0].reg;
138 	if (target > max) {
139 		target -= max;
140 	}
141 
142 	tc->CC[1].reg = target;
143 	wait_synchronization(tc);
144 	tc->INTFLAG.reg = TC_INTFLAG_MC1;
145 
146 	read_synchronize_count(tc);
147 	after = tc->COUNT.reg;
148 
149 	/* Pending now, so no further checking required */
150 	if (tc->INTFLAG.bit.MC1) {
151 		goto out_future;
152 	}
153 
154 	/*
155 	 * Check if we missed the interrupt and call the handler
156 	 * immediately if we did.
157 	 */
158 	if (after < target) {
159 		goto out_future;
160 	}
161 
162 	/* Check wrapped */
163 	if (target < before && after >= before) {
164 		goto out_future;
165 	}
166 
167 	counter_alarm_callback_t cb = data->ch.callback;
168 
169 	tc->INTENCLR.reg = TC_INTENCLR_MC1;
170 	tc->INTFLAG.reg = TC_INTFLAG_MC1;
171 	data->ch.callback = NULL;
172 
173 	cb(dev, 0, target, data->ch.user_data);
174 
175 	return;
176 
177 out_future:
178 	tc->INTENSET.reg = TC_INTFLAG_MC1;
179 }
180 
counter_sam0_tc32_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)181 static int counter_sam0_tc32_set_alarm(const struct device *dev,
182 				       uint8_t chan_id,
183 				       const struct counter_alarm_cfg *alarm_cfg)
184 {
185 	struct counter_sam0_tc32_data *data = dev->data;
186 	const struct counter_sam0_tc32_config *const cfg = dev->config;
187 	TcCount32 *tc = cfg->regs;
188 
189 	ARG_UNUSED(chan_id);
190 
191 	if (alarm_cfg->ticks > tc->CC[0].reg) {
192 		return -EINVAL;
193 	}
194 
195 	unsigned int key = irq_lock();
196 
197 	if (data->ch.callback) {
198 		irq_unlock(key);
199 		return -EBUSY;
200 	}
201 
202 	data->ch.callback = alarm_cfg->callback;
203 	data->ch.user_data = alarm_cfg->user_data;
204 
205 	if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) != 0) {
206 		tc->CC[1].reg = alarm_cfg->ticks;
207 		wait_synchronization(tc);
208 		tc->INTFLAG.reg = TC_INTFLAG_MC1;
209 		tc->INTENSET.reg = TC_INTFLAG_MC1;
210 	} else {
211 		counter_sam0_tc32_relative_alarm(dev, alarm_cfg->ticks);
212 	}
213 
214 	irq_unlock(key);
215 
216 	return 0;
217 }
218 
counter_sam0_tc32_cancel_alarm(const struct device * dev,uint8_t chan_id)219 static int counter_sam0_tc32_cancel_alarm(const struct device *dev,
220 					  uint8_t chan_id)
221 {
222 	struct counter_sam0_tc32_data *data = dev->data;
223 	const struct counter_sam0_tc32_config *const cfg = dev->config;
224 	TcCount32 *tc = cfg->regs;
225 
226 	unsigned int key = irq_lock();
227 
228 	ARG_UNUSED(chan_id);
229 
230 	data->ch.callback = NULL;
231 	tc->INTENCLR.reg = TC_INTENCLR_MC1;
232 	tc->INTFLAG.reg = TC_INTFLAG_MC1;
233 
234 	irq_unlock(key);
235 	return 0;
236 }
237 
counter_sam0_tc32_set_top_value(const struct device * dev,const struct counter_top_cfg * top_cfg)238 static int counter_sam0_tc32_set_top_value(const struct device *dev,
239 					   const struct counter_top_cfg *top_cfg)
240 {
241 	struct counter_sam0_tc32_data *data = dev->data;
242 	const struct counter_sam0_tc32_config *const cfg = dev->config;
243 	TcCount32 *tc = cfg->regs;
244 	int err = 0;
245 	unsigned int key = irq_lock();
246 
247 	if (data->ch.callback) {
248 		irq_unlock(key);
249 		return -EBUSY;
250 	}
251 
252 	if (top_cfg->callback) {
253 		data->top_cb = top_cfg->callback;
254 		data->top_user_data = top_cfg->user_data;
255 		tc->INTENSET.reg = TC_INTFLAG_MC0;
256 	} else {
257 		tc->INTENCLR.reg = TC_INTFLAG_MC0;
258 	}
259 
260 	tc->CC[0].reg = top_cfg->ticks;
261 
262 	if (top_cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
263 		/*
264 		 * Top trigger is on equality of the rising edge only, so
265 		 * manually reset it if the counter has missed the new top.
266 		 */
267 		if (counter_sam0_tc32_read(dev) >= top_cfg->ticks) {
268 			err = -ETIME;
269 			if (top_cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) {
270 				tc->CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;
271 			}
272 		}
273 	} else {
274 		tc->CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;
275 	}
276 
277 	wait_synchronization(tc);
278 
279 	tc->INTFLAG.reg = TC_INTFLAG_MC0;
280 	irq_unlock(key);
281 	return err;
282 }
283 
counter_sam0_tc32_get_pending_int(const struct device * dev)284 static uint32_t counter_sam0_tc32_get_pending_int(const struct device *dev)
285 {
286 	const struct counter_sam0_tc32_config *const cfg = dev->config;
287 	TcCount32 *tc = cfg->regs;
288 
289 	return tc->INTFLAG.reg & (TC_INTFLAG_MC0 | TC_INTFLAG_MC1);
290 }
291 
counter_sam0_tc32_get_top_value(const struct device * dev)292 static uint32_t counter_sam0_tc32_get_top_value(const struct device *dev)
293 {
294 	const struct counter_sam0_tc32_config *const cfg = dev->config;
295 	TcCount32 *tc = cfg->regs;
296 
297 	/*
298 	 * Unsync read is safe here because we're not using
299 	 * capture mode, so things are only set from the CPU
300 	 * end.
301 	 */
302 	return tc->CC[0].reg;
303 }
304 
counter_sam0_tc32_isr(const struct device * dev)305 static void counter_sam0_tc32_isr(const struct device *dev)
306 {
307 	struct counter_sam0_tc32_data *data = dev->data;
308 	const struct counter_sam0_tc32_config *const cfg = dev->config;
309 	TcCount32 *tc = cfg->regs;
310 	uint8_t status = tc->INTFLAG.reg;
311 
312 	/* Acknowledge all interrupts */
313 	tc->INTFLAG.reg = status;
314 
315 	if (status & TC_INTFLAG_MC1) {
316 		if (data->ch.callback) {
317 			counter_alarm_callback_t cb = data->ch.callback;
318 
319 			tc->INTENCLR.reg = TC_INTENCLR_MC1;
320 			data->ch.callback = NULL;
321 
322 			cb(dev, 0, tc->CC[1].reg, data->ch.user_data);
323 		}
324 	}
325 
326 	if (status & TC_INTFLAG_MC0) {
327 		if (data->top_cb) {
328 			data->top_cb(dev, data->top_user_data);
329 		}
330 	}
331 }
332 
counter_sam0_tc32_initialize(const struct device * dev)333 static int counter_sam0_tc32_initialize(const struct device *dev)
334 {
335 	const struct counter_sam0_tc32_config *const cfg = dev->config;
336 	TcCount32 *tc = cfg->regs;
337 	int retval;
338 
339 #ifdef MCLK
340 	/* Enable the GCLK */
341 	GCLK->PCHCTRL[cfg->gclk_id].reg = GCLK_PCHCTRL_GEN_GCLK0 |
342 					  GCLK_PCHCTRL_CHEN;
343 
344 	/* Enable TC clock in MCLK */
345 	*cfg->mclk |= cfg->mclk_mask;
346 #else
347 	/* Enable the GCLK */
348 	GCLK->CLKCTRL.reg = cfg->gclk_clkctrl_id | GCLK_CLKCTRL_GEN_GCLK0 |
349 			    GCLK_CLKCTRL_CLKEN;
350 
351 	/* Enable clock in PM */
352 	PM->APBCMASK.reg |= cfg->pm_apbcmask;
353 #endif
354 
355 	/*
356 	 * In 32 bit mode, NFRQ mode always uses MAX as the counter top, so
357 	 * use MFRQ mode which uses CC0 as the top at the expense of only
358 	 * having CC1 available for alarms.
359 	 */
360 	tc->CTRLA.reg = TC_CTRLA_MODE_COUNT32 |
361 #ifdef TC_CTRLA_WAVEGEN_MFRQ
362 			TC_CTRLA_WAVEGEN_MFRQ |
363 #endif
364 			cfg->prescaler;
365 	wait_synchronization(tc);
366 
367 #ifdef TC_WAVE_WAVEGEN_MFRQ
368 	tc->WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
369 #endif
370 
371 	/* Disable all interrupts */
372 	tc->INTENCLR.reg = TC_INTENCLR_MASK;
373 
374 	retval = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
375 	if (retval < 0) {
376 		return retval;
377 	}
378 
379 	/* Set the initial top as the maximum */
380 	tc->CC[0].reg = UINT32_MAX;
381 
382 	cfg->irq_config_func(dev);
383 
384 	tc->CTRLA.bit.ENABLE = 1;
385 	wait_synchronization(tc);
386 
387 	/* Stop the counter initially */
388 	tc->CTRLBSET.reg = TC_CTRLBSET_CMD_STOP;
389 	wait_synchronization(tc);
390 
391 	return 0;
392 }
393 
394 static DEVICE_API(counter, counter_sam0_tc32_driver_api) = {
395 	.start = counter_sam0_tc32_start,
396 	.stop = counter_sam0_tc32_stop,
397 	.get_value = counter_sam0_tc32_get_value,
398 	.set_alarm = counter_sam0_tc32_set_alarm,
399 	.cancel_alarm = counter_sam0_tc32_cancel_alarm,
400 	.set_top_value = counter_sam0_tc32_set_top_value,
401 	.get_pending_int = counter_sam0_tc32_get_pending_int,
402 	.get_top_value = counter_sam0_tc32_get_top_value,
403 };
404 
405 
406 #ifdef MCLK
407 #define COUNTER_SAM0_TC32_CLOCK_CONTROL(n)				\
408 	.mclk = (volatile uint32_t *)MCLK_MASK_DT_INT_REG_ADDR(n),	\
409 	.mclk_mask = BIT(DT_INST_CLOCKS_CELL_BY_NAME(n, mclk, bit)),	\
410 	.gclk_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, periph_ch),
411 #else
412 #define COUNTER_SAM0_TC32_CLOCK_CONTROL(n)				\
413 	.pm_apbcmask = BIT(DT_INST_CLOCKS_CELL_BY_NAME(n, pm, bit)),	\
414 	.gclk_clkctrl_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, clkctrl_id),
415 #endif
416 
417 #define SAM0_TC32_PRESCALER(n)						\
418 	COND_CODE_1(DT_INST_NODE_HAS_PROP(n, prescaler),		\
419 		    (DT_INST_PROP(n, prescaler)), (1))
420 
421 #define COUNTER_SAM0_TC32_DEVICE(n)					\
422 	PINCTRL_DT_INST_DEFINE(n);					\
423 	static void counter_sam0_tc32_config_##n(const struct device *dev); \
424 	static const struct counter_sam0_tc32_config			\
425 									\
426 	counter_sam0_tc32_dev_config_##n = {				\
427 		.info = {						\
428 			.max_top_value = UINT32_MAX,			\
429 			.freq = SOC_ATMEL_SAM0_GCLK0_FREQ_HZ /		\
430 				SAM0_TC32_PRESCALER(n),			\
431 			.flags = COUNTER_CONFIG_INFO_COUNT_UP,		\
432 			.channels = 1					\
433 		},							\
434 		.regs = (TcCount32 *)DT_INST_REG_ADDR(n),		\
435 		COUNTER_SAM0_TC32_CLOCK_CONTROL(n)			\
436 		.prescaler = UTIL_CAT(TC_CTRLA_PRESCALER_DIV,		\
437 				      SAM0_TC32_PRESCALER(n)),		\
438 		.irq_config_func = &counter_sam0_tc32_config_##n,	\
439 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),		\
440 	};								\
441 									\
442 	static struct counter_sam0_tc32_data counter_sam0_tc32_dev_data_##n;\
443 									\
444 	DEVICE_DT_INST_DEFINE(n,					\
445 			    &counter_sam0_tc32_initialize,		\
446 			    NULL,					\
447 			    &counter_sam0_tc32_dev_data_##n,		\
448 			    &counter_sam0_tc32_dev_config_##n,		\
449 			    PRE_KERNEL_1,				\
450 			    CONFIG_COUNTER_INIT_PRIORITY,		\
451 			    &counter_sam0_tc32_driver_api);		\
452 									\
453 	static void counter_sam0_tc32_config_##n(const struct device *dev) \
454 	{								\
455 		IRQ_CONNECT(DT_INST_IRQN(n),				\
456 			    DT_INST_IRQ(n, priority),			\
457 			    counter_sam0_tc32_isr,			\
458 			    DEVICE_DT_INST_GET(n), 0);			\
459 		irq_enable(DT_INST_IRQN(n));				\
460 	}
461 
462 DT_INST_FOREACH_STATUS_OKAY(COUNTER_SAM0_TC32_DEVICE)
463