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