1 /*
2  * Copyright (c) 2021 Basalte bv
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_imx_snvs_rtc
8 #include <zephyr/logging/log.h>
9 
10 LOG_MODULE_REGISTER(mcux_snvs, CONFIG_COUNTER_LOG_LEVEL);
11 
12 #if CONFIG_COUNTER_MCUX_SNVS_SRTC
13 #define MCUX_SNVS_SRTC
14 #define MCUX_SNVS_NUM_CHANNELS 2
15 #else
16 #define MCUX_SNVS_NUM_CHANNELS 1
17 #endif
18 
19 #include <zephyr/drivers/counter.h>
20 #include <zephyr/irq.h>
21 #include <fsl_snvs_hp.h>
22 
23 #ifdef MCUX_SNVS_SRTC
24 #include <fsl_snvs_lp.h>
25 #endif
26 
27 struct mcux_snvs_config {
28 	/* info must be first element */
29 	struct counter_config_info info;
30 	SNVS_Type *base;
31 	void (*irq_config_func)(const struct device *dev);
32 };
33 
34 struct mcux_snvs_data {
35 	counter_alarm_callback_t alarm_hp_rtc_callback;
36 	void *alarm_hp_rtc_user_data;
37 #ifdef MCUX_SNVS_SRTC
38 	counter_alarm_callback_t alarm_lp_srtc_callback;
39 	void *alarm_lp_srtc_user_data;
40 #endif
41 };
42 
mcux_snvs_start(const struct device * dev)43 static int mcux_snvs_start(const struct device *dev)
44 {
45 	ARG_UNUSED(dev);
46 
47 	return -EALREADY;
48 }
49 
mcux_snvs_stop(const struct device * dev)50 static int mcux_snvs_stop(const struct device *dev)
51 {
52 	ARG_UNUSED(dev);
53 
54 	return -ENOTSUP;
55 }
56 
mcux_snvs_get_value(const struct device * dev,uint32_t * ticks)57 static int mcux_snvs_get_value(const struct device *dev, uint32_t *ticks)
58 {
59 	const struct mcux_snvs_config *config = dev->config;
60 	uint32_t tmp = 0;
61 
62 	do {
63 		*ticks = tmp;
64 		tmp = (config->base->HPRTCMR << 17U);
65 		tmp |= (config->base->HPRTCLR >> 15U);
66 	} while (*ticks != tmp);
67 
68 	return 0;
69 }
70 
mcux_snvs_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)71 static int mcux_snvs_set_alarm(const struct device *dev,
72 			      uint8_t chan_id,
73 			      const struct counter_alarm_cfg *alarm_cfg)
74 {
75 	const struct mcux_snvs_config *config = dev->config;
76 	struct mcux_snvs_data *data = dev->data;
77 
78 	uint32_t current, ticks;
79 
80 	mcux_snvs_get_value(dev, &current);
81 	ticks = alarm_cfg->ticks;
82 
83 	if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) {
84 		ticks += current;
85 	}
86 
87 	if (ticks < current) {
88 		LOG_ERR("Invalid alarm ticks");
89 		return -EINVAL;
90 	}
91 
92 	if (chan_id == 0) {
93 		if (data->alarm_hp_rtc_callback) {
94 			return -EBUSY;
95 		}
96 		data->alarm_hp_rtc_callback = alarm_cfg->callback;
97 		data->alarm_hp_rtc_user_data = alarm_cfg->user_data;
98 
99 		/* disable RTC alarm interrupt */
100 		config->base->HPCR &= ~SNVS_HPCR_HPTA_EN_MASK;
101 		while ((config->base->HPCR & SNVS_HPCR_HPTA_EN_MASK) != 0U) {
102 		}
103 
104 		/* Set alarm in seconds*/
105 		config->base->HPTAMR = (uint32_t)(ticks >> 17U);
106 		config->base->HPTALR = (uint32_t)(ticks << 15U);
107 
108 		/* enable RTC alarm interrupt */
109 		config->base->HPCR |= SNVS_HPCR_HPTA_EN_MASK;
110 #ifdef MCUX_SNVS_SRTC
111 	} else if (chan_id == 1) {
112 		if (data->alarm_lp_srtc_callback) {
113 			return -EBUSY;
114 		}
115 		data->alarm_lp_srtc_callback = alarm_cfg->callback;
116 		data->alarm_lp_srtc_user_data = alarm_cfg->user_data;
117 
118 		/* disable SRTC alarm interrupt */
119 		config->base->LPCR &= ~SNVS_LPCR_LPTA_EN_MASK;
120 		while ((config->base->LPCR & SNVS_LPCR_LPTA_EN_MASK) != 0U) {
121 		}
122 
123 		/* Set alarm in seconds*/
124 		config->base->LPTAR = ticks;
125 
126 		/* enable SRTC alarm interrupt */
127 		config->base->LPCR |= SNVS_LPCR_LPTA_EN_MASK;
128 #endif
129 	} else {
130 		LOG_ERR("Invalid channel id");
131 		return -EINVAL;
132 	}
133 
134 	return 0;
135 }
136 
mcux_snvs_cancel_alarm(const struct device * dev,uint8_t chan_id)137 static int mcux_snvs_cancel_alarm(const struct device *dev,
138 				 uint8_t chan_id)
139 {
140 	const struct mcux_snvs_config *config = dev->config;
141 	struct mcux_snvs_data *data = dev->data;
142 
143 	if (chan_id == 0) {
144 		/* disable RTC alarm interrupt */
145 		config->base->HPCR &= ~SNVS_HPCR_HPTA_EN_MASK;
146 		while ((config->base->HPCR & SNVS_HPCR_HPTA_EN_MASK) != 0U) {
147 		}
148 
149 		/* clear callback */
150 		data->alarm_hp_rtc_callback = NULL;
151 
152 #ifdef MCUX_SNVS_SRTC
153 	} else if (chan_id == 1) {
154 		/* disable SRTC alarm interrupt */
155 		config->base->LPCR &= ~SNVS_LPCR_LPTA_EN_MASK;
156 		while ((config->base->LPCR & SNVS_LPCR_LPTA_EN_MASK) != 0U) {
157 		}
158 
159 		/* clear callback */
160 		data->alarm_lp_srtc_callback = NULL;
161 #endif
162 	} else {
163 		LOG_ERR("Invalid channel id");
164 		return -EINVAL;
165 	}
166 
167 	return 0;
168 }
169 
mcux_snvs_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)170 static int mcux_snvs_set_top_value(const struct device *dev,
171 				  const struct counter_top_cfg *cfg)
172 {
173 	ARG_UNUSED(dev);
174 	ARG_UNUSED(cfg);
175 
176 	return -ENOTSUP;
177 }
178 
mcux_snvs_get_pending_int(const struct device * dev)179 static uint32_t mcux_snvs_get_pending_int(const struct device *dev)
180 {
181 	const struct mcux_snvs_config *config = dev->config;
182 	uint32_t flags;
183 
184 	flags = SNVS_HP_RTC_GetStatusFlags(config->base) & kSNVS_RTC_AlarmInterruptFlag;
185 
186 #ifdef MCUX_SNVS_SRTC
187 	flags |= SNVS_LP_SRTC_GetStatusFlags(config->base) & kSNVS_SRTC_AlarmInterruptFlag;
188 #endif
189 
190 	return flags;
191 }
192 
mcux_snvs_get_top_value(const struct device * dev)193 static uint32_t mcux_snvs_get_top_value(const struct device *dev)
194 {
195 	ARG_UNUSED(dev);
196 
197 	return UINT32_MAX;
198 }
199 
mcux_snvs_isr(const struct device * dev)200 void mcux_snvs_isr(const struct device *dev)
201 {
202 	const struct mcux_snvs_config *config = dev->config;
203 	struct mcux_snvs_data *data = dev->data;
204 
205 	uint32_t current;
206 
207 	mcux_snvs_get_value(dev, &current);
208 
209 	if (SNVS_HP_RTC_GetStatusFlags(config->base) & kSNVS_RTC_AlarmInterruptFlag) {
210 		/* Clear alarm flag */
211 		SNVS_HP_RTC_ClearStatusFlags(config->base, kSNVS_RTC_AlarmInterruptFlag);
212 
213 		if (data->alarm_hp_rtc_callback) {
214 			data->alarm_hp_rtc_callback(dev, 0, current, data->alarm_hp_rtc_user_data);
215 
216 			mcux_snvs_cancel_alarm(dev, 0);
217 		}
218 	}
219 
220 #ifdef MCUX_SNVS_SRTC
221 	if (SNVS_LP_SRTC_GetStatusFlags(config->base) & kSNVS_SRTC_AlarmInterruptFlag) {
222 		/* Clear alarm flag */
223 		SNVS_LP_SRTC_ClearStatusFlags(config->base, kSNVS_SRTC_AlarmInterruptFlag);
224 
225 		if (data->alarm_lp_srtc_callback) {
226 			data->alarm_lp_srtc_callback(dev, 1, current,
227 						     data->alarm_lp_srtc_user_data);
228 			mcux_snvs_cancel_alarm(dev, 1);
229 		}
230 	}
231 #endif
232 }
233 
mcux_snvs_rtc_set(const struct device * dev,uint32_t ticks)234 int mcux_snvs_rtc_set(const struct device *dev, uint32_t ticks)
235 {
236 	const struct mcux_snvs_config *config = dev->config;
237 
238 #ifdef MCUX_SNVS_SRTC
239 	SNVS_LP_SRTC_StopTimer(config->base);
240 
241 	config->base->LPSRTCMR = (uint32_t)(ticks >> 17U);
242 	config->base->LPSRTCLR = (uint32_t)(ticks << 15U);
243 
244 	SNVS_LP_SRTC_StartTimer(config->base);
245 	/* Sync to our high power RTC */
246 	SNVS_HP_RTC_TimeSynchronize(config->base);
247 #else
248 	SNVS_HP_RTC_StopTimer(config->base);
249 
250 	config->base->HPRTCMR = (uint32_t)(ticks >> 17U);
251 	config->base->HPRTCLR = (uint32_t)(ticks << 15U);
252 
253 	SNVS_HP_RTC_StartTimer(config->base);
254 #endif
255 
256 	return 0;
257 }
258 
mcux_snvs_init(const struct device * dev)259 static int mcux_snvs_init(const struct device *dev)
260 {
261 	const struct mcux_snvs_config *config = dev->config;
262 
263 	snvs_hp_rtc_config_t hp_rtc_config;
264 
265 #ifdef MCUX_SNVS_SRTC
266 	snvs_lp_srtc_config_t lp_srtc_config;
267 #endif
268 
269 	SNVS_HP_RTC_GetDefaultConfig(&hp_rtc_config);
270 	SNVS_HP_RTC_Init(config->base, &hp_rtc_config);
271 
272 #ifdef MCUX_SNVS_SRTC
273 	/* Reset power glitch detector */
274 	SNVS_LP_Init(config->base);
275 	/* Init SRTC to default config */
276 	SNVS_LP_SRTC_GetDefaultConfig(&lp_srtc_config);
277 	SNVS_LP_SRTC_Init(config->base, &lp_srtc_config);
278 
279 #if CONFIG_COUNTER_MCUX_SNVS_SRTC_WAKE
280 	config->base->LPCR |= SNVS_LPCR_LPWUI_EN_MASK;
281 #endif
282 
283 	/* RTC should always run */
284 	SNVS_LP_SRTC_StartTimer(config->base);
285 	SNVS_HP_RTC_TimeSynchronize(config->base);
286 #endif
287 
288 	/* RTC should always run */
289 	SNVS_HP_RTC_StartTimer(config->base);
290 
291 	config->irq_config_func(dev);
292 
293 	return 0;
294 }
295 
296 static DEVICE_API(counter, mcux_snvs_driver_api) = {
297 	.start = mcux_snvs_start,
298 	.stop = mcux_snvs_stop,
299 	.get_value = mcux_snvs_get_value,
300 	.set_alarm = mcux_snvs_set_alarm,
301 	.cancel_alarm = mcux_snvs_cancel_alarm,
302 	.set_top_value = mcux_snvs_set_top_value,
303 	.get_pending_int = mcux_snvs_get_pending_int,
304 	.get_top_value = mcux_snvs_get_top_value,
305 };
306 
307 /*
308  * This driver is single-instance. If the devicetree contains multiple
309  * instances, this will fail and the driver needs to be revisited.
310  */
311 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,
312 	     "unsupported snvs instance");
313 
314 #if DT_NODE_HAS_STATUS_OKAY(DT_DRV_INST(0))
315 static struct mcux_snvs_data mcux_snvs_data_0;
316 
317 static void mcux_snvs_irq_config_0(const struct device *dev);
318 
319 static struct mcux_snvs_config mcux_snvs_config_0 = {
320 	.info = {
321 		.max_top_value = 0,
322 		.freq = 1,
323 		.channels = MCUX_SNVS_NUM_CHANNELS,
324 		.flags = COUNTER_CONFIG_INFO_COUNT_UP,
325 	},
326 	.base = (SNVS_Type *)DT_REG_ADDR(DT_INST_PARENT(0)),
327 	.irq_config_func = mcux_snvs_irq_config_0,
328 };
329 
330 DEVICE_DT_INST_DEFINE(0, &mcux_snvs_init, NULL,
331 		      &mcux_snvs_data_0,
332 		      &mcux_snvs_config_0,
333 		      POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY,
334 		      &mcux_snvs_driver_api);
335 
mcux_snvs_irq_config_0(const struct device * dev)336 static void mcux_snvs_irq_config_0(const struct device *dev)
337 {
338 	IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
339 		    mcux_snvs_isr, DEVICE_DT_INST_GET(0), 0);
340 	irq_enable(DT_INST_IRQN(0));
341 }
342 #endif  /* DT_NODE_HAS_STATUS_OKAY(DT_DRV_INST(0)) */
343