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