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