drivers: rtc: mc146818: fix y2k bug

That's correct. We are still fixing the Y2K bug in 2023 \o/

* write century to RAM register 0x32
* ensure year register is in [0,99] (inclusive)

Aside from that, there were a few other errors in the driver.

* translate epoch-centric RTC API year to begin at 1900
* fix off-by-one error with month limit
* fix off-by-one error with wday
* fix off-by-one-hundred error with year limit
* adjust timeptr values in rtc_mc146818_validate_time()
* adjust timeptr values in rtc_mc146818_validate_alarm()

With the above, the testsuite passes!

Signed-off-by: Christopher Friedt <cfriedt@meta.com>
This commit is contained in:
Christopher Friedt 2023-06-19 16:19:46 -04:00 committed by Carles Cufí
commit c8e0022d4b

View file

@ -34,6 +34,9 @@
#define RTC_MONTH 0x08
#define RTC_YEAR 0x09
/* Y2K Bugfix */
#define RTC_CENTURY 0x32
/* Alarm time indices in RTC RAM */
#define RTC_ALARM_SEC 0x01
#define RTC_ALARM_MIN 0x03
@ -106,10 +109,10 @@
#define MIN_WDAY 1
#define MAX_MDAY 31
#define MIN_MDAY 1
#define MAX_MON 11
#define MIN_MON 0
#define MAX_MON 12
#define MIN_MON 1
#define MIN_YEAR_DIFF 0 /* YEAR - 1900 */
#define MAX_YEAR_DIFF 199 /* YEAR - 1900 */
#define MAX_YEAR_DIFF 99 /* YEAR - 1999 */
struct rtc_mc146818_data {
struct k_spinlock lock;
@ -149,16 +152,16 @@ static bool rtc_mc146818_validate_time(const struct rtc_time *timeptr)
if (timeptr->tm_hour < MIN_HOUR || timeptr->tm_hour > MAX_HOUR) {
return false;
}
if (timeptr->tm_wday < MIN_WDAY || timeptr->tm_wday > MAX_WDAY) {
if (timeptr->tm_wday + 1 < MIN_WDAY || timeptr->tm_wday + 1 > MAX_WDAY) {
return false;
}
if (timeptr->tm_mday < MIN_MDAY || timeptr->tm_mday > MAX_MDAY) {
return false;
}
if (timeptr->tm_mon < MIN_MON || timeptr->tm_mon > MAX_MON) {
if (timeptr->tm_mon + 1 < MIN_MON || timeptr->tm_mon + 1 > MAX_MON) {
return false;
}
if (timeptr->tm_year < MIN_YEAR_DIFF || timeptr->tm_year > MAX_YEAR_DIFF) {
if (timeptr->tm_year - 70 < MIN_YEAR_DIFF || timeptr->tm_year - 70 > MAX_YEAR_DIFF) {
return false;
}
return true;
@ -168,6 +171,8 @@ static int rtc_mc146818_set_time(const struct device *dev, const struct rtc_time
{
struct rtc_mc146818_data * const dev_data = dev->data;
uint8_t value;
int year;
int cent;
int ret;
k_spinlock_key_t key = k_spin_lock(&dev_data->lock);
@ -186,6 +191,9 @@ static int rtc_mc146818_set_time(const struct device *dev, const struct rtc_time
value = rtc_read(RTC_DATA);
rtc_write(RTC_DATA, value | RTC_UCI_BIT);
year = (1970 + timeptr->tm_year) % 100;
cent = (1970 + timeptr->tm_year) / 100;
if (!(rtc_read(RTC_DATA) & RTC_DMODE_BIT)) {
rtc_write(RTC_SEC, (uint8_t)bin2bcd(timeptr->tm_sec));
rtc_write(RTC_MIN, (uint8_t)bin2bcd(timeptr->tm_min));
@ -193,7 +201,8 @@ static int rtc_mc146818_set_time(const struct device *dev, const struct rtc_time
rtc_write(RTC_WDAY, (uint8_t)bin2bcd(timeptr->tm_wday));
rtc_write(RTC_MDAY, (uint8_t)bin2bcd(timeptr->tm_mday));
rtc_write(RTC_MONTH, (uint8_t)bin2bcd(timeptr->tm_mon + 1));
rtc_write(RTC_YEAR, (uint8_t)bin2bcd(timeptr->tm_year));
rtc_write(RTC_YEAR, (uint8_t)bin2bcd(year));
rtc_write(RTC_CENTURY, (uint8_t)bin2bcd(cent));
} else {
rtc_write(RTC_SEC, (uint8_t)timeptr->tm_sec);
rtc_write(RTC_MIN, (uint8_t)timeptr->tm_min);
@ -201,7 +210,8 @@ static int rtc_mc146818_set_time(const struct device *dev, const struct rtc_time
rtc_write(RTC_WDAY, (uint8_t)timeptr->tm_wday);
rtc_write(RTC_MDAY, (uint8_t)timeptr->tm_mday);
rtc_write(RTC_MONTH, (uint8_t)timeptr->tm_mon + 1);
rtc_write(RTC_YEAR, (uint8_t)timeptr->tm_year);
rtc_write(RTC_YEAR, year);
rtc_write(RTC_CENTURY, cent);
}
if (timeptr->tm_isdst == 1) {
@ -221,6 +231,8 @@ static int rtc_mc146818_get_time(const struct device *dev, struct rtc_time *tim
{
struct rtc_mc146818_data * const dev_data = dev->data;
int ret;
uint8_t cent;
uint8_t year;
uint8_t value;
k_spinlock_key_t key = k_spin_lock(&dev_data->lock);
@ -239,16 +251,18 @@ static int rtc_mc146818_get_time(const struct device *dev, struct rtc_time *tim
while (rtc_read(RTC_UIP) & RTC_UIP_BIT) {
continue;
}
timeptr->tm_year = rtc_read(RTC_YEAR);
cent = rtc_read(RTC_CENTURY);
year = rtc_read(RTC_YEAR);
timeptr->tm_mon = rtc_read(RTC_MONTH) - 1;
timeptr->tm_mday = rtc_read(RTC_MDAY);
timeptr->tm_wday = rtc_read(RTC_WDAY);
timeptr->tm_wday = rtc_read(RTC_WDAY) - 1;
timeptr->tm_hour = rtc_read(RTC_HOUR);
timeptr->tm_min = rtc_read(RTC_MIN);
timeptr->tm_sec = rtc_read(RTC_SEC);
if (!(rtc_read(RTC_DATA) & RTC_DMODE_BIT)) {
timeptr->tm_year = bcd2bin(timeptr->tm_year);
year = bcd2bin(year);
cent = bcd2bin(cent);
timeptr->tm_mon = bcd2bin(timeptr->tm_mon);
timeptr->tm_mday = bcd2bin(timeptr->tm_mday);
timeptr->tm_wday = bcd2bin(timeptr->tm_wday);
@ -257,6 +271,8 @@ static int rtc_mc146818_get_time(const struct device *dev, struct rtc_time *tim
timeptr->tm_sec = bcd2bin(timeptr->tm_sec);
}
timeptr->tm_year = 100 * (int)cent + year - 1970;
timeptr->tm_nsec = 0;
timeptr->tm_yday = 0;
value = rtc_read(RTC_DATA);
@ -296,7 +312,7 @@ static bool rtc_mc146818_validate_alarm(const struct rtc_time *timeptr, uint32_t
}
if ((mask & RTC_ALARM_TIME_MASK_MONTH) &&
(timeptr->tm_mon < MIN_WDAY || timeptr->tm_mon > MAX_WDAY)) {
(timeptr->tm_mon + 1 < MIN_WDAY || timeptr->tm_mon + 1 > MAX_WDAY)) {
return false;
}
@ -306,7 +322,7 @@ static bool rtc_mc146818_validate_alarm(const struct rtc_time *timeptr, uint32_t
}
if ((mask & RTC_ALARM_TIME_MASK_YEAR) &&
(timeptr->tm_year < MIN_YEAR_DIFF || timeptr->tm_year > MAX_YEAR_DIFF)) {
(timeptr->tm_year - 70 < MIN_YEAR_DIFF || timeptr->tm_year - 70 > MAX_YEAR_DIFF)) {
return false;
}