1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright 2022 Microchip
4 */
5
6 #include <assert.h>
7 #include <drivers/rtc.h>
8
9 /* Time fields shift values to fit date in a 64bit word */
10 #define RTC_CMP_YEAR_SHIFT U(36)
11 #define RTC_CMP_MONTH_SHIFT U(32)
12 #define RTC_CMP_DAY_SHIFT U(27)
13 #define RTC_CMP_HOUR_SHIFT U(22)
14 #define RTC_CMP_MINUTES_SHIFT U(16)
15 #define RTC_CMP_SECONDS_SHIFT U(10)
16
17 struct rtc *rtc_device;
18
19 static const uint8_t rtc_months_days[] = {
20 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
21 };
22
rtc_time_to_ms(struct optee_rtc_time * time)23 static unsigned long long rtc_time_to_ms(struct optee_rtc_time *time)
24 {
25 uint64_t time_ms = 0;
26 unsigned int n = 0;
27 uint64_t days = 0;
28 uint64_t year = 0;
29
30 if (!time->tm_mday || time->tm_year < rtc_device->range_min.tm_year ||
31 time->tm_year > rtc_device->range_max.tm_year)
32 panic();
33
34 /* Elapsed milliseconds since midnight */
35 time_ms = time->tm_ms;
36 time_ms += time->tm_sec * MS_PER_SEC;
37 time_ms += time->tm_min * MS_PER_MIN;
38 time_ms += time->tm_hour * MS_PER_HOUR;
39
40 /* Elapsed days in current month and in past months of current year */
41 days = time->tm_mday - 1;
42 for (n = 0; n < time->tm_mon; n++)
43 days += rtc_get_month_days(n, time->tm_year);
44
45 /* Elapsed days pasted years */
46 year = time->tm_year;
47 days += (year * 365) + (year / 4) - (year / 100) + (year / 400);
48
49 /* Accumulate the elapsed days */
50 time_ms += days * MS_PER_DAY;
51
52 return time_ms;
53 }
54
rtc_is_a_leap_year(uint32_t year)55 bool rtc_is_a_leap_year(uint32_t year)
56 {
57 return !(year % 4) && ((year % 100) || !(year % 400));
58 }
59
rtc_get_month_days(uint32_t month,uint32_t year)60 uint8_t rtc_get_month_days(uint32_t month, uint32_t year)
61 {
62 if (rtc_is_a_leap_year(year) && month == 1)
63 return rtc_months_days[month] + 1;
64 else
65 return rtc_months_days[month];
66 }
67
rtc_timecmp(struct optee_rtc_time * a,struct optee_rtc_time * b)68 int rtc_timecmp(struct optee_rtc_time *a, struct optee_rtc_time *b)
69 {
70 uint64_t tm_a = 0;
71 uint64_t tm_b = 0;
72
73 tm_a = SHIFT_U64(a->tm_year, RTC_CMP_YEAR_SHIFT) +
74 SHIFT_U64(a->tm_mon, RTC_CMP_MONTH_SHIFT) +
75 SHIFT_U64(a->tm_mday, RTC_CMP_DAY_SHIFT) +
76 SHIFT_U64(a->tm_hour, RTC_CMP_HOUR_SHIFT) +
77 SHIFT_U64(a->tm_min, RTC_CMP_MINUTES_SHIFT) +
78 SHIFT_U64(a->tm_sec, RTC_CMP_SECONDS_SHIFT) +
79 a->tm_ms;
80 tm_b = SHIFT_U64(b->tm_year, RTC_CMP_YEAR_SHIFT) +
81 SHIFT_U64(b->tm_mon, RTC_CMP_MONTH_SHIFT) +
82 SHIFT_U64(b->tm_mday, RTC_CMP_DAY_SHIFT) +
83 SHIFT_U64(b->tm_hour, RTC_CMP_HOUR_SHIFT) +
84 SHIFT_U64(b->tm_min, RTC_CMP_MINUTES_SHIFT) +
85 SHIFT_U64(b->tm_sec, RTC_CMP_SECONDS_SHIFT) +
86 b->tm_ms;
87
88 return CMP_TRILEAN(tm_a, tm_b);
89 }
90
rtc_diff_calendar_ms(struct optee_rtc_time * ref1,struct optee_rtc_time * ref2)91 signed long long rtc_diff_calendar_ms(struct optee_rtc_time *ref1,
92 struct optee_rtc_time *ref2)
93 {
94 signed long long res = 0;
95
96 if (SUB_OVERFLOW(rtc_time_to_ms(ref1), rtc_time_to_ms(ref2), &res)) {
97 DMSG("Overflow while computing time diff in milliseconds");
98 return LLONG_MAX;
99 }
100
101 return res;
102 }
103
rtc_diff_calendar_tick(struct optee_rtc_time * ref1,struct optee_rtc_time * ref2,unsigned long long tick_rate)104 signed long long rtc_diff_calendar_tick(struct optee_rtc_time *ref1,
105 struct optee_rtc_time *ref2,
106 unsigned long long tick_rate)
107 {
108 signed long long diff_ms = rtc_diff_calendar_ms(ref1, ref2);
109 signed long long res = 0;
110
111 if (diff_ms == LLONG_MAX || MUL_OVERFLOW(diff_ms, tick_rate, &res)) {
112 DMSG("Overflow while computing calendar diff in ticks");
113 return LLONG_MAX;
114 }
115
116 return res / MS_PER_SEC;
117 }
118
rtc_register(struct rtc * rtc)119 void rtc_register(struct rtc *rtc)
120 {
121 /* One RTC is supported only */
122 assert(!rtc_device);
123
124 /* RTC should *at least* allow to get the time */
125 assert(rtc && rtc->ops && rtc->ops->get_time);
126
127 rtc_device = rtc;
128 }
129