1 /*
2 * Copyright (c) 2020 Henrik Brix Andersen <henrik@brixandersen.dk>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT xlnx_xps_timer_1_00_a
8
9 #include <zephyr/arch/cpu.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/counter.h>
12 #include <zephyr/irq.h>
13 #include <zephyr/sys/sys_io.h>
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(xlnx_axi_timer, CONFIG_COUNTER_LOG_LEVEL);
16
17 /* AXI Timer v2.0 registers offsets (See Xilinx PG079 for details) */
18 #define TCSR0_OFFSET 0x00
19 #define TLR0_OFFSET 0x04
20 #define TCR0_OFFSET 0x08
21 #define TCSR1_OFFSET 0x10
22 #define TLR1_OFFSET 0x14
23 #define TCR1_OFFSET 0x18
24
25 /* TCSRx bit definitions */
26 #define TCSR_MDT BIT(0)
27 #define TCSR_UDT BIT(1)
28 #define TCSR_GENT BIT(2)
29 #define TCSR_CAPT BIT(3)
30 #define TCSR_ARHT BIT(4)
31 #define TCSR_LOAD BIT(5)
32 #define TCSR_ENIT BIT(6)
33 #define TCSR_ENT BIT(7)
34 #define TCSR_TINT BIT(8)
35 #define TCSR_PWMA BIT(9)
36 #define TCSR_ENALL BIT(10)
37 #define TCSR_CASC BIT(11)
38
39 /* 1st timer used as main timer in auto-reload, count-down. generate mode */
40 #define TCSR0_DEFAULT (TCSR_ENIT | TCSR_ARHT | TCSR_GENT | TCSR_UDT)
41
42 /* 2nd timer (if available) used as alarm timer in count-down, generate mode */
43 #define TCSR1_DEFAULT (TCSR_ENIT | TCSR_GENT | TCSR_UDT)
44
45 struct xlnx_axi_timer_config {
46 struct counter_config_info info;
47 mm_reg_t base;
48 void (*irq_config_func)(const struct device *dev);
49 };
50
51 struct xlnx_axi_timer_data {
52 counter_top_callback_t top_callback;
53 void *top_user_data;
54 counter_alarm_callback_t alarm_callback;
55 void *alarm_user_data;
56 };
57
xlnx_axi_timer_read32(const struct device * dev,mm_reg_t offset)58 static inline uint32_t xlnx_axi_timer_read32(const struct device *dev,
59 mm_reg_t offset)
60 {
61 const struct xlnx_axi_timer_config *config = dev->config;
62
63 return sys_read32(config->base + offset);
64 }
65
xlnx_axi_timer_write32(const struct device * dev,uint32_t value,mm_reg_t offset)66 static inline void xlnx_axi_timer_write32(const struct device *dev,
67 uint32_t value,
68 mm_reg_t offset)
69 {
70 const struct xlnx_axi_timer_config *config = dev->config;
71
72 sys_write32(value, config->base + offset);
73 }
74
xlnx_axi_timer_start(const struct device * dev)75 static int xlnx_axi_timer_start(const struct device *dev)
76 {
77 const struct xlnx_axi_timer_data *data = dev->data;
78 uint32_t tcsr = TCSR0_DEFAULT | TCSR_ENT;
79
80 LOG_DBG("starting timer");
81
82 if (data->alarm_callback) {
83 /* Start both timers synchronously */
84 tcsr |= TCSR_ENALL;
85 }
86
87 xlnx_axi_timer_write32(dev, tcsr, TCSR0_OFFSET);
88
89 return 0;
90 }
91
xlnx_axi_timer_stop(const struct device * dev)92 static int xlnx_axi_timer_stop(const struct device *dev)
93 {
94 const struct xlnx_axi_timer_config *config = dev->config;
95 unsigned int key;
96
97 LOG_DBG("stopping timer");
98
99 key = irq_lock();
100
101 /* The timers cannot be stopped synchronously */
102 if (config->info.channels > 0) {
103 xlnx_axi_timer_write32(dev, TCSR1_DEFAULT, TCSR1_OFFSET);
104 }
105 xlnx_axi_timer_write32(dev, TCSR0_DEFAULT, TCSR0_OFFSET);
106
107 irq_unlock(key);
108
109 return 0;
110 }
111
xlnx_axi_timer_get_value(const struct device * dev,uint32_t * ticks)112 static int xlnx_axi_timer_get_value(const struct device *dev, uint32_t *ticks)
113 {
114
115 *ticks = xlnx_axi_timer_read32(dev, TCR0_OFFSET);
116
117 return 0;
118 }
119
xlnx_axi_timer_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * cfg)120 static int xlnx_axi_timer_set_alarm(const struct device *dev, uint8_t chan_id,
121 const struct counter_alarm_cfg *cfg)
122 {
123 struct xlnx_axi_timer_data *data = dev->data;
124 unsigned int key;
125 uint32_t tcsr;
126
127 ARG_UNUSED(chan_id);
128
129 if (cfg->callback == NULL) {
130 return -EINVAL;
131 }
132
133 if (data->alarm_callback != NULL) {
134 return -EBUSY;
135 }
136
137 if (cfg->ticks > xlnx_axi_timer_read32(dev, TLR0_OFFSET)) {
138 return -EINVAL;
139 }
140
141 if (cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) {
142 /*
143 * Since two different timers (with the same clock signal) are
144 * used for main timer and alarm timer we cannot support
145 * absolute alarms in a reliable way.
146 */
147 return -ENOTSUP;
148 }
149
150 LOG_DBG("triggering alarm in 0x%08x ticks", cfg->ticks);
151
152 /* Load alarm timer */
153 xlnx_axi_timer_write32(dev, cfg->ticks, TLR1_OFFSET);
154 xlnx_axi_timer_write32(dev, TCSR1_DEFAULT | TCSR_LOAD, TCSR1_OFFSET);
155
156 key = irq_lock();
157
158 data->alarm_callback = cfg->callback;
159 data->alarm_user_data = cfg->user_data;
160
161 /* Enable alarm timer only if main timer already enabled */
162 tcsr = xlnx_axi_timer_read32(dev, TCSR0_OFFSET);
163 tcsr &= TCSR_ENT;
164 xlnx_axi_timer_write32(dev, TCSR1_DEFAULT | tcsr, TCSR1_OFFSET);
165
166 irq_unlock(key);
167
168 return 0;
169 }
170
xlnx_axi_timer_cancel_alarm(const struct device * dev,uint8_t chan_id)171 static int xlnx_axi_timer_cancel_alarm(const struct device *dev,
172 uint8_t chan_id)
173 {
174 struct xlnx_axi_timer_data *data = dev->data;
175
176 ARG_UNUSED(chan_id);
177
178 LOG_DBG("cancelling alarm");
179
180 xlnx_axi_timer_write32(dev, TCSR1_DEFAULT, TCSR1_OFFSET);
181 data->alarm_callback = NULL;
182 data->alarm_user_data = NULL;
183
184 return 0;
185 }
186
xlnx_axi_timer_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)187 static int xlnx_axi_timer_set_top_value(const struct device *dev,
188 const struct counter_top_cfg *cfg)
189 {
190 struct xlnx_axi_timer_data *data = dev->data;
191 bool reload = true;
192 uint32_t tcsr;
193 uint32_t now;
194
195 if (cfg->ticks == 0) {
196 return -EINVAL;
197 }
198
199 if (data->alarm_callback) {
200 return -EBUSY;
201 }
202
203 LOG_DBG("setting top value to 0x%08x", cfg->ticks);
204
205 data->top_callback = cfg->callback;
206 data->top_user_data = cfg->user_data;
207
208 if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
209 reload = false;
210
211 if (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) {
212 now = xlnx_axi_timer_read32(dev, TCR0_OFFSET);
213 reload = cfg->ticks < now;
214 }
215 }
216
217 tcsr = xlnx_axi_timer_read32(dev, TCSR0_OFFSET);
218 if ((tcsr & TCSR_ENT) == 0U) {
219 /* Timer not enabled, force reload of new top value */
220 reload = true;
221 }
222
223 xlnx_axi_timer_write32(dev, cfg->ticks, TLR0_OFFSET);
224
225 if (reload) {
226 xlnx_axi_timer_write32(dev, tcsr | TCSR_LOAD, TCSR0_OFFSET);
227 xlnx_axi_timer_write32(dev, tcsr, TCSR0_OFFSET);
228 }
229
230 return 0;
231 }
232
xlnx_axi_timer_get_pending_int(const struct device * dev)233 static uint32_t xlnx_axi_timer_get_pending_int(const struct device *dev)
234 {
235 const struct xlnx_axi_timer_config *config = dev->config;
236 uint32_t pending = 0;
237 uint32_t tcsr;
238
239 tcsr = xlnx_axi_timer_read32(dev, TCSR0_OFFSET);
240 if (tcsr & TCSR_TINT) {
241 pending = 1;
242 }
243
244 if (config->info.channels > 0) {
245 tcsr = xlnx_axi_timer_read32(dev, TCSR1_OFFSET);
246 if (tcsr & TCSR_TINT) {
247 pending = 1;
248 }
249 }
250
251 LOG_DBG("%sinterrupt pending", pending ? "" : "no ");
252
253 return pending;
254 }
255
xlnx_axi_timer_get_top_value(const struct device * dev)256 static uint32_t xlnx_axi_timer_get_top_value(const struct device *dev)
257 {
258 return xlnx_axi_timer_read32(dev, TLR0_OFFSET);
259 }
260
xlnx_axi_timer_isr(const struct device * dev)261 static void xlnx_axi_timer_isr(const struct device *dev)
262 {
263 struct xlnx_axi_timer_data *data = dev->data;
264 counter_alarm_callback_t alarm_cb;
265 uint32_t tcsr;
266 uint32_t now;
267
268 tcsr = xlnx_axi_timer_read32(dev, TCSR1_OFFSET);
269 if (tcsr & TCSR_TINT) {
270 xlnx_axi_timer_write32(dev, TCSR1_DEFAULT | TCSR_TINT,
271 TCSR1_OFFSET);
272
273 if (data->alarm_callback) {
274 now = xlnx_axi_timer_read32(dev, TCR0_OFFSET);
275 alarm_cb = data->alarm_callback;
276 data->alarm_callback = NULL;
277
278 alarm_cb(dev, 0, now, data->alarm_user_data);
279 }
280 }
281
282 tcsr = xlnx_axi_timer_read32(dev, TCSR0_OFFSET);
283 if (tcsr & TCSR_TINT) {
284 xlnx_axi_timer_write32(dev, tcsr, TCSR0_OFFSET);
285
286 if (data->top_callback) {
287 data->top_callback(dev, data->top_user_data);
288 }
289 }
290 }
291
xlnx_axi_timer_init(const struct device * dev)292 static int xlnx_axi_timer_init(const struct device *dev)
293 {
294 const struct xlnx_axi_timer_config *config = dev->config;
295
296 LOG_DBG("max top value = 0x%08x", config->info.max_top_value);
297 LOG_DBG("frequency = %d", config->info.freq);
298 LOG_DBG("channels = %d", config->info.channels);
299
300 xlnx_axi_timer_write32(dev, config->info.max_top_value, TLR0_OFFSET);
301 xlnx_axi_timer_write32(dev, TCSR0_DEFAULT | TCSR_LOAD, TCSR0_OFFSET);
302
303 if (config->info.channels > 0) {
304 xlnx_axi_timer_write32(dev, TCSR1_DEFAULT, TCSR1_OFFSET);
305 }
306
307 config->irq_config_func(dev);
308
309 return 0;
310 }
311
312 static DEVICE_API(counter, xlnx_axi_timer_driver_api) = {
313 .start = xlnx_axi_timer_start,
314 .stop = xlnx_axi_timer_stop,
315 .get_value = xlnx_axi_timer_get_value,
316 .set_alarm = xlnx_axi_timer_set_alarm,
317 .cancel_alarm = xlnx_axi_timer_cancel_alarm,
318 .set_top_value = xlnx_axi_timer_set_top_value,
319 .get_pending_int = xlnx_axi_timer_get_pending_int,
320 .get_top_value = xlnx_axi_timer_get_top_value,
321 };
322
323 #define XLNX_AXI_TIMER_INIT(n) \
324 static void xlnx_axi_timer_config_func_##n(const struct device *dev); \
325 \
326 static struct xlnx_axi_timer_config xlnx_axi_timer_config_##n = { \
327 .info = { \
328 .max_top_value = \
329 GENMASK(DT_INST_PROP(n, xlnx_count_width) - 1, 0), \
330 .freq = DT_INST_PROP(n, clock_frequency), \
331 .flags = 0, \
332 .channels = \
333 COND_CODE_1(DT_INST_PROP(n, xlnx_one_timer_only), \
334 (0), (1)), \
335 }, \
336 .base = DT_INST_REG_ADDR(n), \
337 .irq_config_func = xlnx_axi_timer_config_func_##n, \
338 }; \
339 \
340 static struct xlnx_axi_timer_data xlnx_axi_timer_data_##n; \
341 \
342 DEVICE_DT_INST_DEFINE(n, &xlnx_axi_timer_init, \
343 NULL, \
344 &xlnx_axi_timer_data_##n, \
345 &xlnx_axi_timer_config_##n, \
346 POST_KERNEL, \
347 CONFIG_COUNTER_INIT_PRIORITY, \
348 &xlnx_axi_timer_driver_api); \
349 \
350 static void xlnx_axi_timer_config_func_##n(const struct device *dev) \
351 { \
352 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
353 xlnx_axi_timer_isr, \
354 DEVICE_DT_INST_GET(n), 0); \
355 irq_enable(DT_INST_IRQN(n)); \
356 }
357
358 DT_INST_FOREACH_STATUS_OKAY(XLNX_AXI_TIMER_INIT)
359