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