libpayload: 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.

Change-Id: Id773c33e228973e190a7e14c3d11979678b1a619
Signed-off-by: Patrick Rudolph <siro@das-labor.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/18498
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
This commit is contained in:
Patrick Rudolph 2017-02-25 09:56:53 +01:00 committed by Patrick Georgi
parent ef613b97cf
commit 9f3e734e5c
2 changed files with 41 additions and 9 deletions

View File

@ -2,6 +2,7 @@
* This file is part of the libpayload project. * This file is part of the libpayload project.
* *
* Copyright (C) 2008 Uwe Hermann <uwe@hermann-uwe.de> * 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 * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@ -111,23 +112,51 @@ int nvram_updating(void)
*/ */
void rtc_read_clock(struct tm *time) void rtc_read_clock(struct tm *time)
{ {
u16 timeout = 10000;
u8 statusB;
u8 reg8;
memset(time, 0, sizeof(*time)); memset(time, 0, sizeof(*time));
while(nvram_updating()); while (nvram_updating())
if (!timeout--)
return;
statusB = nvram_read(NVRAM_RTC_STATUSB);
if (!(statusB & NVRAM_RTC_FORMAT_BINARY)) {
time->tm_mon = bcd2dec(nvram_read(NVRAM_RTC_MONTH)) - 1; time->tm_mon = bcd2dec(nvram_read(NVRAM_RTC_MONTH)) - 1;
time->tm_sec = bcd2dec(nvram_read(NVRAM_RTC_SECONDS)); time->tm_sec = bcd2dec(nvram_read(NVRAM_RTC_SECONDS));
time->tm_min = bcd2dec(nvram_read(NVRAM_RTC_MINUTES)); time->tm_min = bcd2dec(nvram_read(NVRAM_RTC_MINUTES));
time->tm_mday = bcd2dec(nvram_read(NVRAM_RTC_DAY)); time->tm_mday = bcd2dec(nvram_read(NVRAM_RTC_DAY));
if (!(statusB & NVRAM_RTC_FORMAT_24HOUR)) {
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_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 & NVRAM_RTC_FORMAT_24HOUR)) {
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, /* Instead of finding the century register,
we just make an assumption that if the year value is we just make an assumption that if the year value is
less then 80, then it is 2000+ less then 80, then it is 2000+
*/ */
time->tm_year = bcd2dec(nvram_read(NVRAM_RTC_YEAR));
if (time->tm_year < 80) if (time->tm_year < 80)
time->tm_year += 100; time->tm_year += 100;
} }

View File

@ -130,6 +130,9 @@ static const char _pstruct(key)[] \
#define NVRAM_RTC_YEAR 9 /**< RTC Year 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_FREQ_SELECT 10 /**< RTC Update Status Register */
#define NVRAM_RTC_UIP 0x80 #define NVRAM_RTC_UIP 0x80
#define NVRAM_RTC_STATUSB 11 /**< RTC Status Register B */
#define NVRAM_RTC_FORMAT_24HOUR 0x02
#define NVRAM_RTC_FORMAT_BINARY 0x04
/** Broken down time structure */ /** Broken down time structure */
struct tm { struct tm {