1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (c) 2024 Ambiq Micro
5 */
6
7
8 #include <zephyr/drivers/rtc.h>
9 #include <zephyr/logging/log.h>
10 #include <zephyr/pm/device.h>
11 #include <zephyr/sys/util.h>
12 #include "rtc_utils.h"
13
14 #define DT_DRV_COMPAT ambiq_rtc
15
16 LOG_MODULE_REGISTER(ambiq_rtc, CONFIG_RTC_LOG_LEVEL);
17
18 #include <am_mcu_apollo.h>
19
20 #define AMBIQ_RTC_ALARM_TIME_MASK \
21 (RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR | \
22 RTC_ALARM_TIME_MASK_WEEKDAY | RTC_ALARM_TIME_MASK_MONTH | RTC_ALARM_TIME_MASK_MONTHDAY)
23
24 /* struct tm start time: 1st, Jan, 1900 */
25 #define TM_YEAR_REF 1900
26 #define AMBIQ_RTC_YEAR_MAX 2199
27
28 struct ambiq_rtc_config {
29 uint8_t clk_src;
30 };
31
32 struct ambiq_rtc_data {
33 struct k_spinlock lock;
34 #ifdef CONFIG_RTC_ALARM
35 struct rtc_time alarm_time;
36 uint16_t alarm_set_mask;
37 rtc_alarm_callback alarm_user_callback;
38 void *alarm_user_data;
39 bool alarm_pending;
40 #endif
41 };
42
rtc_time_to_ambiq_time_set(const struct rtc_time * tm,am_hal_rtc_time_t * atm)43 static void rtc_time_to_ambiq_time_set(const struct rtc_time *tm, am_hal_rtc_time_t *atm)
44 {
45 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
46 atm->ui32Century = ((tm->tm_year <= 99) || (tm->tm_year >= 200));
47 #else
48 atm->ui32CenturyBit = ((tm->tm_year > 99) && (tm->tm_year < 200));
49 #endif
50 atm->ui32Year = tm->tm_year;
51 if (tm->tm_year > 99) {
52 atm->ui32Year = tm->tm_year % 100;
53 }
54 atm->ui32Weekday = tm->tm_wday;
55 atm->ui32Month = tm->tm_mon + 1;
56 atm->ui32DayOfMonth = tm->tm_mday;
57 atm->ui32Hour = tm->tm_hour;
58 atm->ui32Minute = tm->tm_min;
59 atm->ui32Second = tm->tm_sec;
60
61 /* Nanoseconds times 10mil is hundredths */
62 atm->ui32Hundredths = tm->tm_nsec / 10000000;
63 if (atm->ui32Hundredths > 99) {
64 uint16_t value = atm->ui32Hundredths / 100;
65
66 atm->ui32Second += value;
67 atm->ui32Hundredths -= value*100;
68 }
69 }
70
71 /* To set the timer registers */
ambiq_time_to_rtc_time_set(const am_hal_rtc_time_t * atm,struct rtc_time * tm)72 static void ambiq_time_to_rtc_time_set(const am_hal_rtc_time_t *atm, struct rtc_time *tm)
73 {
74 tm->tm_year = atm->ui32Year;
75 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
76 if (atm->ui32Century == 0) {
77 tm->tm_year += 100;
78 } else {
79 tm->tm_year += 200;
80 }
81 #else
82 if (atm->ui32CenturyBit == 0) {
83 tm->tm_year += 200;
84 } else {
85 tm->tm_year += 100;
86 }
87 #endif
88 tm->tm_wday = atm->ui32Weekday;
89 tm->tm_mon = atm->ui32Month - 1;
90 tm->tm_mday = atm->ui32DayOfMonth;
91 tm->tm_hour = atm->ui32Hour;
92 tm->tm_min = atm->ui32Minute;
93 tm->tm_sec = atm->ui32Second;
94
95 /* Nanoseconds times 10mil is hundredths */
96 tm->tm_nsec = atm->ui32Hundredths * 10000000;
97 }
98
test_for_rollover(am_hal_rtc_time_t * atm)99 static int test_for_rollover(am_hal_rtc_time_t *atm)
100 {
101 if ((atm->ui32Year == 99) &&
102 (atm->ui32Month == 12) &&
103 (atm->ui32DayOfMonth == 31)) {
104 return -EINVAL;
105 }
106
107 return 0;
108 }
109
110 /* To set the timer registers */
ambiq_rtc_set_time(const struct device * dev,const struct rtc_time * timeptr)111 static int ambiq_rtc_set_time(const struct device *dev, const struct rtc_time *timeptr)
112 {
113 int err = 0;
114 am_hal_rtc_time_t ambiq_time = {0};
115
116 struct ambiq_rtc_data *data = dev->data;
117
118 if (timeptr->tm_year + TM_YEAR_REF > AMBIQ_RTC_YEAR_MAX) {
119 return -EINVAL;
120 }
121
122 k_spinlock_key_t key = k_spin_lock(&data->lock);
123
124 LOG_DBG("set time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, "
125 "min = %d, sec = %d",
126 timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
127 timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
128
129 /* Convertn to Ambiq Time */
130 rtc_time_to_ambiq_time_set(timeptr, &ambiq_time);
131
132 if (test_for_rollover(&ambiq_time)) {
133 return -EINVAL;
134 }
135
136 err = am_hal_rtc_time_set(&ambiq_time);
137 if (err) {
138 LOG_WRN("Set Timer returned an error - %d!", err);
139 }
140
141 k_spin_unlock(&data->lock, key);
142
143 return err;
144 }
145
146 /* To get from the timer registers */
ambiq_rtc_get_time(const struct device * dev,struct rtc_time * timeptr)147 static int ambiq_rtc_get_time(const struct device *dev, struct rtc_time *timeptr)
148 {
149 int err = 0;
150 am_hal_rtc_time_t ambiq_time = {0};
151 struct ambiq_rtc_data *data = dev->data;
152
153 k_spinlock_key_t key = k_spin_lock(&data->lock);
154
155 err = am_hal_rtc_time_get(&ambiq_time);
156 if (err != 0) {
157 LOG_WRN("Get Timer returned an error - %d!", err);
158 goto unlock;
159 }
160
161 ambiq_time_to_rtc_time_set(&ambiq_time, timeptr);
162
163 LOG_DBG("get time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, "
164 "min = %d, sec = %d",
165 timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
166 timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
167
168 unlock:
169 k_spin_unlock(&data->lock, key);
170
171 return err;
172 }
173
174 #ifdef CONFIG_RTC_ALARM
175
ambiq_rtc_alarm_get_supported_fields(const struct device * dev,uint16_t id,uint16_t * mask)176 static int ambiq_rtc_alarm_get_supported_fields(const struct device *dev,
177 uint16_t id, uint16_t *mask)
178 {
179 ARG_UNUSED(dev);
180
181 if (id != 0U) {
182 LOG_ERR("Invalid ID %d", id);
183 return -EINVAL;
184 }
185
186 *mask = AMBIQ_RTC_ALARM_TIME_MASK;
187
188 return 0;
189 }
190
191 /* To get from the alarm registers */
ambiq_rtc_alarm_get_time(const struct device * dev,uint16_t id,uint16_t * mask,struct rtc_time * timeptr)192 static int ambiq_rtc_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask,
193 struct rtc_time *timeptr)
194 {
195 am_hal_rtc_time_t ambiq_time = {0};
196 struct ambiq_rtc_data *data = dev->data;
197
198 if (id != 0U) {
199 LOG_ERR("Invalid ID %d", id);
200 return -EINVAL;
201 }
202
203 k_spinlock_key_t key = k_spin_lock(&data->lock);
204
205 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
206 am_hal_rtc_alarm_get(&ambiq_time);
207 #else
208 am_hal_rtc_alarm_get(&ambiq_time, NULL);
209 #endif
210
211 ambiq_time_to_rtc_time_set(&ambiq_time, timeptr);
212
213 *mask = data->alarm_set_mask;
214
215 LOG_DBG("get alarm: wday = %d, mon = %d, mday = %d, hour = %d, min = %d, sec = %d, "
216 "mask = 0x%04x", timeptr->tm_wday, timeptr->tm_mon, timeptr->tm_mday,
217 timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, *mask);
218
219 k_spin_unlock(&data->lock, key);
220
221 return 0;
222 }
223
ambiq_rtc_alarm_set_time(const struct device * dev,uint16_t id,uint16_t mask,const struct rtc_time * timeptr)224 static int ambiq_rtc_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask,
225 const struct rtc_time *timeptr)
226 {
227 struct ambiq_rtc_data *data = dev->data;
228 am_hal_rtc_time_t ambiq_time = {0};
229 uint16_t mask_available;
230
231 if (id != 0U) {
232 LOG_ERR("Invalid ID %d", id);
233 return -EINVAL;
234 }
235
236 if (rtc_utils_validate_rtc_time(timeptr, mask) == false) {
237 LOG_DBG("Invalid Input Value");
238 return -EINVAL;
239 }
240
241 (void)ambiq_rtc_alarm_get_supported_fields(NULL, 0, &mask_available);
242
243 if (mask & ~mask_available) {
244 return -EINVAL;
245 }
246
247 data->alarm_set_mask = mask;
248
249 k_spinlock_key_t key = k_spin_lock(&data->lock);
250
251 /* Disable and clear the alarm */
252 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
253 am_hal_rtc_int_disable(AM_HAL_RTC_INT_ALM);
254 am_hal_rtc_int_clear(AM_HAL_RTC_INT_ALM);
255 #else
256 am_hal_rtc_interrupt_disable(AM_HAL_RTC_INT_ALM);
257 am_hal_rtc_interrupt_clear(AM_HAL_RTC_INT_ALM);
258 #endif
259
260 /* When mask is 0 */
261 if (mask == 0) {
262 LOG_DBG("The alarm is disabled");
263 goto unlock;
264 }
265
266 LOG_DBG("set alarm: second = %d, min = %d, hour = %d, mday = %d, month = %d,"
267 "wday = %d, mask = 0x%04x",
268 timeptr->tm_sec, timeptr->tm_min, timeptr->tm_hour, timeptr->tm_mday,
269 timeptr->tm_mon, timeptr->tm_wday, mask);
270
271 /* Convertn to Ambiq Time */
272 rtc_time_to_ambiq_time_set(timeptr, &ambiq_time);
273
274 /* Set RTC ALARM, Ambiq must have interval != AM_HAL_RTC_ALM_RPT_DIS */
275 am_hal_rtc_alarm_set(&ambiq_time, AM_HAL_RTC_ALM_RPT_YR);
276
277 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
278 am_hal_rtc_int_enable(AM_HAL_RTC_INT_ALM);
279 #else
280 am_hal_rtc_interrupt_enable(AM_HAL_RTC_INT_ALM);
281 #endif
282
283 unlock:
284 k_spin_unlock(&data->lock, key);
285
286 return 0;
287 }
288
ambiq_rtc_alarm_is_pending(const struct device * dev,uint16_t id)289 static int ambiq_rtc_alarm_is_pending(const struct device *dev, uint16_t id)
290 {
291 struct ambiq_rtc_data *data = dev->data;
292 int ret = 0;
293
294 if (id != 0) {
295 return -EINVAL;
296 }
297
298 K_SPINLOCK(&data->lock) {
299 ret = data->alarm_pending ? 1 : 0;
300 data->alarm_pending = false;
301 }
302
303 return ret;
304 }
305
ambiq_rtc_isr(const struct device * dev)306 static void ambiq_rtc_isr(const struct device *dev)
307 {
308 /* Clear the RTC alarm interrupt. 8*/
309 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
310 am_hal_rtc_int_clear(AM_HAL_RTC_INT_ALM);
311 #else
312 am_hal_rtc_interrupt_clear(AM_HAL_RTC_INT_ALM);
313 #endif
314
315 #if defined(CONFIG_RTC_ALARM)
316 struct ambiq_rtc_data *data = dev->data;
317
318 if (data->alarm_user_callback) {
319 data->alarm_user_callback(dev, 0, data->alarm_user_data);
320 data->alarm_pending = false;
321 } else {
322 data->alarm_pending = true;
323 }
324 #endif
325 }
326
ambiq_rtc_alarm_set_callback(const struct device * dev,uint16_t id,rtc_alarm_callback callback,void * user_data)327 static int ambiq_rtc_alarm_set_callback(const struct device *dev, uint16_t id,
328 rtc_alarm_callback callback, void *user_data)
329 {
330
331 struct ambiq_rtc_data *data = dev->data;
332
333 if (id != 0U) {
334 LOG_ERR("Invalid ID %d", id);
335 return -EINVAL;
336 }
337
338 K_SPINLOCK(&data->lock) {
339 data->alarm_user_callback = callback;
340 data->alarm_user_data = user_data;
341 if ((callback == NULL) && (user_data == NULL)) {
342 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
343 am_hal_rtc_int_disable(AM_HAL_RTC_INT_ALM);
344 #else
345 am_hal_rtc_interrupt_disable(AM_HAL_RTC_INT_ALM);
346 #endif
347 }
348 }
349
350 return 0;
351 }
352 #endif
353
ambiq_rtc_init(const struct device * dev)354 static int ambiq_rtc_init(const struct device *dev)
355 {
356 const struct ambiq_rtc_config *config = dev->config;
357
358 #ifdef CONFIG_RTC_ALARM
359 struct ambiq_rtc_data *data = dev->data;
360 #endif
361
362 /* Enable the clock for RTC. */
363 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
364 am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_XTAL_START + config->clk_src, NULL);
365 #endif
366 am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_RTC_SEL_XTAL + config->clk_src, NULL);
367 /* Enable the RTC. */
368 am_hal_rtc_osc_enable();
369
370 #ifdef CONFIG_RTC_ALARM
371 data->alarm_user_callback = NULL;
372 data->alarm_pending = false;
373
374 IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), ambiq_rtc_isr, DEVICE_DT_INST_GET(0),
375 0);
376 irq_enable(DT_INST_IRQN(0));
377 #endif
378 return 0;
379 }
380
381 static DEVICE_API(rtc, ambiq_rtc_driver_api) = {
382 .set_time = ambiq_rtc_set_time,
383 .get_time = ambiq_rtc_get_time,
384 /* RTC_UPDATE not supported */
385 #ifdef CONFIG_RTC_ALARM
386 .alarm_get_supported_fields = ambiq_rtc_alarm_get_supported_fields,
387 .alarm_set_time = ambiq_rtc_alarm_set_time,
388 .alarm_get_time = ambiq_rtc_alarm_get_time,
389 .alarm_is_pending = ambiq_rtc_alarm_is_pending,
390 .alarm_set_callback = ambiq_rtc_alarm_set_callback,
391 #endif
392 };
393
394 #define AMBIQ_RTC_INIT(inst) \
395 static const struct ambiq_rtc_config ambiq_rtc_config_##inst = { \
396 .clk_src = DT_INST_ENUM_IDX(inst, clock)}; \
397 \
398 static struct ambiq_rtc_data ambiq_rtc_data##inst; \
399 \
400 DEVICE_DT_INST_DEFINE(inst, &ambiq_rtc_init, NULL, &ambiq_rtc_data##inst, \
401 &ambiq_rtc_config_##inst, POST_KERNEL, \
402 CONFIG_RTC_INIT_PRIORITY, &ambiq_rtc_driver_api);
403
404 DT_INST_FOREACH_STATUS_OKAY(AMBIQ_RTC_INIT)
405