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