/
rtc_mcux.c
207 lines (167 loc) · 5.1 KB
/
rtc_mcux.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
* Copyright (c) 2018 blik GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_LEVEL CONFIG_RTC_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(rtc_mcux);
#include <errno.h>
#include <device.h>
#include <init.h>
#include <kernel.h>
#include <rtc.h>
#include <power/power.h>
#include <soc.h>
#include <misc/util.h>
#include <fsl_rtc.h>
struct mcux_rtc_config {
RTC_Type *base;
void (*irq_config_func)(struct device *dev);
};
struct mcux_rtc_data {
struct k_sem sync;
rtc_config_t config;
/* callback information */
void (*callback)(void *data);
void *callback_data;
};
static void mcux_rtc_enable(struct device *dev)
{
const struct mcux_rtc_config *config = dev->config->config_info;
RTC_StartTimer(config->base);
RTC_EnableInterrupts(config->base,
kRTC_AlarmInterruptEnable |
kRTC_TimeOverflowInterruptEnable |
kRTC_TimeInvalidInterruptEnable);
}
static void mcux_rtc_disable(struct device *dev)
{
const struct mcux_rtc_config *config = dev->config->config_info;
RTC_DisableInterrupts(config->base,
kRTC_AlarmInterruptEnable |
kRTC_TimeOverflowInterruptEnable |
kRTC_TimeInvalidInterruptEnable);
RTC_StopTimer(config->base);
/* clear out any set alarms */
config->base->TAR = 0;
}
static int mcux_rtc_set_alarm(struct device *dev, const u32_t alarm_val)
{
const struct mcux_rtc_config *config = dev->config->config_info;
if (alarm_val < config->base->TSR) {
LOG_ERR("alarm cannot be earlier than current time");
return -EINVAL;
}
config->base->TAR = alarm_val;
return 0;
}
static int mcux_rtc_set_config(struct device *dev, struct rtc_config *cfg)
{
const struct mcux_rtc_config *config = dev->config->config_info;
struct mcux_rtc_data *data = dev->driver_data;
int ret = 0;
/* only allow one modifier at a time */
k_sem_take(&data->sync, K_FOREVER);
if (cfg->alarm_enable) {
/* set up callback information */
data->callback = (void *) cfg->cb_fn;
data->callback_data = dev;
RTC_StopTimer(config->base);
config->base->TSR = cfg->init_val;
RTC_StartTimer(config->base);
ret = mcux_rtc_set_alarm(dev, cfg->alarm_val);
} else {
/* clear any existing alarm setting */
config->base->TAR = 0;
/* clear callbacks */
data->callback = NULL;
data->callback_data = NULL;
}
k_sem_give(&data->sync);
return ret;
}
static u32_t mcux_rtc_read(struct device *dev)
{
const struct mcux_rtc_config *config = dev->config->config_info;
u32_t val = config->base->TSR;
/*
* Read TSR seconds twice in case it glitches during an update.
* This can happen when a read occurs at the time the register is
* incrementing.
*/
if (config->base->TSR == val) {
return val;
}
val = config->base->TSR;
return val;
}
static u32_t mcux_rtc_get_pending_int(struct device *dev)
{
const struct mcux_rtc_config *config = dev->config->config_info;
return RTC_GetStatusFlags(config->base) & RTC_SR_TAF_MASK;
}
static const struct rtc_driver_api mcux_rtc_driver_api = {
.enable = mcux_rtc_enable,
.disable = mcux_rtc_disable,
.read = mcux_rtc_read,
.set_config = mcux_rtc_set_config,
.set_alarm = mcux_rtc_set_alarm,
.get_pending_int = mcux_rtc_get_pending_int,
};
static void mcux_rtc_isr(void *arg)
{
struct device *dev = arg;
const struct mcux_rtc_config *config = dev->config->config_info;
struct mcux_rtc_data *data = dev->driver_data;
/* perform any registered callbacks */
if (data->callback) {
data->callback(data->callback_data);
}
/*
* Clear any conditions to ack the IRQ
*
* callback may have already reset the alarm flag if a new
* alarm value was programmed to the TAR
*/
RTC_StopTimer(config->base);
if (RTC_GetStatusFlags(config->base) & RTC_SR_TAF_MASK) {
RTC_ClearStatusFlags(config->base, kRTC_AlarmFlag);
} else if (RTC_GetStatusFlags(config->base) & RTC_SR_TIF_MASK) {
RTC_ClearStatusFlags(config->base, kRTC_TimeInvalidFlag);
} else if (RTC_GetStatusFlags(config->base) & RTC_SR_TOF_MASK) {
RTC_ClearStatusFlags(config->base, kRTC_TimeOverflowFlag);
}
RTC_StartTimer(config->base);
}
static int mcux_rtc_init(struct device *dev)
{
const struct mcux_rtc_config *config = dev->config->config_info;
struct mcux_rtc_data *data = dev->driver_data;
k_sem_init(&data->sync, 1, UINT_MAX);
/* Create default configuration and store it off */
RTC_GetDefaultConfig(&data->config);
RTC_Init(config->base, &data->config);
/* Enable 32kHz oscillator and wait for 1ms to settle */
config->base->CR |= 0x100;
k_busy_wait(USEC_PER_MSEC);
/* connect and enable the IRQ line */
config->irq_config_func(dev);
return 0;
}
static struct mcux_rtc_data rtc_mcux_data_0;
static void rtc_mcux_irq_config_0(struct device *dev);
static struct mcux_rtc_config rtc_mcux_config_0 = {
.base = (RTC_Type *)DT_RTC_MCUX_0_BASE_ADDRESS,
.irq_config_func = rtc_mcux_irq_config_0,
};
DEVICE_DEFINE(rtc, DT_RTC_MCUX_0_NAME,
&mcux_rtc_init, NULL, &rtc_mcux_data_0, &rtc_mcux_config_0,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&mcux_rtc_driver_api);
static void rtc_mcux_irq_config_0(struct device *dev)
{
IRQ_CONNECT(DT_RTC_MCUX_0_IRQ, DT_RTC_MCUX_0_IRQ_PRI,
mcux_rtc_isr, DEVICE_GET(rtc), 0);
irq_enable(DT_RTC_MCUX_0_IRQ);
}