1 /*
2 * Copyright 2023 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * MRT (Multirate timer) is a lightweight timer with multiple independent channels, each capable
9 * of signalling the shared interrupt with a different period. This driver treats all the channels
10 * as separate devices adhering to the counter API. The parent device is responsible for the
11 * initialization, interrupt handling, and any other module-wide tasks. The current implementation
12 * of this driver prioritizes minimizing image size over speed, because it is not expected for the
13 * functions to be called very often, and this IP is mostly present on low memory devices.
14 */
15
16 #define DT_DRV_COMPAT nxp_mrt
17
18 #include <zephyr/drivers/counter.h>
19 #include <zephyr/sys/util.h>
20 #include <zephyr/drivers/clock_control.h>
21 #include <zephyr/devicetree.h>
22 #include <zephyr/device.h>
23 #include <zephyr/irq.h>
24 #include <zephyr/drivers/reset.h>
25
26 #include <soc.h>
27
28 #define LOG_MODULE_NAME counter_mrt
29 #include <zephyr/logging/log.h>
30 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_COUNTER_LOG_LEVEL);
31
32 /* Device holds a pointer to pointer to data */
33 #define MRT_CHANNEL_DATA(dev) \
34 (*(struct nxp_mrt_channel_data *const *const)dev->data)
35
36 /* Device config->data is an array of data pointers ordered by channel number,
37 * dev->data is a pointer to one of these pointers in that array,
38 * so the value of the dev->data - dev->config->data is the channel index
39 */
40 #define MRT_CHANNEL_ID(dev) \
41 (((struct nxp_mrt_channel_data *const *)dev->data) - \
42 ((const struct nxp_mrt_config *)dev->config)->data)
43
44 /* Specific for each channel */
45 struct nxp_mrt_channel_data {
46 uint32_t top;
47 counter_top_callback_t cb;
48 void *user_data;
49 };
50
51 /* Shared between all channels */
52 struct nxp_mrt_config {
53 struct counter_config_info info;
54 MRT_Type *base;
55 const struct device *clock_dev;
56 clock_control_subsys_t clock_subsys;
57 void (*irq_config_func)(const struct device *dev);
58 struct nxp_mrt_channel_data *const *data;
59 const struct device *const *channels;
60 const struct reset_dt_spec reset;
61 };
62
nxp_mrt_stop(const struct device * dev)63 static int nxp_mrt_stop(const struct device *dev)
64 {
65 const struct nxp_mrt_config *config = dev->config;
66 MRT_Type *base = config->base;
67 int channel_id = MRT_CHANNEL_ID(dev);
68
69 LOG_DBG("MRT@%p channel %d stopped", base, channel_id);
70 LOG_WRN("MRT channel resets upon stopping");
71
72 /* LOAD bit and 0 ivalue allows us to forcibly stop the timer */
73 base->CHANNEL[channel_id].INTVAL = MRT_CHANNEL_INTVAL_LOAD(1);
74
75 return 0;
76 }
77
nxp_mrt_start(const struct device * dev)78 static int nxp_mrt_start(const struct device *dev)
79 {
80 const struct nxp_mrt_config *config = dev->config;
81 MRT_Type *base = config->base;
82 struct nxp_mrt_channel_data *data = MRT_CHANNEL_DATA(dev);
83 int channel_id = MRT_CHANNEL_ID(dev);
84
85 if (data->top <= 1) {
86 /* Zephyr API says default should be max top value */
87 LOG_INF("\"Started\" MRT@%p channel %d with default value %d",
88 base, channel_id, config->info.max_top_value);
89 data->top = config->info.max_top_value;
90 }
91
92 /* Start with previously configured top value (if already running this has no effect) */
93 base->CHANNEL[channel_id].INTVAL = data->top;
94
95 LOG_DBG("MRT@%p channel %d started with top value %d", base, channel_id, data->top);
96
97 return 0;
98 }
99
nxp_mrt_get_value(const struct device * dev,uint32_t * ticks)100 static int nxp_mrt_get_value(const struct device *dev, uint32_t *ticks)
101 {
102 const struct nxp_mrt_config *config = dev->config;
103 MRT_Type *base = config->base;
104 int channel_id = MRT_CHANNEL_ID(dev);
105
106 *ticks = base->CHANNEL[channel_id].TIMER & MRT_CHANNEL_TIMER_VALUE_MASK;
107
108 return 0;
109 }
110
111
nxp_mrt_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)112 static int nxp_mrt_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg)
113 {
114 const struct nxp_mrt_config *config = dev->config;
115 MRT_Type *base = config->base;
116 struct nxp_mrt_channel_data *data = MRT_CHANNEL_DATA(dev);
117 int channel_id = MRT_CHANNEL_ID(dev);
118 /* By default in Zephyr API, the counter resets on changing top value */
119 bool reset = !(cfg->flags & COUNTER_TOP_CFG_DONT_RESET);
120 bool active = base->CHANNEL[channel_id].STAT & MRT_CHANNEL_STAT_RUN_MASK;
121 uint32_t current_val = base->CHANNEL[channel_id].TIMER & MRT_CHANNEL_TIMER_VALUE_MASK;
122 int ret = 0;
123
124 /* Store for use by counter_start */
125 data->top = cfg->ticks;
126
127 /* Used by ISR */
128 data->cb = cfg->callback;
129 data->user_data = cfg->user_data;
130
131
132 /* If not yet started, wait for counter_start because setting reg value starts timer */
133 if (!active) {
134 LOG_DBG("Set MRT@%p channel %d top value to %d", base, channel_id, data->top);
135 return ret;
136 }
137
138 /* Otherwise if currently running, need to check for lateness */
139 if (cfg->ticks < current_val) {
140 LOG_WRN("MRT@%p channel %d received requested top value %d which is "
141 "smaller than current count %d",
142 base, channel_id, cfg->ticks, current_val);
143 /* Zephyr API says return this error in case of lateness
144 * when COUNTER_TOP_CFG_DONT_RESET is set but can still set period
145 */
146 ret = reset ? 0 : -ETIME;
147 /* If user said not to reset, they can also clarify exception for lateness */
148 reset |= cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE;
149 }
150
151 /* Sets the top value. If we need to reset, LOAD bit does this */
152 base->CHANNEL[channel_id].INTVAL = MRT_CHANNEL_INTVAL_IVALUE(cfg->ticks) |
153 MRT_CHANNEL_INTVAL_LOAD(reset ? 1 : 0);
154
155 LOG_DBG("Changed MRT@%p channel %d top value while active to %d",
156 base, channel_id,
157 base->CHANNEL[channel_id].INTVAL & MRT_CHANNEL_INTVAL_IVALUE_MASK);
158
159 return ret;
160 }
161
nxp_mrt_get_top_value(const struct device * dev)162 static uint32_t nxp_mrt_get_top_value(const struct device *dev)
163 {
164 const struct nxp_mrt_config *config = dev->config;
165 MRT_Type *base = config->base;
166 int channel_id = MRT_CHANNEL_ID(dev);
167
168 return base->CHANNEL[channel_id].INTVAL & MRT_CHANNEL_INTVAL_IVALUE_MASK;
169 }
170
nxp_mrt_get_pending_int(const struct device * dev)171 static uint32_t nxp_mrt_get_pending_int(const struct device *dev)
172 {
173 const struct nxp_mrt_config *config = dev->config;
174 MRT_Type *base = config->base;
175 int channel_id = MRT_CHANNEL_ID(dev);
176
177 return base->CHANNEL[channel_id].STAT & MRT_CHANNEL_STAT_INTFLAG_MASK;
178 }
179
nxp_mrt_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)180 static inline int nxp_mrt_set_alarm(const struct device *dev,
181 uint8_t chan_id,
182 const struct counter_alarm_cfg *alarm_cfg)
183 {
184 ARG_UNUSED(dev);
185 ARG_UNUSED(chan_id);
186 ARG_UNUSED(alarm_cfg);
187
188 LOG_ERR("MRT does not support alarms");
189 return -ENOTSUP;
190 }
191
nxp_mrt_cancel_alarm(const struct device * dev,uint8_t chan_id)192 static inline int nxp_mrt_cancel_alarm(const struct device *dev, uint8_t chan_id)
193 {
194 ARG_UNUSED(dev);
195 ARG_UNUSED(chan_id);
196
197 LOG_ERR("MRT does not support alarms");
198 return -ENOTSUP;
199 }
200
nxp_mrt_get_freq(const struct device * dev)201 uint32_t nxp_mrt_get_freq(const struct device *dev)
202 {
203 const struct nxp_mrt_config *config = dev->config;
204 uint32_t freq;
205
206 clock_control_get_rate(config->clock_dev, config->clock_subsys, &freq);
207
208 return freq;
209 }
210
nxp_mrt_init(const struct device * dev)211 static int nxp_mrt_init(const struct device *dev)
212 {
213 const struct nxp_mrt_config *config = dev->config;
214 MRT_Type *base = config->base;
215 uint32_t num_channels = (base->MODCFG & MRT_MODCFG_NOC_MASK) >> MRT_MODCFG_NOC_SHIFT;
216 int ret = 0;
217
218 if (!device_is_ready(config->reset.dev)) {
219 LOG_ERR("Reset device not ready");
220 return -ENODEV;
221 }
222
223 ret = reset_line_toggle(config->reset.dev, config->reset.id);
224 if (ret) {
225 return ret;
226 }
227
228 clock_control_on(config->clock_dev, config->clock_subsys);
229
230 config->irq_config_func(dev);
231
232 /* Enable interrupts for all the channels that have devices */
233 for (int i = 0; i < num_channels; i++) {
234 if (config->channels[i]) {
235 base->CHANNEL[i].CTRL = MRT_CHANNEL_CTRL_INTEN_MASK;
236 }
237 }
238
239 return 0;
240 }
241
nxp_mrt_isr(const struct device * dev)242 static void nxp_mrt_isr(const struct device *dev)
243 {
244 const struct nxp_mrt_config *config = dev->config;
245 MRT_Type *base = config->base;
246 uint32_t irq_pends = base->IRQ_FLAG;
247 uint32_t num_channels = (base->MODCFG & MRT_MODCFG_NOC_MASK) >> MRT_MODCFG_NOC_SHIFT;
248
249 for (int i = 0; i < num_channels; i++) {
250 /* Channel IRQ pending flags lowest order bits in IRQ_FLAG register */
251 if (!(irq_pends & (0x1 << i))) {
252 continue;
253 }
254
255 LOG_DBG("Handling interrupt for MRT%p channel %d", base, i);
256
257 /* W1C interrupt flag */
258 base->CHANNEL[i].STAT |= MRT_CHANNEL_STAT_INTFLAG_MASK;
259
260 /* Channel devs & pointer path to channel cbs is in shared config */
261 if (config->data[i]->cb) {
262 config->data[i]->cb(config->channels[i], config->data[i]->user_data);
263 }
264 }
265 }
266
267 struct counter_driver_api nxp_mrt_api = {
268 .get_value = nxp_mrt_get_value,
269 .start = nxp_mrt_start,
270 .stop = nxp_mrt_stop,
271 .set_top_value = nxp_mrt_set_top_value,
272 .get_top_value = nxp_mrt_get_top_value,
273 .get_pending_int = nxp_mrt_get_pending_int,
274 .set_alarm = nxp_mrt_set_alarm,
275 .cancel_alarm = nxp_mrt_cancel_alarm,
276 .get_freq = nxp_mrt_get_freq,
277 };
278
279 /* Creates a device for a channel (needed for counter API) */
280 #define NXP_MRT_CHANNEL_DEV_INIT(node, mrt_inst) \
281 DEVICE_DT_DEFINE(node, NULL, NULL, \
282 (void *) \
283 &nxp_mrt_##mrt_inst##_channel_datas[DT_REG_ADDR(node)], \
284 &nxp_mrt_##mrt_inst##_config, \
285 POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY, \
286 &nxp_mrt_api); \
287
288 /* Creates a data struct for a channel device */
289 #define NXP_MRT_CHANNEL_DATA_INIT(node) \
290 static struct nxp_mrt_channel_data \
291 nxp_mrt_channel_data_##node; \
292
293 /* Initializes an element of the channel data pointer array */
294 #define NXP_MRT_CHANNEL_DATA_ARRAY_INIT(node) \
295 [DT_REG_ADDR(node)] = \
296 &nxp_mrt_channel_data_##node,
297
298 /* Initializes an element of the channel device pointer array */
299 #define NXP_MRT_CHANNEL_DEV_ARRAY_INIT(node) \
300 [DT_REG_ADDR(node)] = DEVICE_DT_GET(node),
301
302 #define NXP_MRT_INIT(n) \
303 /* ISR is shared between all channels */ \
304 static void nxp_mrt_##n##_irq_config_func(const struct device *dev) \
305 { \
306 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
307 nxp_mrt_isr, DEVICE_DT_INST_GET(n), 0); \
308 irq_enable(DT_INST_IRQN(n)); \
309 } \
310 \
311 /* Initialize all the data structs for active channels */ \
312 DT_INST_FOREACH_CHILD_STATUS_OKAY(n, NXP_MRT_CHANNEL_DATA_INIT) \
313 \
314 /* Create an array of const pointers to the data structs */ \
315 static struct nxp_mrt_channel_data *const nxp_mrt_##n##_channel_datas \
316 [DT_INST_PROP(n, num_channels)] = { \
317 DT_INST_FOREACH_CHILD_STATUS_OKAY(n, \
318 NXP_MRT_CHANNEL_DATA_ARRAY_INIT) \
319 }; \
320 \
321 /* Forward declaration */ \
322 const static struct nxp_mrt_config nxp_mrt_##n##_config; \
323 \
324 /* Create all the channel/counter devices */ \
325 DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, NXP_MRT_CHANNEL_DEV_INIT, n) \
326 \
327 /* This channel device array is needed by the module device ISR */ \
328 const struct device *const nxp_mrt_##n##_channels \
329 [DT_INST_PROP(n, num_channels)] = { \
330 DT_INST_FOREACH_CHILD_STATUS_OKAY(n, \
331 NXP_MRT_CHANNEL_DEV_ARRAY_INIT) \
332 }; \
333 \
334 /* This config struct is shared by all the channels and parent device */\
335 const static struct nxp_mrt_config nxp_mrt_##n##_config = { \
336 .info = { \
337 .max_top_value = \
338 GENMASK(DT_INST_PROP(n, num_bits) - 1, 0), \
339 .channels = 0, \
340 }, \
341 .base = (MRT_Type *)DT_INST_REG_ADDR(n), \
342 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
343 .clock_subsys = (clock_control_subsys_t) \
344 DT_INST_CLOCKS_CELL(n, name), \
345 .irq_config_func = nxp_mrt_##n##_irq_config_func, \
346 .data = nxp_mrt_##n##_channel_datas, \
347 .channels = nxp_mrt_##n##_channels, \
348 .reset = RESET_DT_SPEC_INST_GET(n), \
349 }; \
350 \
351 /* Init parent device in order to handle ISR and init. */ \
352 DEVICE_DT_INST_DEFINE(n, &nxp_mrt_init, NULL, NULL, \
353 &nxp_mrt_##n##_config, \
354 POST_KERNEL, \
355 CONFIG_COUNTER_INIT_PRIORITY, NULL);
356
357 DT_INST_FOREACH_STATUS_OKAY(NXP_MRT_INIT)
358