1 /*
2  * Copyright 2023-2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_tpm_timer
8 
9 #include <zephyr/drivers/counter.h>
10 #include <zephyr/drivers/clock_control.h>
11 #include <zephyr/irq.h>
12 #include <zephyr/logging/log.h>
13 
14 #include <fsl_tpm.h>
15 
16 LOG_MODULE_REGISTER(mcux_tpm, CONFIG_COUNTER_LOG_LEVEL);
17 
18 #define DEV_CFG(_dev) ((const struct mcux_tpm_config *)(_dev)->config)
19 #define DEV_DATA(_dev) ((struct mcux_tpm_data *)(_dev)->data)
20 
21 struct mcux_tpm_config {
22 	struct counter_config_info info;
23 
24 	DEVICE_MMIO_NAMED_ROM(tpm_mmio);
25 
26 	const struct device *clock_dev;
27 	clock_control_subsys_t clock_subsys;
28 
29 	tpm_clock_source_t tpm_clock_source;
30 	tpm_clock_prescale_t prescale;
31 };
32 
33 struct mcux_tpm_data {
34 	DEVICE_MMIO_NAMED_RAM(tpm_mmio);
35 	counter_alarm_callback_t alarm_callback;
36 	counter_top_callback_t top_callback;
37 	uint32_t freq;
38 	void *alarm_user_data;
39 	void *top_user_data;
40 };
41 
get_base(const struct device * dev)42 static TPM_Type *get_base(const struct device *dev)
43 {
44 	return (TPM_Type *)DEVICE_MMIO_NAMED_GET(dev, tpm_mmio);
45 }
46 
mcux_tpm_start(const struct device * dev)47 static int mcux_tpm_start(const struct device *dev)
48 {
49 	const struct mcux_tpm_config *config = dev->config;
50 	TPM_Type *base = get_base(dev);
51 
52 	TPM_StartTimer(base, config->tpm_clock_source);
53 
54 	return 0;
55 }
56 
mcux_tpm_stop(const struct device * dev)57 static int mcux_tpm_stop(const struct device *dev)
58 {
59 	TPM_Type *base = get_base(dev);
60 
61 	TPM_StopTimer(base);
62 
63 	return 0;
64 }
65 
mcux_tpm_get_value(const struct device * dev,uint32_t * ticks)66 static int mcux_tpm_get_value(const struct device *dev, uint32_t *ticks)
67 {
68 	TPM_Type *base = get_base(dev);
69 
70 	*ticks = TPM_GetCurrentTimerCount(base);
71 
72 	return 0;
73 }
74 
mcux_tpm_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)75 static int mcux_tpm_set_alarm(const struct device *dev, uint8_t chan_id,
76 			      const struct counter_alarm_cfg *alarm_cfg)
77 {
78 	TPM_Type *base = get_base(dev);
79 	uint32_t current = TPM_GetCurrentTimerCount(base);
80 	uint32_t top_value = base->MOD;
81 	struct mcux_tpm_data *data = dev->data;
82 	uint32_t ticks = alarm_cfg->ticks;
83 
84 	if (chan_id != kTPM_Chnl_0) {
85 		LOG_ERR("Invalid channel id");
86 		return -EINVAL;
87 	}
88 
89 	if (ticks > (top_value)) {
90 		return -EINVAL;
91 	}
92 
93 	if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) {
94 		if (top_value - current >= ticks) {
95 			ticks += current;
96 		} else {
97 			ticks -= top_value - current;
98 		}
99 	}
100 
101 	if (data->alarm_callback) {
102 		return -EBUSY;
103 	}
104 
105 	data->alarm_callback = alarm_cfg->callback;
106 	data->alarm_user_data = alarm_cfg->user_data;
107 
108 	TPM_SetupOutputCompare(base, kTPM_Chnl_0, kTPM_NoOutputSignal, ticks);
109 	TPM_EnableInterrupts(base, kTPM_Chnl0InterruptEnable);
110 
111 	return 0;
112 }
113 
mcux_tpm_cancel_alarm(const struct device * dev,uint8_t chan_id)114 static int mcux_tpm_cancel_alarm(const struct device *dev, uint8_t chan_id)
115 {
116 	TPM_Type *base = get_base(dev);
117 	struct mcux_tpm_data *data = dev->data;
118 
119 	if (chan_id != kTPM_Chnl_0) {
120 		LOG_ERR("Invalid channel id");
121 		return -EINVAL;
122 	}
123 
124 	TPM_DisableInterrupts(base, kTPM_Chnl0InterruptEnable);
125 	data->alarm_callback = NULL;
126 
127 	return 0;
128 }
129 
mcux_tpm_isr(const struct device * dev)130 void mcux_tpm_isr(const struct device *dev)
131 {
132 	TPM_Type *base = get_base(dev);
133 	struct mcux_tpm_data *data = dev->data;
134 	uint32_t current = TPM_GetCurrentTimerCount(base);
135 	uint32_t status;
136 
137 	status =  TPM_GetStatusFlags(base) & (kTPM_Chnl0Flag | kTPM_TimeOverflowFlag);
138 	TPM_ClearStatusFlags(base, status);
139 	barrier_dsync_fence_full();
140 
141 	if ((status & kTPM_Chnl0Flag) && data->alarm_callback) {
142 		TPM_DisableInterrupts(base,
143 				      kTPM_Chnl0InterruptEnable);
144 		counter_alarm_callback_t alarm_cb = data->alarm_callback;
145 
146 		data->alarm_callback = NULL;
147 		alarm_cb(dev, 0, current, data->alarm_user_data);
148 	}
149 
150 	if ((status & kTPM_TimeOverflowFlag) && data->top_callback) {
151 		data->top_callback(dev, data->top_user_data);
152 	}
153 }
154 
mcux_tpm_get_pending_int(const struct device * dev)155 static uint32_t mcux_tpm_get_pending_int(const struct device *dev)
156 {
157 	TPM_Type *base = get_base(dev);
158 
159 	return (TPM_GetStatusFlags(base) & kTPM_Chnl0Flag);
160 }
161 
mcux_tpm_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)162 static int mcux_tpm_set_top_value(const struct device *dev,
163 				  const struct counter_top_cfg *cfg)
164 {
165 	const struct mcux_tpm_config *config = dev->config;
166 	TPM_Type *base = get_base(dev);
167 	struct mcux_tpm_data *data = dev->data;
168 
169 	if (data->alarm_callback) {
170 		return -EBUSY;
171 	}
172 
173 	/* Check if timer already enabled. */
174 #if defined(FSL_FEATURE_TPM_HAS_SC_CLKS) && FSL_FEATURE_TPM_HAS_SC_CLKS
175 	if (base->SC & TPM_SC_CLKS_MASK) {
176 #else
177 	if (base->SC & TPM_SC_CMOD_MASK) {
178 #endif
179 		/* Timer already enabled, check flags before resetting */
180 		if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
181 			return -ENOTSUP;
182 		}
183 
184 		TPM_StopTimer(base);
185 		base->CNT = 0;
186 		TPM_SetTimerPeriod(base, cfg->ticks);
187 		TPM_StartTimer(base, config->tpm_clock_source);
188 	} else {
189 		base->CNT = 0;
190 		TPM_SetTimerPeriod(base, cfg->ticks);
191 	}
192 
193 	data->top_callback = cfg->callback;
194 	data->top_user_data = cfg->user_data;
195 
196 	TPM_EnableInterrupts(base, kTPM_TimeOverflowInterruptEnable);
197 
198 	return 0;
199 }
200 
201 static uint32_t mcux_tpm_get_top_value(const struct device *dev)
202 {
203 	TPM_Type *base = get_base(dev);
204 
205 	return base->MOD;
206 }
207 
208 static uint32_t mcux_tpm_get_freq(const struct device *dev)
209 {
210 	struct mcux_tpm_data *data = dev->data;
211 
212 	return data->freq;
213 }
214 
215 static int mcux_tpm_init(const struct device *dev)
216 {
217 	const struct mcux_tpm_config *config = dev->config;
218 	struct mcux_tpm_data *data = dev->data;
219 	tpm_config_t tpmConfig;
220 	uint32_t input_clock_freq;
221 	TPM_Type *base;
222 
223 	DEVICE_MMIO_NAMED_MAP(dev, tpm_mmio, K_MEM_CACHE_NONE | K_MEM_DIRECT_MAP);
224 
225 	if (!device_is_ready(config->clock_dev)) {
226 		LOG_ERR("clock control device not ready");
227 		return -ENODEV;
228 	}
229 
230 	if (clock_control_on(config->clock_dev, config->clock_subsys)) {
231 		LOG_ERR("Could not turn on clock");
232 		return -EINVAL;
233 	}
234 
235 	if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
236 				   &input_clock_freq)) {
237 		LOG_ERR("Could not get clock frequency");
238 		return -EINVAL;
239 	}
240 
241 	data->freq = input_clock_freq / (1U << config->prescale);
242 
243 	TPM_GetDefaultConfig(&tpmConfig);
244 	tpmConfig.prescale = config->prescale;
245 	base = get_base(dev);
246 	TPM_Init(base, &tpmConfig);
247 
248 	/* Set the modulo to max value. */
249 	base->MOD = TPM_MAX_COUNTER_VALUE(base);
250 
251 	return 0;
252 }
253 
254 static DEVICE_API(counter, mcux_tpm_driver_api) = {
255 	.start = mcux_tpm_start,
256 	.stop = mcux_tpm_stop,
257 	.get_value = mcux_tpm_get_value,
258 	.set_alarm = mcux_tpm_set_alarm,
259 	.cancel_alarm = mcux_tpm_cancel_alarm,
260 	.set_top_value = mcux_tpm_set_top_value,
261 	.get_pending_int = mcux_tpm_get_pending_int,
262 	.get_top_value = mcux_tpm_get_top_value,
263 	.get_freq = mcux_tpm_get_freq,
264 };
265 
266 #define TO_TPM_PRESCALE_DIVIDE(val) _DO_CONCAT(kTPM_Prescale_Divide_, val)
267 
268 #define TPM_DEVICE_INIT_MCUX(n)							\
269 	static struct mcux_tpm_data mcux_tpm_data_ ## n;			\
270 										\
271 	static const struct mcux_tpm_config mcux_tpm_config_ ## n = {		\
272 		DEVICE_MMIO_NAMED_ROM_INIT(tpm_mmio, DT_DRV_INST(n)),		\
273 		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)),		\
274 		.clock_subsys =							\
275 			(clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name),	\
276 		.tpm_clock_source = kTPM_SystemClock,				\
277 		.prescale = TO_TPM_PRESCALE_DIVIDE(DT_INST_PROP(n, prescaler)),	\
278 		.info = {							\
279 			.max_top_value = TPM_MAX_COUNTER_VALUE(TPM(n)),		\
280 			.freq = 0,						\
281 			.channels = 1,						\
282 			.flags = COUNTER_CONFIG_INFO_COUNT_UP,			\
283 		},								\
284 	};									\
285 										\
286 	static int mcux_tpm_## n ##_init(const struct device *dev);		\
287 	DEVICE_DT_INST_DEFINE(n,						\
288 			mcux_tpm_## n ##_init,					\
289 			NULL,							\
290 			&mcux_tpm_data_ ## n,					\
291 			&mcux_tpm_config_ ## n,					\
292 			POST_KERNEL,						\
293 			CONFIG_COUNTER_INIT_PRIORITY,				\
294 			&mcux_tpm_driver_api);					\
295 										\
296 	static int mcux_tpm_## n ##_init(const struct device *dev)		\
297 	{									\
298 		IRQ_CONNECT(DT_INST_IRQN(n),					\
299 			DT_INST_IRQ(n, priority),				\
300 			mcux_tpm_isr, DEVICE_DT_INST_GET(n), 0);		\
301 		irq_enable(DT_INST_IRQN(n));					\
302 		return mcux_tpm_init(dev);					\
303 	}									\
304 
305 DT_INST_FOREACH_STATUS_OKAY(TPM_DEVICE_INIT_MCUX)
306