1 /*
2 * Copyright (c) 2019 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT microchip_xec_timer
8
9 /**
10 * @file
11 * @brief Microchip XEC Counter driver
12 *
13 * This is the driver for the 16/32-bit counters on the Microchip SoCs.
14 *
15 * Notes:
16 * - The counters are running in down counting mode.
17 * - Interrupts are triggered (if enabled) when the counter
18 * reaches zero.
19 * - These are not free running counters where there are separate
20 * compare values for interrupts. When setting single shot alarms,
21 * the counter values are changed so that interrupts are triggered
22 * when the counters reach zero.
23 */
24
25 #include <zephyr/irq.h>
26 #include <zephyr/logging/log.h>
27 LOG_MODULE_REGISTER(counter_mchp_xec, CONFIG_COUNTER_LOG_LEVEL);
28
29 #include <zephyr/drivers/counter.h>
30 #include <soc.h>
31 #include <errno.h>
32 #include <stdbool.h>
33
34 struct counter_xec_config {
35 struct counter_config_info info;
36 void (*config_func)(void);
37 uint32_t base_address;
38 uint16_t prescaler;
39 uint8_t girq_id;
40 uint8_t girq_bit;
41 };
42
43 struct counter_xec_data {
44 counter_alarm_callback_t alarm_cb;
45 counter_top_callback_t top_cb;
46 void *user_data;
47 };
48
49 #define COUNTER_XEC_REG_BASE(_dev) \
50 ((struct btmr_regs *) \
51 ((const struct counter_xec_config * const) \
52 _dev->config)->base_address)
53
54 #define COUNTER_XEC_CONFIG(_dev) \
55 (((const struct counter_xec_config * const) \
56 _dev->config))
57
58 #define COUNTER_XEC_DATA(_dev) \
59 ((struct counter_xec_data *)dev->data)
60
counter_xec_start(const struct device * dev)61 static int counter_xec_start(const struct device *dev)
62 {
63 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
64
65 if (counter->CTRL & MCHP_BTMR_CTRL_ENABLE) {
66 return -EALREADY;
67 }
68
69 counter->CTRL |= (MCHP_BTMR_CTRL_ENABLE | MCHP_BTMR_CTRL_START);
70
71 LOG_DBG("%p Counter started", dev);
72
73 return 0;
74 }
75
counter_xec_stop(const struct device * dev)76 static int counter_xec_stop(const struct device *dev)
77 {
78 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
79 uint32_t reg;
80
81 if (!(counter->CTRL & MCHP_BTMR_CTRL_ENABLE)) {
82 /* Already stopped, nothing to do */
83 return 0;
84 }
85
86 reg = counter->CTRL;
87 reg &= ~MCHP_BTMR_CTRL_ENABLE;
88 reg &= ~MCHP_BTMR_CTRL_START;
89 reg &= ~MCHP_BTMR_CTRL_HALT;
90 reg &= ~MCHP_BTMR_CTRL_RELOAD;
91 reg &= ~MCHP_BTMR_CTRL_AUTO_RESTART;
92 counter->CTRL = reg;
93
94 counter->IEN = MCHP_BTMR_INTDIS;
95 counter->CNT = counter->PRLD;
96
97 LOG_DBG("%p Counter stopped", dev);
98
99 return 0;
100 }
101
counter_xec_get_value(const struct device * dev,uint32_t * ticks)102 static int counter_xec_get_value(const struct device *dev, uint32_t *ticks)
103 {
104 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
105
106 *ticks = counter->CNT;
107 return 0;
108 }
109
counter_xec_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)110 static int counter_xec_set_alarm(const struct device *dev, uint8_t chan_id,
111 const struct counter_alarm_cfg *alarm_cfg)
112 {
113 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
114 struct counter_xec_data *data = COUNTER_XEC_DATA(dev);
115
116 if (chan_id != 0) {
117 LOG_ERR("Invalid channel id %u", chan_id);
118 return -ENOTSUP;
119 }
120
121 /* Interrupts are only triggered when the counter reaches 0.
122 * So only relative alarms are supported.
123 */
124 if (alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) {
125 return -ENOTSUP;
126 }
127
128 if (data->alarm_cb != NULL) {
129 return -EBUSY;
130 }
131
132 if (!alarm_cfg->callback) {
133 return -EINVAL;
134 }
135
136 if (alarm_cfg->ticks > counter->PRLD) {
137 return -EINVAL;
138 }
139
140 counter->CNT = alarm_cfg->ticks;
141
142 data->alarm_cb = alarm_cfg->callback;
143 data->user_data = alarm_cfg->user_data;
144
145 counter->IEN = MCHP_BTMR_INTEN;
146
147 LOG_DBG("%p Counter alarm set to %u ticks", dev, alarm_cfg->ticks);
148
149 counter->CTRL |= MCHP_BTMR_CTRL_START;
150
151 return 0;
152 }
153
154
counter_xec_cancel_alarm(const struct device * dev,uint8_t chan_id)155 static int counter_xec_cancel_alarm(const struct device *dev, uint8_t chan_id)
156 {
157 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
158 struct counter_xec_data *data = COUNTER_XEC_DATA(dev);
159
160 if (chan_id != 0) {
161 LOG_ERR("Invalid channel id %u", chan_id);
162 return -ENOTSUP;
163 }
164
165 counter->CTRL &= ~MCHP_BTMR_CTRL_START;
166 counter->IEN = MCHP_BTMR_INTDIS;
167
168 data->alarm_cb = NULL;
169 data->user_data = NULL;
170
171 LOG_DBG("%p Counter alarm canceled", dev);
172
173 return 0;
174 }
175
counter_xec_get_pending_int(const struct device * dev)176 static uint32_t counter_xec_get_pending_int(const struct device *dev)
177 {
178 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
179
180 return counter->STS;
181 }
182
counter_xec_get_top_value(const struct device * dev)183 static uint32_t counter_xec_get_top_value(const struct device *dev)
184 {
185 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
186
187 return counter->PRLD;
188 }
189
counter_xec_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)190 static int counter_xec_set_top_value(const struct device *dev,
191 const struct counter_top_cfg *cfg)
192 {
193 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
194 const struct counter_xec_config *counter_cfg = COUNTER_XEC_CONFIG(dev);
195 struct counter_xec_data *data = COUNTER_XEC_DATA(dev);
196 int ret = 0;
197 bool restart;
198
199 if (data->alarm_cb) {
200 return -EBUSY;
201 }
202
203 if (cfg->ticks > counter_cfg->info.max_top_value) {
204 return -EINVAL;
205 }
206
207 restart = ((counter->CTRL & MCHP_BTMR_CTRL_START) != 0U);
208
209 counter->CTRL &= ~MCHP_BTMR_CTRL_START;
210
211 if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
212 if (counter->CNT > cfg->ticks) {
213 ret = -ETIME;
214
215 if (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) {
216 counter->CNT = cfg->ticks;
217 }
218 }
219 } else {
220 counter->CNT = cfg->ticks;
221 }
222
223 counter->PRLD = cfg->ticks;
224
225 data->top_cb = cfg->callback;
226 data->user_data = cfg->user_data;
227
228 if (data->top_cb) {
229 counter->IEN = MCHP_BTMR_INTEN;
230 counter->CTRL |= MCHP_BTMR_CTRL_AUTO_RESTART;
231 } else {
232 counter->IEN = MCHP_BTMR_INTDIS;
233 counter->CTRL &= ~MCHP_BTMR_CTRL_AUTO_RESTART;
234 }
235
236 LOG_DBG("%p Counter top value was set to %u", dev, cfg->ticks);
237
238 if (restart) {
239 counter->CTRL |= MCHP_BTMR_CTRL_START;
240 }
241
242 return ret;
243 }
244
counter_xec_isr(const struct device * dev)245 static void counter_xec_isr(const struct device *dev)
246 {
247 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
248 const struct counter_xec_config *counter_cfg = COUNTER_XEC_CONFIG(dev);
249 struct counter_xec_data *data = COUNTER_XEC_DATA(dev);
250 counter_alarm_callback_t alarm_cb;
251 void *user_data;
252
253 counter->STS = MCHP_BTMR_STS_ACTIVE;
254
255 #if defined(CONFIG_SOC_MEC172X_NSZ)
256 mchp_soc_ecia_girq_src_clr(counter_cfg->girq_id, counter_cfg->girq_bit);
257 #else
258 MCHP_GIRQ_SRC(counter_cfg->girq_id) = BIT(counter_cfg->girq_bit);
259 #endif
260
261 LOG_DBG("%p Counter ISR", dev);
262
263 if (data->alarm_cb) {
264 /* Alarm is one-shot, so disable interrupt and callback */
265 counter->IEN = MCHP_BTMR_INTDIS;
266
267 alarm_cb = data->alarm_cb;
268 data->alarm_cb = NULL;
269 user_data = data->user_data;
270
271 alarm_cb(dev, 0, counter->CNT, user_data);
272 } else if (data->top_cb) {
273 data->top_cb(dev, data->user_data);
274 }
275 }
276
277 static DEVICE_API(counter, counter_xec_api) = {
278 .start = counter_xec_start,
279 .stop = counter_xec_stop,
280 .get_value = counter_xec_get_value,
281 .set_alarm = counter_xec_set_alarm,
282 .cancel_alarm = counter_xec_cancel_alarm,
283 .set_top_value = counter_xec_set_top_value,
284 .get_pending_int = counter_xec_get_pending_int,
285 .get_top_value = counter_xec_get_top_value,
286 };
287
counter_xec_init(const struct device * dev)288 static int counter_xec_init(const struct device *dev)
289 {
290 struct btmr_regs *counter = COUNTER_XEC_REG_BASE(dev);
291 const struct counter_xec_config *counter_cfg = COUNTER_XEC_CONFIG(dev);
292
293 counter_xec_stop(dev);
294
295 counter->CTRL &= ~MCHP_BTMR_CTRL_COUNT_UP;
296 counter->CTRL |= (counter_cfg->prescaler << MCHP_BTMR_CTRL_PRESCALE_POS) &
297 MCHP_BTMR_CTRL_PRESCALE_MASK;
298
299 /* Set preload and actually pre-load the counter */
300 counter->PRLD = counter_cfg->info.max_top_value;
301 counter->CNT = counter_cfg->info.max_top_value;
302
303 #if defined(CONFIG_SOC_MEC172X_NSZ)
304 mchp_soc_ecia_girq_src_en(counter_cfg->girq_id, counter_cfg->girq_bit);
305 #else
306 MCHP_GIRQ_ENSET(counter_cfg->girq_id) = BIT(counter_cfg->girq_bit);
307 #endif
308
309 counter_cfg->config_func();
310
311 return 0;
312 }
313
314 #define COUNTER_XEC_INIT(inst) \
315 static void counter_xec_irq_config_##inst(void); \
316 \
317 static struct counter_xec_data counter_xec_dev_data_##inst; \
318 \
319 static struct counter_xec_config counter_xec_dev_config_##inst = { \
320 .info = { \
321 .max_top_value = DT_INST_PROP(inst, max_value), \
322 .freq = DT_INST_PROP(inst, clock_frequency) / \
323 (1 << DT_INST_PROP(inst, prescaler)), \
324 .flags = 0, \
325 .channels = 1, \
326 }, \
327 \
328 .config_func = counter_xec_irq_config_##inst, \
329 .base_address = DT_INST_REG_ADDR(inst), \
330 .prescaler = DT_INST_PROP(inst, prescaler), \
331 .girq_id = DT_INST_PROP_BY_IDX(0, girqs, 0), \
332 .girq_bit = DT_INST_PROP_BY_IDX(0, girqs, 1), \
333 }; \
334 \
335 DEVICE_DT_INST_DEFINE(inst, \
336 counter_xec_init, \
337 NULL, \
338 &counter_xec_dev_data_##inst, \
339 &counter_xec_dev_config_##inst, \
340 POST_KERNEL, \
341 CONFIG_COUNTER_INIT_PRIORITY, \
342 &counter_xec_api); \
343 \
344 static void counter_xec_irq_config_##inst(void) \
345 { \
346 IRQ_CONNECT(DT_INST_IRQN(inst), \
347 DT_INST_IRQ(inst, priority), \
348 counter_xec_isr, \
349 DEVICE_DT_INST_GET(inst), 0); \
350 irq_enable(DT_INST_IRQN(inst)); \
351 }
352
353 DT_INST_FOREACH_STATUS_OKAY(COUNTER_XEC_INIT)
354