1 /*
2  * Copyright (c) 2020 Vestas Wind Systems A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/devicetree.h>
8 #include <zephyr/sys/util.h>
9 #if DT_HAS_COMPAT_STATUS_OKAY(nxp_kinetis_lptmr)
10 #define DT_DRV_COMPAT nxp_kinetis_lptmr
11 #else
12 #define DT_DRV_COMPAT nxp_lptmr
13 #endif
14 
15 #include <zephyr/drivers/counter.h>
16 #include <zephyr/irq.h>
17 #include <fsl_lptmr.h>
18 
19 struct mcux_lptmr_config {
20 	struct counter_config_info info;
21 	LPTMR_Type *base;
22 	lptmr_prescaler_clock_select_t clk_source;
23 	lptmr_prescaler_glitch_value_t prescaler_glitch;
24 	bool bypass_prescaler_glitch;
25 	lptmr_timer_mode_t mode;
26 	lptmr_pin_select_t pin;
27 	lptmr_pin_polarity_t polarity;
28 	void (*irq_config_func)(const struct device *dev);
29 };
30 
31 struct mcux_lptmr_data {
32 	counter_top_callback_t top_callback;
33 	void *top_user_data;
34 };
35 
mcux_lptmr_start(const struct device * dev)36 static int mcux_lptmr_start(const struct device *dev)
37 {
38 	const struct mcux_lptmr_config *config = dev->config;
39 
40 	LPTMR_EnableInterrupts(config->base,
41 			       kLPTMR_TimerInterruptEnable);
42 	LPTMR_StartTimer(config->base);
43 
44 	return 0;
45 }
46 
mcux_lptmr_stop(const struct device * dev)47 static int mcux_lptmr_stop(const struct device *dev)
48 {
49 	const struct mcux_lptmr_config *config = dev->config;
50 
51 	LPTMR_DisableInterrupts(config->base,
52 				kLPTMR_TimerInterruptEnable);
53 	LPTMR_StopTimer(config->base);
54 
55 	return 0;
56 }
57 
mcux_lptmr_get_value(const struct device * dev,uint32_t * ticks)58 static int mcux_lptmr_get_value(const struct device *dev, uint32_t *ticks)
59 {
60 	const struct mcux_lptmr_config *config = dev->config;
61 
62 	*ticks = LPTMR_GetCurrentTimerCount(config->base);
63 
64 	return 0;
65 }
66 
mcux_lptmr_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)67 static int mcux_lptmr_set_top_value(const struct device *dev,
68 				    const struct counter_top_cfg *cfg)
69 {
70 	const struct mcux_lptmr_config *config = dev->config;
71 	struct mcux_lptmr_data *data = dev->data;
72 
73 	if (cfg->ticks == 0) {
74 		return -EINVAL;
75 	}
76 
77 	data->top_callback = cfg->callback;
78 	data->top_user_data = cfg->user_data;
79 
80 	if (config->base->CSR & LPTMR_CSR_TEN_MASK) {
81 		/* Timer already enabled, check flags before resetting */
82 		if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
83 			return -ENOTSUP;
84 		}
85 		LPTMR_StopTimer(config->base);
86 		LPTMR_SetTimerPeriod(config->base, cfg->ticks);
87 		LPTMR_StartTimer(config->base);
88 	} else {
89 		LPTMR_SetTimerPeriod(config->base, cfg->ticks);
90 	}
91 
92 	return 0;
93 }
94 
mcux_lptmr_get_pending_int(const struct device * dev)95 static uint32_t mcux_lptmr_get_pending_int(const struct device *dev)
96 {
97 	const struct mcux_lptmr_config *config = dev->config;
98 	uint32_t mask = LPTMR_CSR_TCF_MASK | LPTMR_CSR_TIE_MASK;
99 	uint32_t flags;
100 
101 	flags = LPTMR_GetStatusFlags(config->base);
102 
103 	return ((flags & mask) == mask);
104 }
105 
mcux_lptmr_get_top_value(const struct device * dev)106 static uint32_t mcux_lptmr_get_top_value(const struct device *dev)
107 {
108 	const struct mcux_lptmr_config *config = dev->config;
109 
110 	return (config->base->CMR & LPTMR_CMR_COMPARE_MASK) + 1U;
111 }
112 
mcux_lptmr_isr(const struct device * dev)113 static void mcux_lptmr_isr(const struct device *dev)
114 {
115 	const struct mcux_lptmr_config *config = dev->config;
116 	struct mcux_lptmr_data *data = dev->data;
117 	uint32_t flags;
118 
119 	flags = LPTMR_GetStatusFlags(config->base);
120 	LPTMR_ClearStatusFlags(config->base, flags);
121 
122 	if (data->top_callback) {
123 		data->top_callback(dev, data->top_user_data);
124 	}
125 }
126 
mcux_lptmr_init(const struct device * dev)127 static int mcux_lptmr_init(const struct device *dev)
128 {
129 	const struct mcux_lptmr_config *config = dev->config;
130 	lptmr_config_t lptmr_config;
131 
132 	LPTMR_GetDefaultConfig(&lptmr_config);
133 	lptmr_config.timerMode = config->mode;
134 	lptmr_config.enableFreeRunning = false;
135 	lptmr_config.prescalerClockSource = config->clk_source;
136 	lptmr_config.bypassPrescaler = config->bypass_prescaler_glitch;
137 	lptmr_config.value = config->prescaler_glitch;
138 
139 	if (config->mode == kLPTMR_TimerModePulseCounter) {
140 		lptmr_config.pinSelect = config->pin;
141 		lptmr_config.pinPolarity = config->polarity;
142 	}
143 
144 	LPTMR_Init(config->base, &lptmr_config);
145 
146 	LPTMR_SetTimerPeriod(config->base, config->info.max_top_value);
147 
148 	config->irq_config_func(dev);
149 
150 	return 0;
151 }
152 
153 static DEVICE_API(counter, mcux_lptmr_driver_api) = {
154 	.start = mcux_lptmr_start,
155 	.stop = mcux_lptmr_stop,
156 	.get_value = mcux_lptmr_get_value,
157 	.set_top_value = mcux_lptmr_set_top_value,
158 	.get_pending_int = mcux_lptmr_get_pending_int,
159 	.get_top_value = mcux_lptmr_get_top_value,
160 };
161 
162 #define COUNTER_MCUX_LPTMR_DEVICE_INIT(n)					\
163 	static void mcux_lptmr_irq_config_##n(const struct device *dev)		\
164 	{									\
165 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority),		\
166 			mcux_lptmr_isr, DEVICE_DT_INST_GET(n), 0);		\
167 		irq_enable(DT_INST_IRQN(n));					\
168 	}									\
169 										\
170 	static struct mcux_lptmr_data mcux_lptmr_data_##n;			\
171 	static void mcux_lptmr_irq_config_##n(const struct device *dev);	\
172 										\
173 	BUILD_ASSERT(!(DT_INST_PROP(n, timer_mode_sel) == 1 &&			\
174 		DT_INST_PROP(n, prescale_glitch_filter) == 16),			\
175 		"Pulse mode cannot have a glitch value of 16");			\
176 										\
177 	BUILD_ASSERT(DT_INST_PROP(n, resolution) <= 32 &&			\
178 		DT_INST_PROP(n, resolution) > 0,				\
179 		"LPTMR resolution property should be a width between 0 and 32");\
180 										\
181 	static struct mcux_lptmr_config mcux_lptmr_config_##n = {		\
182 		.info = {							\
183 			.max_top_value =					\
184 				GENMASK(DT_INST_PROP(n, resolution) - 1, 0),	\
185 			.freq = DT_INST_PROP(n, clock_frequency) /		\
186 				DT_INST_PROP(n, prescaler),			\
187 			.flags = COUNTER_CONFIG_INFO_COUNT_UP,			\
188 			.channels = 0,						\
189 		},								\
190 		.base = (LPTMR_Type *)DT_INST_REG_ADDR(n),			\
191 		.clk_source = DT_INST_PROP(n, clk_source),			\
192 		.bypass_prescaler_glitch =					\
193 			1 - DT_INST_PROP(n, timer_mode_sel),			\
194 		.mode = DT_INST_PROP(n, timer_mode_sel),			\
195 		.pin = DT_INST_PROP_OR(n, input_pin, 0),			\
196 		.polarity = DT_INST_PROP(n, active_low),			\
197 		.prescaler_glitch = DT_INST_PROP(n, prescale_glitch_filter) +	\
198 			DT_INST_PROP(n, timer_mode_sel) - 1,			\
199 		.irq_config_func = mcux_lptmr_irq_config_##n,			\
200 	};									\
201 										\
202 	DEVICE_DT_INST_DEFINE(n, &mcux_lptmr_init, NULL,			\
203 		&mcux_lptmr_data_##n,						\
204 		&mcux_lptmr_config_##n,						\
205 		POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY,			\
206 		&mcux_lptmr_driver_api);
207 
208 
209 DT_INST_FOREACH_STATUS_OKAY(COUNTER_MCUX_LPTMR_DEVICE_INIT)
210