1 /*
2 * Copyright (c) 2022 Bjarki Arge Andreasen
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT zephyr_rtc_emul
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/rtc.h>
12
13 #include "rtc_utils.h"
14
15 struct rtc_emul_data;
16
17 struct rtc_emul_work_delayable {
18 struct k_work_delayable dwork;
19 const struct device *dev;
20 };
21
22 struct rtc_emul_alarm {
23 struct rtc_time datetime;
24 rtc_alarm_callback callback;
25 void *user_data;
26 uint16_t mask;
27 bool pending;
28 };
29
30 struct rtc_emul_data {
31 bool datetime_set;
32
33 struct rtc_time datetime;
34
35 struct k_spinlock lock;
36
37 struct rtc_emul_work_delayable dwork;
38
39 #ifdef CONFIG_RTC_ALARM
40 struct rtc_emul_alarm *alarms;
41 uint16_t alarms_count;
42 #endif /* CONFIG_RTC_ALARM */
43
44 #ifdef CONFIG_RTC_UPDATE
45 rtc_update_callback update_callback;
46 void *update_callback_user_data;
47 #endif /* CONFIG_RTC_UPDATE */
48
49 #ifdef CONFIG_RTC_CALIBRATION
50 int32_t calibration;
51 #endif /* CONFIG_RTC_CALIBRATION */
52 };
53
54 static const uint8_t rtc_emul_days_in_month[12] = {
55 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
56 };
57
58 static const uint8_t rtc_emul_days_in_month_with_leap[12] = {
59 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
60 };
61
rtc_emul_is_leap_year(struct rtc_time * datetime)62 static bool rtc_emul_is_leap_year(struct rtc_time *datetime)
63 {
64 if ((datetime->tm_year % 400 == 0) ||
65 (((datetime->tm_year % 100) > 0) && ((datetime->tm_year % 4) == 0))) {
66 return true;
67 }
68
69 return false;
70 }
71
rtc_emul_get_days_in_month(struct rtc_time * datetime)72 static int rtc_emul_get_days_in_month(struct rtc_time *datetime)
73 {
74 const uint8_t *dim = (rtc_emul_is_leap_year(datetime) == true) ?
75 (rtc_emul_days_in_month_with_leap) :
76 (rtc_emul_days_in_month);
77
78 return dim[datetime->tm_mon];
79 }
80
rtc_emul_increment_tm(struct rtc_time * datetime)81 static void rtc_emul_increment_tm(struct rtc_time *datetime)
82 {
83 /* Increment second */
84 datetime->tm_sec++;
85
86 /* Validate second limit */
87 if (datetime->tm_sec < 60) {
88 return;
89 }
90
91 datetime->tm_sec = 0;
92
93 /* Increment minute */
94 datetime->tm_min++;
95
96 /* Validate minute limit */
97 if (datetime->tm_min < 60) {
98 return;
99 }
100
101 datetime->tm_min = 0;
102
103 /* Increment hour */
104 datetime->tm_hour++;
105
106 /* Validate hour limit */
107 if (datetime->tm_hour < 24) {
108 return;
109 }
110
111 datetime->tm_hour = 0;
112
113 /* Increment day */
114 datetime->tm_wday++;
115 datetime->tm_mday++;
116 datetime->tm_yday++;
117
118 /* Limit week day */
119 if (datetime->tm_wday > 6) {
120 datetime->tm_wday = 0;
121 }
122
123 /* Validate month limit */
124 if (datetime->tm_mday <= rtc_emul_get_days_in_month(datetime)) {
125 return;
126 }
127
128 datetime->tm_mday = 1;
129
130 /* Increment month */
131 datetime->tm_mon++;
132
133 /* Validate month limit */
134 if (datetime->tm_mon < 12) {
135 return;
136 }
137
138 /* Increment year */
139 datetime->tm_mon = 0;
140 datetime->tm_yday = 0;
141 datetime->tm_year++;
142 }
143
144 #ifdef CONFIG_RTC_ALARM
rtc_emul_test_alarms(const struct device * dev)145 static void rtc_emul_test_alarms(const struct device *dev)
146 {
147 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
148 struct rtc_emul_alarm *alarm;
149
150 for (uint16_t i = 0; i < data->alarms_count; i++) {
151 alarm = &data->alarms[i];
152
153 if (alarm->mask == 0) {
154 continue;
155 }
156
157 if ((alarm->mask & RTC_ALARM_TIME_MASK_SECOND) &&
158 (alarm->datetime.tm_sec != data->datetime.tm_sec)) {
159 continue;
160 }
161
162 if ((alarm->mask & RTC_ALARM_TIME_MASK_MINUTE) &&
163 (alarm->datetime.tm_min != data->datetime.tm_min)) {
164 continue;
165 }
166
167 if ((alarm->mask & RTC_ALARM_TIME_MASK_HOUR) &&
168 (alarm->datetime.tm_hour != data->datetime.tm_hour)) {
169 continue;
170 }
171
172 if ((alarm->mask & RTC_ALARM_TIME_MASK_MONTHDAY) &&
173 (alarm->datetime.tm_mday != data->datetime.tm_mday)) {
174 continue;
175 }
176
177 if ((alarm->mask & RTC_ALARM_TIME_MASK_MONTH) &&
178 (alarm->datetime.tm_mon != data->datetime.tm_mon)) {
179 continue;
180 }
181
182 if ((alarm->mask & RTC_ALARM_TIME_MASK_WEEKDAY) &&
183 (alarm->datetime.tm_wday != data->datetime.tm_wday)) {
184 continue;
185 }
186
187 if (alarm->callback == NULL) {
188 alarm->pending = true;
189
190 continue;
191 }
192
193 alarm->callback(dev, i, alarm->user_data);
194
195 alarm->pending = false;
196 }
197 }
198 #endif /* CONFIG_RTC_ALARM */
199
200 #ifdef CONFIG_RTC_UPDATE
rtc_emul_invoke_update_callback(const struct device * dev)201 static void rtc_emul_invoke_update_callback(const struct device *dev)
202 {
203 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
204
205 if (data->update_callback == NULL) {
206 return;
207 }
208
209 data->update_callback(dev, data->update_callback_user_data);
210 }
211 #endif /* CONFIG_RTC_UPDATE */
212
rtc_emul_update(struct k_work * work)213 static void rtc_emul_update(struct k_work *work)
214 {
215 struct rtc_emul_work_delayable *work_delayable = (struct rtc_emul_work_delayable *)work;
216 const struct device *dev = work_delayable->dev;
217 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
218
219 k_work_schedule(&work_delayable->dwork, K_MSEC(1000));
220
221 K_SPINLOCK(&data->lock) {
222 rtc_emul_increment_tm(&data->datetime);
223
224 #ifdef CONFIG_RTC_ALARM
225 rtc_emul_test_alarms(dev);
226 #endif /* CONFIG_RTC_ALARM */
227
228 #ifdef CONFIG_RTC_UPDATE
229 rtc_emul_invoke_update_callback(dev);
230 #endif /* CONFIG_RTC_UPDATE */
231 }
232 }
233
rtc_emul_set_time(const struct device * dev,const struct rtc_time * timeptr)234 static int rtc_emul_set_time(const struct device *dev, const struct rtc_time *timeptr)
235 {
236 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
237
238 /* Validate arguments */
239 if (timeptr == NULL) {
240 return -EINVAL;
241 }
242
243 K_SPINLOCK(&data->lock)
244 {
245 data->datetime = *timeptr;
246 data->datetime.tm_isdst = -1;
247 data->datetime.tm_nsec = 0;
248
249 data->datetime_set = true;
250 }
251
252 return 0;
253 }
254
rtc_emul_get_time(const struct device * dev,struct rtc_time * timeptr)255 static int rtc_emul_get_time(const struct device *dev, struct rtc_time *timeptr)
256 {
257 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
258 int ret = 0;
259
260 /* Validate arguments */
261 if (timeptr == NULL) {
262 return -EINVAL;
263 }
264
265 K_SPINLOCK(&data->lock)
266 {
267 /* Validate RTC time is set */
268 if (data->datetime_set == false) {
269 ret = -ENODATA;
270
271 K_SPINLOCK_BREAK;
272 }
273
274 *timeptr = data->datetime;
275 }
276
277 return ret;
278 }
279
280 #ifdef CONFIG_RTC_ALARM
rtc_emul_alarm_get_supported_fields(const struct device * dev,uint16_t id,uint16_t * mask)281 static int rtc_emul_alarm_get_supported_fields(const struct device *dev, uint16_t id,
282 uint16_t *mask)
283 {
284 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
285
286 if (data->alarms_count <= id) {
287 return -EINVAL;
288 }
289
290 *mask = (RTC_ALARM_TIME_MASK_SECOND
291 | RTC_ALARM_TIME_MASK_MINUTE
292 | RTC_ALARM_TIME_MASK_HOUR
293 | RTC_ALARM_TIME_MASK_MONTHDAY
294 | RTC_ALARM_TIME_MASK_MONTH
295 | RTC_ALARM_TIME_MASK_WEEKDAY);
296
297 return 0;
298 }
299
rtc_emul_alarm_set_time(const struct device * dev,uint16_t id,uint16_t mask,const struct rtc_time * timeptr)300 static int rtc_emul_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask,
301 const struct rtc_time *timeptr)
302 {
303 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
304
305 if (data->alarms_count <= id) {
306 return -EINVAL;
307 }
308
309 if ((mask > 0) && (timeptr == NULL)) {
310 return -EINVAL;
311 }
312
313 if (mask > 0) {
314 if (rtc_utils_validate_rtc_time(timeptr, mask) == false) {
315 return -EINVAL;
316 }
317 }
318
319 K_SPINLOCK(&data->lock)
320 {
321 data->alarms[id].mask = mask;
322
323 if (timeptr != NULL) {
324 data->alarms[id].datetime = *timeptr;
325 }
326 }
327
328 return 0;
329 }
330
rtc_emul_alarm_get_time(const struct device * dev,uint16_t id,uint16_t * mask,struct rtc_time * timeptr)331 static int rtc_emul_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask,
332 struct rtc_time *timeptr)
333 {
334 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
335
336 if (data->alarms_count <= id) {
337 return -EINVAL;
338 }
339
340 K_SPINLOCK(&data->lock)
341 {
342 *timeptr = data->alarms[id].datetime;
343 *mask = data->alarms[id].mask;
344 }
345
346 return 0;
347 }
348
rtc_emul_alarm_is_pending(const struct device * dev,uint16_t id)349 static int rtc_emul_alarm_is_pending(const struct device *dev, uint16_t id)
350 {
351 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
352 int ret = 0;
353
354 if (data->alarms_count <= id) {
355 return -EINVAL;
356 }
357
358 K_SPINLOCK(&data->lock)
359 {
360 ret = (data->alarms[id].pending == true) ? 1 : 0;
361
362 data->alarms[id].pending = false;
363 }
364
365 return ret;
366 }
367
rtc_emul_alarm_set_callback(const struct device * dev,uint16_t id,rtc_alarm_callback callback,void * user_data)368 static int rtc_emul_alarm_set_callback(const struct device *dev, uint16_t id,
369 rtc_alarm_callback callback, void *user_data)
370 {
371 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
372
373 if (data->alarms_count <= id) {
374 return -EINVAL;
375 }
376
377 K_SPINLOCK(&data->lock)
378 {
379 data->alarms[id].callback = callback;
380 data->alarms[id].user_data = user_data;
381 }
382
383 return 0;
384 }
385 #endif /* CONFIG_RTC_ALARM */
386
387 #ifdef CONFIG_RTC_UPDATE
rtc_emul_update_set_callback(const struct device * dev,rtc_update_callback callback,void * user_data)388 static int rtc_emul_update_set_callback(const struct device *dev,
389 rtc_update_callback callback, void *user_data)
390 {
391 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
392
393 K_SPINLOCK(&data->lock)
394 {
395 data->update_callback = callback;
396 data->update_callback_user_data = user_data;
397 }
398
399 return 0;
400 }
401 #endif /* CONFIG_RTC_UPDATE */
402
403 #ifdef CONFIG_RTC_CALIBRATION
rtc_emul_set_calibration(const struct device * dev,int32_t calibration)404 static int rtc_emul_set_calibration(const struct device *dev, int32_t calibration)
405 {
406 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
407
408 K_SPINLOCK(&data->lock)
409 {
410 data->calibration = calibration;
411 }
412
413 return 0;
414 }
415
rtc_emul_get_calibration(const struct device * dev,int32_t * calibration)416 static int rtc_emul_get_calibration(const struct device *dev, int32_t *calibration)
417 {
418 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
419
420 K_SPINLOCK(&data->lock)
421 {
422 *calibration = data->calibration;
423 }
424
425 return 0;
426 }
427 #endif /* CONFIG_RTC_CALIBRATION */
428
429 static DEVICE_API(rtc, rtc_emul_driver_api) = {
430 .set_time = rtc_emul_set_time,
431 .get_time = rtc_emul_get_time,
432 #ifdef CONFIG_RTC_ALARM
433 .alarm_get_supported_fields = rtc_emul_alarm_get_supported_fields,
434 .alarm_set_time = rtc_emul_alarm_set_time,
435 .alarm_get_time = rtc_emul_alarm_get_time,
436 .alarm_is_pending = rtc_emul_alarm_is_pending,
437 .alarm_set_callback = rtc_emul_alarm_set_callback,
438 #endif /* CONFIG_RTC_ALARM */
439 #ifdef CONFIG_RTC_UPDATE
440 .update_set_callback = rtc_emul_update_set_callback,
441 #endif /* CONFIG_RTC_UPDATE */
442 #ifdef CONFIG_RTC_CALIBRATION
443 .set_calibration = rtc_emul_set_calibration,
444 .get_calibration = rtc_emul_get_calibration,
445 #endif /* CONFIG_RTC_CALIBRATION */
446 };
447
rtc_emul_init(const struct device * dev)448 int rtc_emul_init(const struct device *dev)
449 {
450 struct rtc_emul_data *data = (struct rtc_emul_data *)dev->data;
451
452 data->dwork.dev = dev;
453 k_work_init_delayable(&data->dwork.dwork, rtc_emul_update);
454
455 k_work_schedule(&data->dwork.dwork, K_MSEC(1000));
456
457 return 0;
458 }
459
460 #ifdef CONFIG_RTC_ALARM
461 #define RTC_EMUL_DEVICE_DATA(id) \
462 static struct rtc_emul_alarm rtc_emul_alarms_##id[DT_INST_PROP(id, alarms_count)]; \
463 \
464 struct rtc_emul_data rtc_emul_data_##id = { \
465 .alarms = rtc_emul_alarms_##id, \
466 .alarms_count = ARRAY_SIZE(rtc_emul_alarms_##id), \
467 };
468 #else
469 #define RTC_EMUL_DEVICE_DATA(id) \
470 struct rtc_emul_data rtc_emul_data_##id;
471 #endif /* CONFIG_RTC_ALARM */
472
473 #define RTC_EMUL_DEVICE(id) \
474 RTC_EMUL_DEVICE_DATA(id) \
475 \
476 DEVICE_DT_INST_DEFINE(id, rtc_emul_init, NULL, &rtc_emul_data_##id, NULL, POST_KERNEL, \
477 CONFIG_RTC_INIT_PRIORITY, &rtc_emul_driver_api);
478
479 DT_INST_FOREACH_STATUS_OKAY(RTC_EMUL_DEVICE);
480