Patrick Rudolph (siro@das-labor.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18498
-gerrit
commit a559af48c6a6b4f67af4b494e08ac37e52a8b638 Author: Patrick Rudolph siro@das-labor.org Date: Sat Feb 25 09:56:53 2017 +0100
libpayload: Fix and improve rtc functions
On Lenovo T500 the RTC readings where wrong, as RTC has different encodings, depending on the statusB register.
Support BCD vs binary RTC format and AM/PM vs 24h RTC format.
Fixes wrong date and time on Lenovo 500.
Add a function to write RTC. Writing the RTC allows payloads to easily change the RTC.
Change-Id: Id773c33e228973e190a7e14c3d11979678b1a619 Signed-off-by: Patrick Rudolph siro@das-labor.org --- payloads/libpayload/drivers/nvram.c | 100 +++++++++++++++++++++++++++---- payloads/libpayload/include/libpayload.h | 2 + 2 files changed, 92 insertions(+), 10 deletions(-)
diff --git a/payloads/libpayload/drivers/nvram.c b/payloads/libpayload/drivers/nvram.c index c825331..6e2ebdc 100644 --- a/payloads/libpayload/drivers/nvram.c +++ b/payloads/libpayload/drivers/nvram.c @@ -2,6 +2,7 @@ * This file is part of the libpayload project. * * Copyright (C) 2008 Uwe Hermann uwe@hermann-uwe.de + * Copyright (C) 2017 Patrick Rudolph siro@das-labor.org * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -111,23 +112,102 @@ int nvram_updating(void) */ void rtc_read_clock(struct tm *time) { - memset(time, 0, sizeof(*time)); + u16 timeout = 10000; + u8 statusB; + u8 reg8;
- while(nvram_updating()); + memset(time, 0, sizeof(*time));
- time->tm_mon = bcd2dec(nvram_read(NVRAM_RTC_MONTH)) - 1; - time->tm_sec = bcd2dec(nvram_read(NVRAM_RTC_SECONDS)); - time->tm_min = bcd2dec(nvram_read(NVRAM_RTC_MINUTES)); - time->tm_mday = bcd2dec(nvram_read(NVRAM_RTC_DAY)); - time->tm_hour = bcd2dec(nvram_read(NVRAM_RTC_HOURS)); + while (nvram_updating()) + if (!timeout--) + return; + + statusB = nvram_read(NVRAM_RTC_STATUSB); + + if (!(statusB & 0x04)) { + time->tm_mon = bcd2dec(nvram_read(NVRAM_RTC_MONTH)) - 1; + time->tm_sec = bcd2dec(nvram_read(NVRAM_RTC_SECONDS)); + time->tm_min = bcd2dec(nvram_read(NVRAM_RTC_MINUTES)); + time->tm_mday = bcd2dec(nvram_read(NVRAM_RTC_DAY)); + + if (!(statusB & 0x02)) { + reg8 = nvram_read(NVRAM_RTC_HOURS); + time->tm_hour = bcd2dec(reg8 & 0x7f); + time->tm_hour += (reg8 & 0x80) ? 12 : 0; + time->tm_hour %= 24; + } else + time->tm_hour = bcd2dec(nvram_read(NVRAM_RTC_HOURS)); + time->tm_year = bcd2dec(nvram_read(NVRAM_RTC_YEAR)); + } else { + time->tm_mon = nvram_read(NVRAM_RTC_MONTH) - 1; + time->tm_sec = nvram_read(NVRAM_RTC_SECONDS); + time->tm_min = nvram_read(NVRAM_RTC_MINUTES); + time->tm_mday = nvram_read(NVRAM_RTC_DAY); + if (!(statusB & 0x02)) { + reg8 = nvram_read(NVRAM_RTC_HOURS); + time->tm_hour = reg8 & 0x7f; + time->tm_hour += (reg8 & 0x80) ? 12 : 0; + time->tm_hour %= 24; + } else + time->tm_hour = nvram_read(NVRAM_RTC_HOURS); + time->tm_year = nvram_read(NVRAM_RTC_YEAR); + }
/* Instead of finding the century register, we just make an assumption that if the year value is less then 80, then it is 2000+ */ - - time->tm_year = bcd2dec(nvram_read(NVRAM_RTC_YEAR)); - if (time->tm_year < 80) time->tm_year += 100; } + +/** + * Write the current time and date to the RTC + * + * @param time A pointer to a broken-down time structure + */ +void rtc_write_clock(struct tm *time) +{ + u16 timeout = 10000; + u8 statusB; + u8 reg8; + + while (nvram_updating()) + if (!timeout--) + return; + + statusB = nvram_read(NVRAM_RTC_STATUSB); + + if (time->tm_year > 100) + time->tm_year -= 100; + + if (!(statusB & 0x04)) { + nvram_write(dec2bcd(time->tm_mon + 1), NVRAM_RTC_MONTH); + nvram_write(dec2bcd(time->tm_sec), NVRAM_RTC_SECONDS); + nvram_write(dec2bcd(time->tm_min), NVRAM_RTC_MINUTES); + nvram_write(dec2bcd(time->tm_mday), NVRAM_RTC_DAY); + if (!(statusB & 0x02)) { + if (time->tm_hour > 12) + reg8 = dec2bcd(time->tm_hour - 12) | 0x80; + else + reg8 = dec2bcd(time->tm_hour); + } else + reg8 = dec2bcd(time->tm_hour); + nvram_write(reg8, NVRAM_RTC_HOURS); + nvram_write(dec2bcd(time->tm_year), NVRAM_RTC_YEAR); + } else { + nvram_write(time->tm_mon + 1, NVRAM_RTC_MONTH); + nvram_write(time->tm_sec, NVRAM_RTC_SECONDS); + nvram_write(time->tm_min, NVRAM_RTC_MINUTES); + nvram_write(time->tm_mday, NVRAM_RTC_DAY); + if (!(statusB & 0x02)) { + if (time->tm_hour > 12) + reg8 = (time->tm_hour - 12) | 0x80; + else + reg8 = time->tm_hour; + } else + reg8 = time->tm_hour; + nvram_write(reg8, NVRAM_RTC_HOURS); + nvram_write(time->tm_year, NVRAM_RTC_YEAR); + } +} diff --git a/payloads/libpayload/include/libpayload.h b/payloads/libpayload/include/libpayload.h index 97d5944..53c96cd 100644 --- a/payloads/libpayload/include/libpayload.h +++ b/payloads/libpayload/include/libpayload.h @@ -104,6 +104,7 @@ static const char _pstruct(key)[] \ #define NVRAM_RTC_MONTH 8 /**< RTC Month offset in CMOS */ #define NVRAM_RTC_YEAR 9 /**< RTC Year offset in CMOS */ #define NVRAM_RTC_FREQ_SELECT 10 /**< RTC Update Status Register */ +#define NVRAM_RTC_STATUSB 11 /**< RTC Status Register B */ #define NVRAM_RTC_UIP 0x80
/** Broken down time structure */ @@ -123,6 +124,7 @@ u8 nvram_read(u8 addr); void nvram_write(u8 val, u8 addr); int nvram_updating(void); void rtc_read_clock(struct tm *tm); +void rtc_write_clock(struct tm *time); /** @} */
/**