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