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