Skip to content

Commit f076ef4

Browse files
jwerner-chromiumalexandrebelloni
authored andcommitted
rtc: rk808: Compensate for Rockchip calendar deviation on November 31st
In A.D. 1582 Pope Gregory XIII found that the existing Julian calendar insufficiently represented reality, and changed the rules about calculating leap years to account for this. Similarly, in A.D. 2013 Rockchip hardware engineers found that the new Gregorian calendar still contained flaws, and that the month of November should be counted up to 31 days instead. Unfortunately it takes a long time for calendar changes to gain widespread adoption, and just like more than 300 years went by before the last Protestant nation implemented Greg's proposal, we will have to wait a while until all religions and operating system kernels acknowledge the inherent advantages of the Rockchip system. Until then we need to translate dates read from (and written to) Rockchip hardware back to the Gregorian format. This patch works by defining Jan 1st, 2016 as the arbitrary anchor date on which Rockchip and Gregorian calendars are in sync. From that we can translate arbitrary later dates back and forth by counting the number of November/December transitons since the anchor date to determine the offset between the calendars. We choose this method (rather than trying to regularly "correct" the date stored in hardware) since it's the only way to ensure perfect time-keeping even if the system may be shut down for an unknown number of years. The drawback is that other software reading the same hardware (e.g. mainboard firmware) must use the same translation convention (including the same anchor date) to be able to read and write correct timestamps from/to the RTC. Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-by: Douglas Anderson <dianders@chromium.org> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
1 parent 3abb1ad commit f076ef4

File tree

1 file changed

+44
-4
lines changed

1 file changed

+44
-4
lines changed

drivers/rtc/rtc-rk808.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,42 @@ struct rk808_rtc {
5656
int irq;
5757
};
5858

59+
/*
60+
* The Rockchip calendar used by the RK808 counts November with 31 days. We use
61+
* these translation functions to convert its dates to/from the Gregorian
62+
* calendar used by the rest of the world. We arbitrarily define Jan 1st, 2016
63+
* as the day when both calendars were in sync, and treat all other dates
64+
* relative to that.
65+
* NOTE: Other system software (e.g. firmware) that reads the same hardware must
66+
* implement this exact same conversion algorithm, with the same anchor date.
67+
*/
68+
static time64_t nov2dec_transitions(struct rtc_time *tm)
69+
{
70+
return (tm->tm_year + 1900) - 2016 + (tm->tm_mon + 1 > 11 ? 1 : 0);
71+
}
72+
73+
static void rockchip_to_gregorian(struct rtc_time *tm)
74+
{
75+
/* If it's Nov 31st, rtc_tm_to_time64() will count that like Dec 1st */
76+
time64_t time = rtc_tm_to_time64(tm);
77+
rtc_time64_to_tm(time + nov2dec_transitions(tm) * 86400, tm);
78+
}
79+
80+
static void gregorian_to_rockchip(struct rtc_time *tm)
81+
{
82+
time64_t extra_days = nov2dec_transitions(tm);
83+
time64_t time = rtc_tm_to_time64(tm);
84+
rtc_time64_to_tm(time - extra_days * 86400, tm);
85+
86+
/* Compensate if we went back over Nov 31st (will work up to 2381) */
87+
if (nov2dec_transitions(tm) < extra_days) {
88+
if (tm->tm_mon + 1 == 11)
89+
tm->tm_mday++; /* This may result in 31! */
90+
else
91+
rtc_time64_to_tm(time - (extra_days - 1) * 86400, tm);
92+
}
93+
}
94+
5995
/* Read current time and date in RTC */
6096
static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
6197
{
@@ -101,9 +137,10 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
101137
tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1;
102138
tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100;
103139
tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK);
140+
rockchip_to_gregorian(tm);
104141
dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
105142
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
106-
tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec);
143+
tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
107144

108145
return ret;
109146
}
@@ -116,16 +153,17 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
116153
u8 rtc_data[NUM_TIME_REGS];
117154
int ret;
118155

156+
dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
157+
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
158+
tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
159+
gregorian_to_rockchip(tm);
119160
rtc_data[0] = bin2bcd(tm->tm_sec);
120161
rtc_data[1] = bin2bcd(tm->tm_min);
121162
rtc_data[2] = bin2bcd(tm->tm_hour);
122163
rtc_data[3] = bin2bcd(tm->tm_mday);
123164
rtc_data[4] = bin2bcd(tm->tm_mon + 1);
124165
rtc_data[5] = bin2bcd(tm->tm_year - 100);
125166
rtc_data[6] = bin2bcd(tm->tm_wday);
126-
dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
127-
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
128-
tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec);
129167

130168
/* Stop RTC while updating the RTC registers */
131169
ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
@@ -170,6 +208,7 @@ static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
170208
alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK);
171209
alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1;
172210
alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;
211+
rockchip_to_gregorian(&alrm->time);
173212

174213
ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg);
175214
if (ret) {
@@ -227,6 +266,7 @@ static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
227266
alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
228267
alrm->time.tm_min, alrm->time.tm_sec);
229268

269+
gregorian_to_rockchip(&alrm->time);
230270
alrm_data[0] = bin2bcd(alrm->time.tm_sec);
231271
alrm_data[1] = bin2bcd(alrm->time.tm_min);
232272
alrm_data[2] = bin2bcd(alrm->time.tm_hour);

0 commit comments

Comments
 (0)