From 9f3e734e5c3760ff30906862a3f9ca724ea5fffb Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Sat, 25 Feb 2017 09:56:53 +0100 Subject: 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 Reviewed-on: https://review.coreboot.org/c/coreboot/+/18498 Tested-by: build bot (Jenkins) Reviewed-by: Nico Huber --- payloads/libpayload/drivers/nvram.c | 49 +++++++++++++++++++++++++------- payloads/libpayload/include/libpayload.h | 3 ++ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/payloads/libpayload/drivers/nvram.c b/payloads/libpayload/drivers/nvram.c index a116d1b65f..34ee0331e1 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 + * Copyright (C) 2017 Patrick Rudolph * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -111,23 +112,51 @@ 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 & NVRAM_RTC_FORMAT_BINARY)) { + 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 & 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_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, 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; } diff --git a/payloads/libpayload/include/libpayload.h b/payloads/libpayload/include/libpayload.h index 4b6a250f28..74969726bf 100644 --- a/payloads/libpayload/include/libpayload.h +++ b/payloads/libpayload/include/libpayload.h @@ -130,6 +130,9 @@ static const char _pstruct(key)[] \ #define NVRAM_RTC_YEAR 9 /**< RTC Year offset in CMOS */ #define NVRAM_RTC_FREQ_SELECT 10 /**< RTC Update Status Register */ #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 */ struct tm { -- cgit v1.2.3