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