Skip to content

Commit 4d161a3

Browse files
pdgendtMaureenHelm
authored andcommitted
drivers: counter: NXP SNVS rtc: Add support for NXP imx SNVS RTC
Adds a driver using the SNVS high power and optionally low power RTC instances. A device specific function `mcux_snvs_rtc_set` is provided to update the current counter value. Signed-off-by: Pieter De Gendt <[email protected]>
1 parent f700559 commit 4d161a3

File tree

10 files changed

+445
-1
lines changed

10 files changed

+445
-1
lines changed

drivers/counter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_SAM_TC counter_sam_tc.c
1616
zephyr_library_sources_ifdef(CONFIG_COUNTER_SAM0_TC32 counter_sam0_tc32.c)
1717
zephyr_library_sources_ifdef(CONFIG_COUNTER_CMOS counter_cmos.c)
1818
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_GPT counter_mcux_gpt.c)
19+
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_SNVS counter_mcux_snvs.c)
1920
zephyr_library_sources_ifdef(CONFIG_COUNTER_XEC counter_mchp_xec.c)
2021
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPTMR counter_mcux_lptmr.c)
2122
zephyr_library_sources_ifdef(CONFIG_COUNTER_MAXIM_DS3231 maxim_ds3231.c)

drivers/counter/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ source "drivers/counter/Kconfig.cmos"
4444

4545
source "drivers/counter/Kconfig.mcux_gpt"
4646

47+
source "drivers/counter/Kconfig.mcux_snvs"
48+
4749
source "drivers/counter/Kconfig.xec"
4850

4951
source "drivers/counter/Kconfig.mcux_lptmr"

drivers/counter/Kconfig.mcux_snvs

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

0 commit comments

Comments
 (0)