1 /*
2 * Copyright (c) 2021, Piotr Mienkowski
3 * Copyright (c) 2023, Gerson Fernando Budke
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT atmel_sam_tc
9
10 /** @file
11 * @brief Atmel SAM MCU family counter (TC) driver.
12 *
13 * This version of the driver uses a single channel to provide a basic 16-bit
14 * counter (on SAM4E series the counter is 32-bit). Remaining TC channels could
15 * be used in the future to provide additional functionality, e.g. input clock
16 * divider configured via DT properties.
17 *
18 * Remarks:
19 * - The driver is not thread safe.
20 * - The driver does not implement guard periods.
21 * - The driver does not guarantee that short relative alarm will trigger the
22 * interrupt immediately and not after the full cycle / counter overflow.
23 *
24 * Use at your own risk or submit a patch.
25 */
26
27 #include <errno.h>
28 #include <zephyr/sys/__assert.h>
29 #include <zephyr/sys/util.h>
30 #include <zephyr/device.h>
31 #include <zephyr/init.h>
32 #include <soc.h>
33 #include <zephyr/drivers/counter.h>
34 #include <zephyr/drivers/pinctrl.h>
35 #include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
36
37 #include <zephyr/logging/log.h>
38 #include <zephyr/irq.h>
39 LOG_MODULE_REGISTER(counter_sam_tc, CONFIG_COUNTER_LOG_LEVEL);
40
41 #define MAX_ALARMS_PER_TC_CHANNEL 2
42 #if defined(CONFIG_SOC_SERIES_SAM4E) || defined(CONFIG_SOC_SERIES_SAM3X)
43 #define COUNTER_SAM_TOP_VALUE_MAX UINT32_MAX
44 #else
45 #define COUNTER_SAM_TOP_VALUE_MAX UINT16_MAX
46 #define COUNTER_SAM_16_BIT
47 #endif
48
49 /* Device constant configuration parameters */
50 struct counter_sam_dev_cfg {
51 struct counter_config_info info;
52 Tc *regs;
53 uint32_t reg_cmr;
54 uint32_t reg_rc;
55 void (*irq_config_func)(const struct device *dev);
56 const struct atmel_sam_pmc_config clock_cfg[TCCHANNEL_NUMBER];
57 const struct pinctrl_dev_config *pcfg;
58 uint8_t clk_sel;
59 bool nodivclk;
60 uint8_t tc_chan_num;
61 };
62
63 struct counter_sam_alarm_data {
64 counter_alarm_callback_t callback;
65 void *user_data;
66 };
67
68 /* Device run time data */
69 struct counter_sam_dev_data {
70 counter_top_callback_t top_cb;
71 void *top_user_data;
72
73 struct counter_sam_alarm_data alarm[MAX_ALARMS_PER_TC_CHANNEL];
74 };
75
76 static const uint32_t sam_tc_input_freq_table[] = {
77 #if defined(CONFIG_SOC_SERIES_SAME70) || defined(CONFIG_SOC_SERIES_SAMV71)
78 USEC_PER_SEC,
79 SOC_ATMEL_SAM_MCK_FREQ_HZ / 8,
80 SOC_ATMEL_SAM_MCK_FREQ_HZ / 32,
81 SOC_ATMEL_SAM_MCK_FREQ_HZ / 128,
82 32768,
83 #elif defined(CONFIG_SOC_SERIES_SAM4L)
84 USEC_PER_SEC,
85 SOC_ATMEL_SAM_MCK_FREQ_HZ / 2,
86 SOC_ATMEL_SAM_MCK_FREQ_HZ / 8,
87 SOC_ATMEL_SAM_MCK_FREQ_HZ / 32,
88 SOC_ATMEL_SAM_MCK_FREQ_HZ / 128,
89 #else
90 SOC_ATMEL_SAM_MCK_FREQ_HZ / 2,
91 SOC_ATMEL_SAM_MCK_FREQ_HZ / 8,
92 SOC_ATMEL_SAM_MCK_FREQ_HZ / 32,
93 SOC_ATMEL_SAM_MCK_FREQ_HZ / 128,
94 32768,
95 #endif
96 USEC_PER_SEC, USEC_PER_SEC, USEC_PER_SEC,
97 };
98
counter_sam_tc_start(const struct device * dev)99 static int counter_sam_tc_start(const struct device *dev)
100 {
101 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
102 Tc *tc = dev_cfg->regs;
103 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
104
105 tc_ch->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
106
107 return 0;
108 }
109
counter_sam_tc_stop(const struct device * dev)110 static int counter_sam_tc_stop(const struct device *dev)
111 {
112 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
113 Tc *tc = dev_cfg->regs;
114 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
115
116 tc_ch->TC_CCR = TC_CCR_CLKDIS;
117
118 return 0;
119 }
120
counter_sam_tc_get_value(const struct device * dev,uint32_t * ticks)121 static int counter_sam_tc_get_value(const struct device *dev, uint32_t *ticks)
122 {
123 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
124 Tc *tc = dev_cfg->regs;
125 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
126
127 *ticks = tc_ch->TC_CV;
128
129 return 0;
130 }
131
counter_sam_tc_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)132 static int counter_sam_tc_set_alarm(const struct device *dev, uint8_t chan_id,
133 const struct counter_alarm_cfg *alarm_cfg)
134 {
135 struct counter_sam_dev_data *data = dev->data;
136 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
137 Tc *tc = dev_cfg->regs;
138 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
139 uint32_t top_value;
140 uint32_t alarm_value;
141
142 __ASSERT_NO_MSG(alarm_cfg->callback != NULL);
143
144 top_value = tc_ch->TC_RC;
145
146 if ((top_value != 0) && (alarm_cfg->ticks > top_value)) {
147 return -EINVAL;
148 }
149
150 #ifdef COUNTER_SAM_16_BIT
151 if ((top_value == 0) && (alarm_cfg->ticks > UINT16_MAX)) {
152 return -EINVAL;
153 }
154 #endif
155
156 if (data->alarm[chan_id].callback != NULL) {
157 return -EBUSY;
158 }
159
160 if (chan_id == 0) {
161 tc_ch->TC_IDR = TC_IDR_CPAS;
162 } else {
163 tc_ch->TC_IDR = TC_IDR_CPBS;
164 }
165
166 data->alarm[chan_id].callback = alarm_cfg->callback;
167 data->alarm[chan_id].user_data = alarm_cfg->user_data;
168
169 if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) != 0) {
170 alarm_value = alarm_cfg->ticks;
171 } else {
172 alarm_value = tc_ch->TC_CV + alarm_cfg->ticks;
173 if (top_value != 0) {
174 alarm_value %= top_value;
175 }
176 }
177
178 if (chan_id == 0) {
179 tc_ch->TC_RA = alarm_value;
180 /* Clear interrupt status register */
181 (void)tc_ch->TC_SR;
182 tc_ch->TC_IER = TC_IER_CPAS;
183 } else {
184 tc_ch->TC_RB = alarm_value;
185 /* Clear interrupt status register */
186 (void)tc_ch->TC_SR;
187 tc_ch->TC_IER = TC_IER_CPBS;
188 }
189
190 LOG_DBG("set alarm: channel %u, count %u", chan_id, alarm_value);
191
192 return 0;
193 }
194
counter_sam_tc_cancel_alarm(const struct device * dev,uint8_t chan_id)195 static int counter_sam_tc_cancel_alarm(const struct device *dev, uint8_t chan_id)
196 {
197 struct counter_sam_dev_data *data = dev->data;
198 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
199 Tc *tc = dev_cfg->regs;
200 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
201
202 if (chan_id == 0) {
203 tc_ch->TC_IDR = TC_IDR_CPAS;
204 tc_ch->TC_RA = 0;
205 } else {
206 tc_ch->TC_IDR = TC_IDR_CPBS;
207 tc_ch->TC_RB = 0;
208 }
209
210 data->alarm[chan_id].callback = NULL;
211 data->alarm[chan_id].user_data = NULL;
212
213 LOG_DBG("cancel alarm: channel %u", chan_id);
214
215 return 0;
216 }
217
counter_sam_tc_set_top_value(const struct device * dev,const struct counter_top_cfg * top_cfg)218 static int counter_sam_tc_set_top_value(const struct device *dev,
219 const struct counter_top_cfg *top_cfg)
220 {
221 struct counter_sam_dev_data *data = dev->data;
222 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
223 Tc *tc = dev_cfg->regs;
224 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
225 int ret = 0;
226
227 for (int i = 0; i < MAX_ALARMS_PER_TC_CHANNEL; i++) {
228 if (data->alarm[i].callback) {
229 return -EBUSY;
230 }
231 }
232
233 /* Disable the compare interrupt */
234 tc_ch->TC_IDR = TC_IDR_CPCS;
235
236 data->top_cb = top_cfg->callback;
237 data->top_user_data = top_cfg->user_data;
238
239 tc_ch->TC_RC = top_cfg->ticks;
240
241 if ((top_cfg->flags & COUNTER_TOP_CFG_DONT_RESET) != 0) {
242 if (tc_ch->TC_CV >= top_cfg->ticks) {
243 ret = -ETIME;
244 if ((top_cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) != 0) {
245 tc_ch->TC_CCR = TC_CCR_SWTRG;
246 }
247 }
248 } else {
249 tc_ch->TC_CCR = TC_CCR_SWTRG;
250 }
251
252 /* Enable the compare interrupt */
253 tc_ch->TC_IER = TC_IER_CPCS;
254
255 return ret;
256 }
257
counter_sam_tc_get_top_value(const struct device * dev)258 static uint32_t counter_sam_tc_get_top_value(const struct device *dev)
259 {
260 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
261 Tc *tc = dev_cfg->regs;
262 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
263
264 return tc_ch->TC_RC;
265 }
266
counter_sam_tc_get_pending_int(const struct device * dev)267 static uint32_t counter_sam_tc_get_pending_int(const struct device *dev)
268 {
269 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
270 Tc *tc = dev_cfg->regs;
271 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
272
273 return tc_ch->TC_SR & tc_ch->TC_IMR;
274 }
275
counter_sam_tc_isr(const struct device * dev)276 static void counter_sam_tc_isr(const struct device *dev)
277 {
278 struct counter_sam_dev_data *data = dev->data;
279 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
280 Tc *tc = dev_cfg->regs;
281 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
282 uint32_t status;
283
284 status = tc_ch->TC_SR;
285
286 if ((status & TC_SR_CPAS) != 0) {
287 tc_ch->TC_IDR = TC_IDR_CPAS;
288 if (data->alarm[0].callback) {
289 counter_alarm_callback_t cb = data->alarm[0].callback;
290
291 data->alarm[0].callback = NULL;
292 cb(dev, 0, tc_ch->TC_RA, data->alarm[0].user_data);
293 }
294 }
295
296 if ((status & TC_SR_CPBS) != 0) {
297 tc_ch->TC_IDR = TC_IDR_CPBS;
298 if (data->alarm[1].callback) {
299 counter_alarm_callback_t cb = data->alarm[1].callback;
300
301 data->alarm[1].callback = NULL;
302 cb(dev, 1, tc_ch->TC_RB, data->alarm[1].user_data);
303 }
304 }
305
306 if ((status & TC_SR_CPCS) != 0) {
307 if (data->top_cb) {
308 data->top_cb(dev, data->top_user_data);
309 }
310 }
311 }
312
counter_sam_initialize(const struct device * dev)313 static int counter_sam_initialize(const struct device *dev)
314 {
315 const struct counter_sam_dev_cfg *const dev_cfg = dev->config;
316 Tc *const tc = dev_cfg->regs;
317 TcChannel *tc_ch = &tc->TcChannel[dev_cfg->tc_chan_num];
318 int retval;
319
320 /* Connect pins to the peripheral */
321 retval = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
322 if (retval < 0) {
323 return retval;
324 }
325
326 /* Enable channel's clock */
327 (void)clock_control_on(SAM_DT_PMC_CONTROLLER,
328 (clock_control_subsys_t)&dev_cfg->clock_cfg[dev_cfg->tc_chan_num]);
329
330 /* Clock and Mode Selection */
331 tc_ch->TC_CMR = dev_cfg->reg_cmr;
332 tc_ch->TC_RC = dev_cfg->reg_rc;
333
334 #ifdef TC_EMR_NODIVCLK
335 if (dev_cfg->nodivclk) {
336 tc_ch->TC_EMR = TC_EMR_NODIVCLK;
337 }
338 #endif
339 dev_cfg->irq_config_func(dev);
340
341 LOG_INF("Device %s initialized", dev->name);
342
343 return 0;
344 }
345
346 static const struct counter_driver_api counter_sam_driver_api = {
347 .start = counter_sam_tc_start,
348 .stop = counter_sam_tc_stop,
349 .get_value = counter_sam_tc_get_value,
350 .set_alarm = counter_sam_tc_set_alarm,
351 .cancel_alarm = counter_sam_tc_cancel_alarm,
352 .set_top_value = counter_sam_tc_set_top_value,
353 .get_top_value = counter_sam_tc_get_top_value,
354 .get_pending_int = counter_sam_tc_get_pending_int,
355 };
356
357 #define COUNTER_SAM_TC_CMR(n) \
358 (TC_CMR_TCCLKS(DT_INST_PROP_OR(n, clk, 0)) \
359 | TC_CMR_WAVEFORM_WAVSEL_UP_RC \
360 | TC_CMR_WAVE)
361
362 #define COUNTER_SAM_TC_REG_CMR(n) \
363 DT_INST_PROP_OR(n, reg_cmr, COUNTER_SAM_TC_CMR(n))
364
365 #define COUNTER_SAM_TC_INPUT_FREQUENCY(n) \
366 COND_CODE_1(DT_INST_PROP(n, nodivclk), \
367 (SOC_ATMEL_SAM_MCK_FREQ_HZ), \
368 (sam_tc_input_freq_table[COUNTER_SAM_TC_REG_CMR(n) \
369 & TC_CMR_TCCLKS_Msk]))
370
371 #define COUNTER_SAM_TC_INIT(n) \
372 PINCTRL_DT_INST_DEFINE(n); \
373 \
374 static void counter_##n##_sam_config_func(const struct device *dev); \
375 \
376 static const struct counter_sam_dev_cfg counter_##n##_sam_config = { \
377 .info = { \
378 .max_top_value = COUNTER_SAM_TOP_VALUE_MAX, \
379 .freq = COUNTER_SAM_TC_INPUT_FREQUENCY(n), \
380 .flags = COUNTER_CONFIG_INFO_COUNT_UP, \
381 .channels = MAX_ALARMS_PER_TC_CHANNEL \
382 }, \
383 .regs = (Tc *)DT_INST_REG_ADDR(n), \
384 .reg_cmr = COUNTER_SAM_TC_REG_CMR(n), \
385 .reg_rc = DT_INST_PROP_OR(n, reg_rc, 0), \
386 .irq_config_func = &counter_##n##_sam_config_func, \
387 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
388 .nodivclk = DT_INST_PROP(n, nodivclk), \
389 .tc_chan_num = DT_INST_PROP_OR(n, channel, 0), \
390 .clock_cfg = SAM_DT_INST_CLOCKS_PMC_CFG(n), \
391 }; \
392 \
393 static struct counter_sam_dev_data counter_##n##_sam_data; \
394 \
395 DEVICE_DT_INST_DEFINE(n, counter_sam_initialize, NULL, \
396 &counter_##n##_sam_data, &counter_##n##_sam_config, \
397 PRE_KERNEL_1, CONFIG_COUNTER_INIT_PRIORITY, \
398 &counter_sam_driver_api); \
399 \
400 static void counter_##n##_sam_config_func(const struct device *dev) \
401 { \
402 IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, 0, irq), \
403 DT_INST_IRQ_BY_IDX(n, 0, priority), \
404 counter_sam_tc_isr, \
405 DEVICE_DT_INST_GET(n), 0); \
406 irq_enable(DT_INST_IRQ_BY_IDX(n, 0, irq)); \
407 \
408 IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, 1, irq), \
409 DT_INST_IRQ_BY_IDX(n, 1, priority), \
410 counter_sam_tc_isr, \
411 DEVICE_DT_INST_GET(n), 0); \
412 irq_enable(DT_INST_IRQ_BY_IDX(n, 1, irq)); \
413 \
414 IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, 2, irq), \
415 DT_INST_IRQ_BY_IDX(n, 2, priority), \
416 counter_sam_tc_isr, \
417 DEVICE_DT_INST_GET(n), 0); \
418 irq_enable(DT_INST_IRQ_BY_IDX(n, 2, irq)); \
419 }
420
421 DT_INST_FOREACH_STATUS_OKAY(COUNTER_SAM_TC_INIT)
422