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