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 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 55 bool rtc_is_a_leap_year(uint32_t year) 56 { 57 return !(year % 4) && ((year % 100) || !(year % 400)); 58 } 59 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 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 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 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 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