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