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