1 /*
2 * Copyright (c) 2019, Linaro Limited.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nxp_imx_gpt
8
9 #include <zephyr/drivers/counter.h>
10 #include <zephyr/drivers/clock_control.h>
11 #include <zephyr/irq.h>
12 #include <fsl_gpt.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/sys/barrier.h>
15
16 LOG_MODULE_REGISTER(mcux_gpt, CONFIG_COUNTER_LOG_LEVEL);
17
18 struct mcux_gpt_config {
19 /* info must be first element */
20 struct counter_config_info info;
21 const struct device *clock_dev;
22 clock_control_subsys_t clock_subsys;
23 GPT_Type *base;
24 clock_name_t clock_source;
25 };
26
27 struct mcux_gpt_data {
28 counter_alarm_callback_t alarm_callback;
29 counter_top_callback_t top_callback;
30 void *alarm_user_data;
31 void *top_user_data;
32 };
33
mcux_gpt_start(const struct device * dev)34 static int mcux_gpt_start(const struct device *dev)
35 {
36 const struct mcux_gpt_config *config = dev->config;
37
38 GPT_StartTimer(config->base);
39
40 return 0;
41 }
42
mcux_gpt_stop(const struct device * dev)43 static int mcux_gpt_stop(const struct device *dev)
44 {
45 const struct mcux_gpt_config *config = dev->config;
46
47 GPT_StopTimer(config->base);
48
49 return 0;
50 }
51
mcux_gpt_get_value(const struct device * dev,uint32_t * ticks)52 static int mcux_gpt_get_value(const struct device *dev, uint32_t *ticks)
53 {
54 const struct mcux_gpt_config *config = dev->config;
55
56 *ticks = GPT_GetCurrentTimerCount(config->base);
57 return 0;
58 }
59
mcux_gpt_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)60 static int mcux_gpt_set_alarm(const struct device *dev, uint8_t chan_id,
61 const struct counter_alarm_cfg *alarm_cfg)
62 {
63 const struct mcux_gpt_config *config = dev->config;
64 struct mcux_gpt_data *data = dev->data;
65
66 uint32_t current = GPT_GetCurrentTimerCount(config->base);
67 uint32_t ticks = alarm_cfg->ticks;
68
69 if (chan_id != 0) {
70 LOG_ERR("Invalid channel id");
71 return -EINVAL;
72 }
73
74 if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) {
75 ticks += current;
76 }
77
78 if (data->alarm_callback) {
79 return -EBUSY;
80 }
81
82 data->alarm_callback = alarm_cfg->callback;
83 data->alarm_user_data = alarm_cfg->user_data;
84
85 GPT_SetOutputCompareValue(config->base, kGPT_OutputCompare_Channel1,
86 ticks);
87 GPT_EnableInterrupts(config->base, kGPT_OutputCompare1InterruptEnable);
88
89 return 0;
90 }
91
mcux_gpt_cancel_alarm(const struct device * dev,uint8_t chan_id)92 static int mcux_gpt_cancel_alarm(const struct device *dev, uint8_t chan_id)
93 {
94 const struct mcux_gpt_config *config = dev->config;
95 struct mcux_gpt_data *data = dev->data;
96
97 if (chan_id != 0) {
98 LOG_ERR("Invalid channel id");
99 return -EINVAL;
100 }
101
102 GPT_DisableInterrupts(config->base, kGPT_OutputCompare1InterruptEnable);
103 data->alarm_callback = NULL;
104
105 return 0;
106 }
107
mcux_gpt_isr(const struct device * dev)108 void mcux_gpt_isr(const struct device *dev)
109 {
110 const struct mcux_gpt_config *config = dev->config;
111 struct mcux_gpt_data *data = dev->data;
112 uint32_t current = GPT_GetCurrentTimerCount(config->base);
113 uint32_t status;
114
115 status = GPT_GetStatusFlags(config->base, kGPT_OutputCompare1Flag |
116 kGPT_RollOverFlag);
117 GPT_ClearStatusFlags(config->base, status);
118 barrier_dsync_fence_full();
119
120 if ((status & kGPT_OutputCompare1Flag) && data->alarm_callback) {
121 GPT_DisableInterrupts(config->base,
122 kGPT_OutputCompare1InterruptEnable);
123 counter_alarm_callback_t alarm_cb = data->alarm_callback;
124 data->alarm_callback = NULL;
125 alarm_cb(dev, 0, current, data->alarm_user_data);
126 }
127
128 if ((status & kGPT_RollOverFlag) && data->top_callback) {
129 data->top_callback(dev, data->top_user_data);
130 }
131 }
132
mcux_gpt_get_pending_int(const struct device * dev)133 static uint32_t mcux_gpt_get_pending_int(const struct device *dev)
134 {
135 const struct mcux_gpt_config *config = dev->config;
136
137 return GPT_GetStatusFlags(config->base, kGPT_OutputCompare1Flag);
138 }
139
mcux_gpt_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)140 static int mcux_gpt_set_top_value(const struct device *dev,
141 const struct counter_top_cfg *cfg)
142 {
143 const struct mcux_gpt_config *config = dev->config;
144 struct mcux_gpt_data *data = dev->data;
145
146 if (cfg->ticks != config->info.max_top_value) {
147 LOG_ERR("Wrap can only be set to 0x%x",
148 config->info.max_top_value);
149 return -ENOTSUP;
150 }
151
152 data->top_callback = cfg->callback;
153 data->top_user_data = cfg->user_data;
154
155 GPT_EnableInterrupts(config->base, kGPT_RollOverFlagInterruptEnable);
156
157 return 0;
158 }
159
mcux_gpt_get_top_value(const struct device * dev)160 static uint32_t mcux_gpt_get_top_value(const struct device *dev)
161 {
162 const struct mcux_gpt_config *config = dev->config;
163
164 return config->info.max_top_value;
165 }
166
mcux_gpt_init(const struct device * dev)167 static int mcux_gpt_init(const struct device *dev)
168 {
169 const struct mcux_gpt_config *config = dev->config;
170 gpt_config_t gptConfig;
171 uint32_t clock_freq;
172
173 if (!device_is_ready(config->clock_dev)) {
174 LOG_ERR("clock control device not ready");
175 return -ENODEV;
176 }
177
178 if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
179 &clock_freq)) {
180 return -EINVAL;
181 }
182
183 /* Adjust divider to match expected freq */
184 if (clock_freq % config->info.freq) {
185 LOG_ERR("Cannot Adjust GPT freq to %u\n", config->info.freq);
186 LOG_ERR("clock src is %u\n", clock_freq);
187 return -EINVAL;
188 }
189
190 GPT_GetDefaultConfig(&gptConfig);
191 gptConfig.enableFreeRun = true; /* Do not reset on compare */
192 gptConfig.clockSource = kGPT_ClockSource_Periph;
193 gptConfig.divider = clock_freq / config->info.freq;
194 GPT_Init(config->base, &gptConfig);
195
196 return 0;
197 }
198
199 static const struct counter_driver_api mcux_gpt_driver_api = {
200 .start = mcux_gpt_start,
201 .stop = mcux_gpt_stop,
202 .get_value = mcux_gpt_get_value,
203 .set_alarm = mcux_gpt_set_alarm,
204 .cancel_alarm = mcux_gpt_cancel_alarm,
205 .set_top_value = mcux_gpt_set_top_value,
206 .get_pending_int = mcux_gpt_get_pending_int,
207 .get_top_value = mcux_gpt_get_top_value,
208 };
209
210 #define GPT_DEVICE_INIT_MCUX(n) \
211 static struct mcux_gpt_data mcux_gpt_data_ ## n; \
212 \
213 static const struct mcux_gpt_config mcux_gpt_config_ ## n = { \
214 .base = (void *)DT_INST_REG_ADDR(n), \
215 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
216 .clock_subsys = \
217 (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name),\
218 .info = { \
219 .max_top_value = UINT32_MAX, \
220 .freq = DT_INST_PROP(n, gptfreq), \
221 .channels = 1, \
222 .flags = COUNTER_CONFIG_INFO_COUNT_UP, \
223 }, \
224 }; \
225 \
226 static int mcux_gpt_## n ##_init(const struct device *dev); \
227 DEVICE_DT_INST_DEFINE(n, \
228 mcux_gpt_## n ##_init, \
229 NULL, \
230 &mcux_gpt_data_ ## n, \
231 &mcux_gpt_config_ ## n, \
232 POST_KERNEL, \
233 CONFIG_COUNTER_INIT_PRIORITY, \
234 &mcux_gpt_driver_api); \
235 \
236 static int mcux_gpt_## n ##_init(const struct device *dev) \
237 { \
238 IRQ_CONNECT(DT_INST_IRQN(n), \
239 DT_INST_IRQ(n, priority), \
240 mcux_gpt_isr, DEVICE_DT_INST_GET(n), 0); \
241 irq_enable(DT_INST_IRQN(n)); \
242 return mcux_gpt_init(dev); \
243 } \
244
245 DT_INST_FOREACH_STATUS_OKAY(GPT_DEVICE_INIT_MCUX)
246