1 /*
2 * Copyright (c) 2024 Analog Devices, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT adi_max32_rtc_counter
8
9 #include <zephyr/drivers/counter.h>
10 #include <zephyr/drivers/clock_control/adi_max32_clock_control.h>
11 #include <zephyr/drivers/pinctrl.h>
12 #include <zephyr/irq.h>
13
14 #include <rtc.h>
15 #include <wrap_max32_lp.h>
16
17 /* Resoultion is 1sec for time of day alarm*/
18 #define MAX32_RTC_COUNTER_FREQ 1
19
20 /* 20bits used for time of day alarm */
21 #define MAX32_RTC_COUNTER_MAX_VALUE ((1 << 21) - 1)
22
23 #define MAX32_RTC_COUNTER_INT_FL MXC_RTC_INT_FL_LONG
24 #define MAX32_RTC_COUNTER_INT_EN MXC_RTC_INT_EN_LONG
25
26 struct max32_rtc_data {
27 counter_alarm_callback_t alarm_callback;
28 counter_top_callback_t top_callback;
29 void *alarm_user_data;
30 void *top_user_data;
31 };
32
33 struct max32_rtc_config {
34 struct counter_config_info info;
35 mxc_rtc_regs_t *regs;
36 void (*irq_func)(void);
37 };
38
api_start(const struct device * dev)39 static int api_start(const struct device *dev)
40 {
41 /* Ensure that both sec and subsec are reset to 0 */
42 while (MXC_RTC_Init(0, 0) == E_BUSY) {
43 ;
44 }
45
46 while (MXC_RTC_Start() == E_BUSY) {
47 ;
48 }
49
50 while (MXC_RTC_EnableInt(MAX32_RTC_COUNTER_INT_EN) == E_BUSY) {
51 ;
52 }
53
54 return 0;
55 }
56
api_stop(const struct device * dev)57 static int api_stop(const struct device *dev)
58 {
59 ARG_UNUSED(dev);
60
61 while (MXC_RTC_DisableInt(MAX32_RTC_COUNTER_INT_EN) == E_BUSY) {
62 ;
63 }
64 MXC_RTC_Stop();
65
66 return 0;
67 }
68
api_get_value(const struct device * dev,uint32_t * ticks)69 static int api_get_value(const struct device *dev, uint32_t *ticks)
70 {
71 const struct max32_rtc_config *cfg = dev->config;
72 mxc_rtc_regs_t *regs = cfg->regs;
73 uint32_t sec = 0, subsec = 0;
74
75 /* Read twice incase of glitch */
76 sec = regs->sec;
77 if (regs->sec != sec) {
78 sec = regs->sec;
79 }
80
81 /* Read twice incase of glitch */
82 subsec = regs->ssec;
83 if (regs->ssec != subsec) {
84 subsec = regs->ssec;
85 }
86
87 *ticks = sec;
88 if (subsec >= (MXC_RTC_MAX_SSEC / 2)) {
89 *ticks += 1;
90 }
91
92 return 0;
93 }
94
api_set_top_value(const struct device * dev,const struct counter_top_cfg * counter_cfg)95 static int api_set_top_value(const struct device *dev, const struct counter_top_cfg *counter_cfg)
96 {
97 const struct max32_rtc_config *cfg = dev->config;
98 struct max32_rtc_data *const data = dev->data;
99
100 if (counter_cfg->ticks == 0) {
101 return -EINVAL;
102 }
103
104 if (counter_cfg->ticks != cfg->info.max_top_value) {
105 return -ENOTSUP;
106 }
107
108 data->top_callback = counter_cfg->callback;
109 data->top_user_data = counter_cfg->user_data;
110
111 return 0;
112 }
113
api_get_pending_int(const struct device * dev)114 static uint32_t api_get_pending_int(const struct device *dev)
115 {
116 ARG_UNUSED(dev);
117 int flags = MXC_RTC_GetFlags();
118
119 if (flags & MAX32_RTC_COUNTER_INT_FL) {
120 return 1;
121 }
122
123 return 0;
124 }
125
api_get_top_value(const struct device * dev)126 static uint32_t api_get_top_value(const struct device *dev)
127 {
128 const struct max32_rtc_config *cfg = dev->config;
129
130 return cfg->info.max_top_value;
131 }
132
api_set_alarm(const struct device * dev,uint8_t chan,const struct counter_alarm_cfg * alarm_cfg)133 static int api_set_alarm(const struct device *dev, uint8_t chan,
134 const struct counter_alarm_cfg *alarm_cfg)
135 {
136 int ret;
137 struct max32_rtc_data *data = dev->data;
138 uint32_t ticks = alarm_cfg->ticks;
139 uint32_t current;
140
141 /* Alarm frequenct is 1Hz so that it seems ticks becomes 0
142 * some times, in that case system being blocked.
143 * Set it to 1 if ticks is 0
144 */
145 if (ticks == 0) {
146 ticks = 1;
147 }
148
149 if (alarm_cfg->ticks > api_get_top_value(dev)) {
150 return -EINVAL;
151 }
152
153 if (data->alarm_callback != NULL) {
154 return -EBUSY;
155 }
156
157 api_stop(dev);
158
159 api_get_value(dev, ¤t);
160 if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) {
161 ticks += current;
162 }
163
164 ret = MXC_RTC_SetTimeofdayAlarm(ticks);
165 if (ret == E_BUSY) {
166 ret = -EBUSY;
167 }
168
169 if (ret == 0) {
170 data->alarm_callback = alarm_cfg->callback;
171 data->alarm_user_data = alarm_cfg->user_data;
172 }
173
174 api_start(dev);
175
176 return ret;
177 }
178
api_cancel_alarm(const struct device * dev,uint8_t chan)179 static int api_cancel_alarm(const struct device *dev, uint8_t chan)
180 {
181 struct max32_rtc_data *data = dev->data;
182
183 while (MXC_RTC_DisableInt(MAX32_RTC_COUNTER_INT_EN) == E_BUSY) {
184 ;
185 }
186 data->alarm_callback = NULL;
187
188 return 0;
189 }
190
rtc_max32_isr(const struct device * dev)191 static void rtc_max32_isr(const struct device *dev)
192 {
193 struct max32_rtc_data *const data = dev->data;
194 int flags = MXC_RTC_GetFlags();
195
196 if (flags & MAX32_RTC_COUNTER_INT_FL) {
197 if (data->alarm_callback) {
198 counter_alarm_callback_t cb;
199 uint32_t current;
200
201 api_get_value(dev, ¤t);
202
203 cb = data->alarm_callback;
204 data->alarm_callback = NULL;
205 cb(dev, 0, current, data->alarm_user_data);
206 }
207 }
208
209 /* Clear all flags */
210 MXC_RTC_ClearFlags(flags);
211 }
212
rtc_max32_init(const struct device * dev)213 static int rtc_max32_init(const struct device *dev)
214 {
215 const struct max32_rtc_config *cfg = dev->config;
216
217 while (MXC_RTC_Init(0, 0) == E_BUSY) {
218 ;
219 }
220
221 api_stop(dev);
222
223 cfg->irq_func();
224
225 return 0;
226 }
227
228 static DEVICE_API(counter, counter_rtc_max32_driver_api) = {
229 .start = api_start,
230 .stop = api_stop,
231 .get_value = api_get_value,
232 .set_top_value = api_set_top_value,
233 .get_pending_int = api_get_pending_int,
234 .get_top_value = api_get_top_value,
235 .set_alarm = api_set_alarm,
236 .cancel_alarm = api_cancel_alarm,
237 };
238
239 #define COUNTER_RTC_MAX32_INIT(_num) \
240 static struct max32_rtc_data rtc_max32_data_##_num; \
241 static void max32_rtc_irq_init_##_num(void) \
242 { \
243 IRQ_CONNECT(DT_INST_IRQN(_num), DT_INST_IRQ(_num, priority), rtc_max32_isr, \
244 DEVICE_DT_INST_GET(_num), 0); \
245 irq_enable(DT_INST_IRQN(_num)); \
246 if (DT_INST_PROP(_num, wakeup_source)) { \
247 MXC_LP_EnableRTCAlarmWakeup(); \
248 } \
249 }; \
250 static const struct max32_rtc_config rtc_max32_config_##_num = { \
251 .info = \
252 { \
253 .max_top_value = MAX32_RTC_COUNTER_MAX_VALUE, \
254 .freq = MAX32_RTC_COUNTER_FREQ, \
255 .flags = COUNTER_CONFIG_INFO_COUNT_UP, \
256 .channels = 1, \
257 }, \
258 .regs = (mxc_rtc_regs_t *)DT_INST_REG_ADDR(_num), \
259 .irq_func = max32_rtc_irq_init_##_num, \
260 }; \
261 \
262 DEVICE_DT_INST_DEFINE(_num, &rtc_max32_init, NULL, &rtc_max32_data_##_num, \
263 &rtc_max32_config_##_num, PRE_KERNEL_1, \
264 CONFIG_COUNTER_INIT_PRIORITY, &counter_rtc_max32_driver_api);
265
266 DT_INST_FOREACH_STATUS_OKAY(COUNTER_RTC_MAX32_INIT)
267