Marc Jones (marc.jones@se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/7889
-gerrit
commit b6be30e5cd80fd974742cd8678ec72ed5c645cf5 Author: Gabe Black gabeblack@google.com Date: Wed Apr 30 21:31:44 2014 -0700
rtc: Add an RTC API, and implement it for x86.
This CL adds an API for RTC drivers, and implements its two functions, rtc_get and rtc_set, for x86's RTC. The function which resets the clock when the CMOS has lost state now uses the RTC driver instead of accessing the those registers directly. The availability of "ALTCENTURY" is now set through a kconfig variable so it can be available to the RTC driver without having to have a specialized interface.
BUG=None TEST=Built and booted on Link with the event log code modified to use the RTC interface. Verified that the event times were accurate. BRANCH=nyan
Original-Change-Id: Ifa807898e583254e57167fd44932ea86627a02ee Original-Signed-off-by: Gabe Black gabeblack@google.com Original-Reviewed-on: https://chromium-review.googlesource.com/197795 Original-Reviewed-by: David Hendricks dhendrix@chromium.org Original-Tested-by: Gabe Black gabeblack@chromium.org Original-Commit-Queue: Gabe Black gabeblack@chromium.org (cherry picked from commit 9e0fd75142d29afe34f6c6b9ce0099f478ca5a93) Signed-off-by: Marc Jones marc.jones@se-eng.com
Change-Id: I159f9b4872a0bb932961b4168b180c087dfb1883 --- 3rdparty | 2 +- src/drivers/pc80/Kconfig | 15 ++ src/drivers/pc80/Makefile.inc | 10 +- src/drivers/pc80/mc146818.c | 323 ++++++++++++++++++++++++++++ src/drivers/pc80/mc146818rtc.c | 372 +++++---------------------------- src/include/bcd.h | 35 ++++ src/include/pc80/mc146818rtc.h | 5 +- src/include/rtc.h | 37 ++++ src/southbridge/amd/agesa/hudson/lpc.c | 2 +- src/southbridge/amd/cimx/sb700/late.c | 2 +- src/southbridge/amd/cimx/sb800/late.c | 2 +- src/southbridge/amd/cimx/sb900/late.c | 2 +- src/southbridge/amd/pi/avalon/lpc.c | 2 +- src/southbridge/amd/sb600/lpc.c | 2 +- src/southbridge/amd/sb700/lpc.c | 2 +- src/southbridge/amd/sb800/lpc.c | 2 +- 16 files changed, 477 insertions(+), 338 deletions(-)
diff --git a/3rdparty b/3rdparty index a8b0c52..9f68e20 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit a8b0c52850495c30dfa1cd8cc2c679a6ba4e18ac +Subproject commit 9f68e20e5ef4b6681fb18bdb4022471bc6810788 diff --git a/src/drivers/pc80/Kconfig b/src/drivers/pc80/Kconfig index 5fad3a5..8043177 100644 --- a/src/drivers/pc80/Kconfig +++ b/src/drivers/pc80/Kconfig @@ -22,3 +22,18 @@ config LPC_TPM Enable this option to enable TPM support in coreboot.
If unsure, say N. + +config DRIVERS_MC146818_RTC + bool + default y if ARCH_X86 + default n if !ARCH_X86 + +config DRIVERS_MC146818_CMOS + bool + default y if ARCH_X86 + default n if !ARCH_X86 + +config DRIVERS_RTC_HAS_ALTCENTURY + bool "The RTC supports alt century" + depends on DRIVERS_MC146818_RTC + default y diff --git a/src/drivers/pc80/Makefile.inc b/src/drivers/pc80/Makefile.inc index fe6d11f..d792764 100644 --- a/src/drivers/pc80/Makefile.inc +++ b/src/drivers/pc80/Makefile.inc @@ -1,5 +1,7 @@ -romstage-y += mc146818rtc.c -ramstage-y += mc146818rtc.c +romstage-$(CONFIG_DRIVERS_MC146818_RTC) += mc146818rtc.c +ramstage-$(CONFIG_DRIVERS_MC146818_RTC) += mc146818rtc.c +romstage-$(CONFIG_DRIVERS_MC146818_CMOS) += mc146818.c +ramstage-$(CONFIG_DRIVERS_MC146818_CMOS) += mc146818.c ramstage-y += isa-dma.c ramstage-y += i8254.c ramstage-y += i8259.c @@ -7,7 +9,10 @@ ramstage-$(CONFIG_UDELAY_IO) += udelay_io.c ramstage-y += keyboard.c ramstage-$(CONFIG_SPKMODEM) += spkmodem.c
+ifeq ($(CONFIG_DRIVERS_MC146818_CMOS),y) romstage-$(CONFIG_USE_OPTION_TABLE) += mc146818rtc_early.c +endif + romstage-$(CONFIG_LPC_TPM) += tpm.c romstage-$(CONFIG_SPKMODEM) += spkmodem.c
@@ -17,4 +22,5 @@ cbfs-files-$(CONFIG_HAVE_CMOS_DEFAULT) += cmos.default cmos.default-file = $(CONFIG_CMOS_DEFAULT_FILE):nvramtool cmos.default-type = 0xaa
+smm-y += mc146818.c smm-y += mc146818rtc.c diff --git a/src/drivers/pc80/mc146818.c b/src/drivers/pc80/mc146818.c new file mode 100644 index 0000000..f3a9d2b --- /dev/null +++ b/src/drivers/pc80/mc146818.c @@ -0,0 +1,323 @@ +#include <bcd.h> +#include <stdint.h> +#include <build.h> +#include <console/console.h> +#include <pc80/mc146818rtc.h> +#include <boot/coreboot_tables.h> +#include <rtc.h> +#include <string.h> +#if CONFIG_USE_OPTION_TABLE +#include "option_table.h" +#include <cbfs.h> +#endif +#if CONFIG_HAVE_ACPI_RESUME +#include <arch/acpi.h> +#endif + + +static void cmos_reset_date(void) +{ + /* Now setup a default date equals to the build date */ + struct rtc_time time = { + .sec = 0, + .min = 0, + .hour = 1, + .mday = bcd2bin(COREBOOT_BUILD_DAY_BCD), + .mon = bcd2bin(COREBOOT_BUILD_MONTH_BCD), + .year = 2000 + bcd2bin(COREBOOT_BUILD_YEAR_BCD), + .wday = bcd2bin(COREBOOT_BUILD_WEEKDAY_BCD) + }; + rtc_set(&time); +} + +#if CONFIG_USE_OPTION_TABLE +static int cmos_checksum_valid(int range_start, int range_end, int cks_loc) +{ + int i; + u16 sum, old_sum; + sum = 0; + for (i = range_start; i <= range_end; i++) + sum += cmos_read(i); + old_sum = ((cmos_read(cks_loc) << 8) | cmos_read(cks_loc + 1)) & + 0x0ffff; + return sum == old_sum; +} + +static void cmos_set_checksum(int range_start, int range_end, int cks_loc) +{ + int i; + u16 sum; + sum = 0; + for (i = range_start; i <= range_end; i++) + sum += cmos_read(i); + cmos_write(((sum >> 8) & 0x0ff), cks_loc); + cmos_write(((sum >> 0) & 0x0ff), cks_loc + 1); +} +#endif + +#define RTC_CONTROL_DEFAULT (RTC_24H) +#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ) + +#ifndef __SMM__ +void cmos_init(int invalid) +{ + int cmos_invalid = 0; + int checksum_invalid = 0; +#if CONFIG_USE_OPTION_TABLE + unsigned char x; +#endif + +#if CONFIG_HAVE_ACPI_RESUME + /* Avoid clearing pending interrupts and resetting the RTC control + * register in the resume path because the Linux kernel relies on + * this to know if it should restart the RTC timerqueue if the wake + * was due to the RTC alarm. + */ + if (acpi_get_sleep_type() == 3) + return; +#endif + + printk(BIOS_DEBUG, "RTC Init\n"); + +#if CONFIG_USE_OPTION_TABLE + /* See if there has been a CMOS power problem. */ + x = cmos_read(RTC_VALID); + cmos_invalid = !(x & RTC_VRT); + + /* See if there is a CMOS checksum error */ + checksum_invalid = !cmos_checksum_valid(PC_CKS_RANGE_START, + PC_CKS_RANGE_END,PC_CKS_LOC); + +#define CLEAR_CMOS 0 +#else +#define CLEAR_CMOS 1 +#endif + + if (invalid || cmos_invalid || checksum_invalid) { +#if CLEAR_CMOS + int i; + + cmos_write(0, 0x01); + cmos_write(0, 0x03); + cmos_write(0, 0x05); + for (i = 10; i < 128; i++) + cmos_write(0, i); +#endif + if (cmos_invalid) + cmos_reset_date(); + + printk(BIOS_WARNING, "RTC:%s%s%s%s\n", + invalid?" Clear requested":"", + cmos_invalid?" Power Problem":"", + checksum_invalid?" Checksum invalid":"", + CLEAR_CMOS?" zeroing cmos":""); + } + + /* Setup the real time clock */ + cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL); + /* Setup the frequency it operates at */ + cmos_write(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT); + /* Ensure all reserved bits are 0 in register D */ + cmos_write(RTC_VRT, RTC_VALID); + +#if CONFIG_USE_OPTION_TABLE + /* See if there is a LB CMOS checksum error */ + checksum_invalid = !cmos_checksum_valid(LB_CKS_RANGE_START, + LB_CKS_RANGE_END,LB_CKS_LOC); + if (checksum_invalid) + printk(BIOS_DEBUG, "RTC: coreboot checksum invalid\n"); + + /* Make certain we have a valid checksum */ + cmos_set_checksum(PC_CKS_RANGE_START, PC_CKS_RANGE_END, PC_CKS_LOC); +#endif + + /* Clear any pending interrupts */ + cmos_read(RTC_INTR_FLAGS); +} +#endif + + +#if CONFIG_USE_OPTION_TABLE +/* + * This routine returns the value of the requested bits + * input bit = bit count from the beginning of the cmos image + * length = number of bits to include in the value + * vret = a character pointer to where the value is to be returned + * returns 0 = successful, -1 = an error occurred + */ +static int get_cmos_value(unsigned long bit, unsigned long length, void *vret) +{ + unsigned char *ret; + unsigned long byte,byte_bit; + unsigned long i; + unsigned char uchar; + + /* + * The table is checked when it is built to ensure all + * values are valid. + */ + ret = vret; + byte = bit / 8; /* find the byte where the data starts */ + byte_bit = bit % 8; /* find the bit in the byte where the data starts */ + if (length < 9) { /* one byte or less */ + uchar = cmos_read(byte); /* load the byte */ + uchar >>= byte_bit; /* shift the bits to byte align */ + /* clear unspecified bits */ + ret[0] = uchar & ((1 << length) - 1); + } else { /* more that one byte so transfer the whole bytes */ + for (i = 0; length; i++, length -= 8, byte++) { + /* load the byte */ + ret[i] = cmos_read(byte); + } + } + return 0; +} + +int get_option(void *dest, const char *name) +{ + struct cmos_option_table *ct; + struct cmos_entries *ce; + size_t namelen; + int found = 0; + + /* Figure out how long name is */ + namelen = strnlen(name, CMOS_MAX_NAME_LENGTH); + + /* find the requested entry record */ + ct = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, "cmos_layout.bin", + CBFS_COMPONENT_CMOS_LAYOUT, NULL); + if (!ct) { + printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. " + "Options are disabled\n"); + return -2; + } + ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length); + for(; ce->tag == LB_TAG_OPTION; + ce = (struct cmos_entries*)((unsigned char *)ce + ce->size)) { + if (memcmp(ce->name, name, namelen) == 0) { + found = 1; + break; + } + } + if (!found) { + printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name); + return -2; + } + + if (get_cmos_value(ce->bit, ce->length, dest)) + return -3; + if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, + LB_CKS_LOC)) + return -4; + return 0 ; +} + +static int set_cmos_value(unsigned long bit, unsigned long length, void *vret) +{ + unsigned char *ret; + unsigned long byte,byte_bit; + unsigned long i; + unsigned char uchar, mask; + unsigned int chksum_update_needed = 0; + + ret = vret; + byte = bit / 8; /* find the byte where the data starts */ + byte_bit = bit % 8; /* find the bit where the data starts */ + if (length <= 8) { /* one byte or less */ + mask = (1 << length) - 1; + mask <<= byte_bit; + + uchar = cmos_read(byte); + uchar &= ~mask; + uchar |= (ret[0] << byte_bit); + cmos_write(uchar, byte); + if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END) + chksum_update_needed = 1; + } else { /* more that one byte so transfer the whole bytes */ + if (byte_bit || length % 8) + return -1; + + for (i = 0; length; i++, length -= 8, byte++) + cmos_write(ret[i], byte); + if (byte >= LB_CKS_RANGE_START && + byte <= LB_CKS_RANGE_END) + chksum_update_needed = 1; + } + + if (chksum_update_needed) { + cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END, + LB_CKS_LOC); + } + return 0; +} + + +int set_option(const char *name, void *value) +{ + struct cmos_option_table *ct; + struct cmos_entries *ce; + unsigned long length; + size_t namelen; + int found = 0; + + /* Figure out how long name is */ + namelen = strnlen(name, CMOS_MAX_NAME_LENGTH); + + /* find the requested entry record */ + ct = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, "cmos_layout.bin", + CBFS_COMPONENT_CMOS_LAYOUT, NULL); + if (!ct) { + printk(BIOS_ERR, "cmos_layout.bin could not be found. " + "Options are disabled\n"); + return -2; + } + ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length); + for(; ce->tag == LB_TAG_OPTION; + ce = (struct cmos_entries*)((unsigned char *)ce + ce->size)) { + if (memcmp(ce->name, name, namelen) == 0) { + found = 1; + break; + } + } + if (!found) { + printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name); + return -2; + } + + length = ce->length; + if (ce->config == 's') { + length = MAX(strlen((const char *)value) * 8, ce->length - 8); + /* make sure the string is null terminated */ + if ((set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0}))) + return -3; + } + + if ((set_cmos_value(ce->bit, length, value))) + return -3; + + return 0; +} +#endif /* CONFIG_USE_OPTION_TABLE */ + +/* + * If the CMOS is cleared, the rtc_reg has the invalid date. That + * hurts some OSes. Even if we don't set USE_OPTION_TABLE, we need + * to make sure the date is valid. + */ +void cmos_check_update_date(void) +{ + u8 year, century; + + /* Note: Need to check if the hardware supports RTC_CLK_ALTCENTURY. */ + century = CONFIG_DRIVERS_RTC_HAS_ALTCENTURY ? + cmos_read(RTC_CLK_ALTCENTURY) : 0; + year = cmos_read(RTC_CLK_YEAR); + + /* + * TODO: If century is 0xFF, 100% that the cmos is cleared. + * Other than that, so far rtc_year is the only entry to check + * if the date is valid. + */ + if (century > 0x99 || year > 0x99) /* Invalid date */ + cmos_reset_date(); +} diff --git a/src/drivers/pc80/mc146818rtc.c b/src/drivers/pc80/mc146818rtc.c index 014a8c9..7954af4 100644 --- a/src/drivers/pc80/mc146818rtc.c +++ b/src/drivers/pc80/mc146818rtc.c @@ -1,327 +1,53 @@ -#include <stdint.h> -#include <version.h> -#include <console/console.h> -#include <pc80/mc146818rtc.h> -#include <boot/coreboot_tables.h> -#include <string.h> -#if CONFIG_USE_OPTION_TABLE -#include "option_table.h" -#include <cbfs.h> -#endif -#include <arch/acpi.h> - - -static void cmos_update_date(u8 has_century) -{ - /* Now setup a default date equals to the build date */ - cmos_write(0, RTC_CLK_SECOND); - cmos_write(0, RTC_CLK_MINUTE); - cmos_write(1, RTC_CLK_HOUR); - cmos_write(coreboot_build_date.weekday + 1, RTC_CLK_DAYOFWEEK); - cmos_write(coreboot_build_date.day, RTC_CLK_DAYOFMONTH); - cmos_write(coreboot_build_date.month, RTC_CLK_MONTH); - cmos_write(coreboot_build_date.year, RTC_CLK_YEAR); - if (has_century) - cmos_write(coreboot_build_date.century, RTC_CLK_ALTCENTURY); -} - -#if CONFIG_USE_OPTION_TABLE -static int cmos_checksum_valid(int range_start, int range_end, int cks_loc) -{ - int i; - u16 sum, old_sum; - sum = 0; - for (i = range_start; i <= range_end; i++) - sum += cmos_read(i); - old_sum = ((cmos_read(cks_loc) << 8) | cmos_read(cks_loc + 1)) & - 0x0ffff; - return sum == old_sum; -} - -static void cmos_set_checksum(int range_start, int range_end, int cks_loc) -{ - int i; - u16 sum; - sum = 0; - for (i = range_start; i <= range_end; i++) - sum += cmos_read(i); - cmos_write(((sum >> 8) & 0x0ff), cks_loc); - cmos_write(((sum >> 0) & 0x0ff), cks_loc + 1); -} -#endif - -#if CONFIG_ARCH_X86 -#define RTC_CONTROL_DEFAULT (RTC_24H) -#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ) -#else -#if CONFIG_ARCH_ALPHA -#define RTC_CONTROL_DEFAULT (RTC_SQWE | RTC_24H) -#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ) -#endif -#endif - -#ifndef __SMM__ -void cmos_init(int invalid) -{ - int cmos_invalid = 0; - int checksum_invalid = 0; -#if CONFIG_USE_OPTION_TABLE - unsigned char x; -#endif - -#ifndef __PRE_RAM__ - /* - * Avoid clearing pending interrupts and resetting the RTC control - * register in the resume path because the Linux kernel relies on - * this to know if it should restart the RTC timer queue if the wake - * was due to the RTC alarm. - */ - if (acpi_is_wakeup_s3()) - return; -#endif /* __PRE_RAM__ */ - - printk(BIOS_DEBUG, "RTC Init\n"); - -#if CONFIG_USE_OPTION_TABLE - /* See if there has been a CMOS power problem. */ - x = cmos_read(RTC_VALID); - cmos_invalid = !(x & RTC_VRT); - - /* See if there is a CMOS checksum error */ - checksum_invalid = !cmos_checksum_valid(PC_CKS_RANGE_START, - PC_CKS_RANGE_END,PC_CKS_LOC); - -#define CLEAR_CMOS 0 -#else -#define CLEAR_CMOS 1 -#endif - - if (invalid || cmos_invalid || checksum_invalid) { -#if CLEAR_CMOS - int i; - - cmos_write(0, 0x01); - cmos_write(0, 0x03); - cmos_write(0, 0x05); - for (i = 10; i < 128; i++) - cmos_write(0, i); -#endif - if (cmos_invalid) - cmos_update_date(RTC_HAS_NO_ALTCENTURY); - - printk(BIOS_WARNING, "RTC:%s%s%s%s\n", - invalid?" Clear requested":"", - cmos_invalid?" Power Problem":"", - checksum_invalid?" Checksum invalid":"", - CLEAR_CMOS?" zeroing cmos":""); - } - - /* Setup the real time clock */ - cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL); - /* Setup the frequency it operates at */ - cmos_write(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT); - /* Ensure all reserved bits are 0 in register D */ - cmos_write(RTC_VRT, RTC_VALID); - -#if CONFIG_USE_OPTION_TABLE - /* See if there is a LB CMOS checksum error */ - checksum_invalid = !cmos_checksum_valid(LB_CKS_RANGE_START, - LB_CKS_RANGE_END,LB_CKS_LOC); - if (checksum_invalid) - printk(BIOS_DEBUG, "RTC: coreboot checksum invalid\n"); - - /* Make certain we have a valid checksum */ - cmos_set_checksum(PC_CKS_RANGE_START, PC_CKS_RANGE_END, PC_CKS_LOC); -#endif - - /* Clear any pending interrupts */ - cmos_read(RTC_INTR_FLAGS); -} -#endif - - -#if CONFIG_USE_OPTION_TABLE -/* - * This routine returns the value of the requested bits. - * input bit = bit count from the beginning of the cmos image - * length = number of bits to include in the value - * ret = a character pointer to where the value is to be returned - * returns CB_SUCCESS = successful, cb_err code if an error occurred - */ -static enum cb_err get_cmos_value(unsigned long bit, unsigned long length, - void *vret) -{ - unsigned char *ret; - unsigned long byte,byte_bit; - unsigned long i; - unsigned char uchar; - - /* - * The table is checked when it is built to ensure all - * values are valid. - */ - ret = vret; - byte = bit / 8; /* find the byte where the data starts */ - byte_bit = bit % 8; /* find the bit in the byte where the data starts */ - if (length < 9) { /* one byte or less */ - uchar = cmos_read(byte); /* load the byte */ - uchar >>= byte_bit; /* shift the bits to byte align */ - /* clear unspecified bits */ - ret[0] = uchar & ((1 << length) - 1); - } else { /* more that one byte so transfer the whole bytes */ - for (i = 0; length; i++, length -= 8, byte++) { - /* load the byte */ - ret[i] = cmos_read(byte); - } - } - return CB_SUCCESS; -} - -enum cb_err get_option(void *dest, const char *name) -{ - struct cmos_option_table *ct; - struct cmos_entries *ce; - size_t namelen; - int found = 0; - - /* Figure out how long name is */ - namelen = strnlen(name, CMOS_MAX_NAME_LENGTH); - - /* find the requested entry record */ - ct = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, "cmos_layout.bin", - CBFS_COMPONENT_CMOS_LAYOUT, NULL); - if (!ct) { - printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. " - "Options are disabled\n"); - return CB_CMOS_LAYOUT_NOT_FOUND; - } - ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length); - for(; ce->tag == LB_TAG_OPTION; - ce = (struct cmos_entries*)((unsigned char *)ce + ce->size)) { - if (memcmp(ce->name, name, namelen) == 0) { - found = 1; - break; - } - } - if (!found) { - printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name); - return CB_CMOS_OPTION_NOT_FOUND; - } - - if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS) - return CB_CMOS_ACCESS_ERROR; - if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC)) - return CB_CMOS_CHECKSUM_INVALID; - return CB_SUCCESS; -} - -static enum cb_err set_cmos_value(unsigned long bit, unsigned long length, - void *vret) -{ - unsigned char *ret; - unsigned long byte,byte_bit; - unsigned long i; - unsigned char uchar, mask; - unsigned int chksum_update_needed = 0; - - ret = vret; - byte = bit / 8; /* find the byte where the data starts */ - byte_bit = bit % 8; /* find the bit where the data starts */ - if (length <= 8) { /* one byte or less */ - mask = (1 << length) - 1; - mask <<= byte_bit; - - uchar = cmos_read(byte); - uchar &= ~mask; - uchar |= (ret[0] << byte_bit); - cmos_write(uchar, byte); - if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END) - chksum_update_needed = 1; - } else { /* more that one byte so transfer the whole bytes */ - if (byte_bit || length % 8) - return CB_ERR_ARG; - - for (i = 0; length; i++, length -= 8, byte++) - cmos_write(ret[i], byte); - if (byte >= LB_CKS_RANGE_START && - byte <= LB_CKS_RANGE_END) - chksum_update_needed = 1; - } - - if (chksum_update_needed) { - cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END, - LB_CKS_LOC); - } - return CB_SUCCESS; -} - - -enum cb_err set_option(const char *name, void *value) -{ - struct cmos_option_table *ct; - struct cmos_entries *ce; - unsigned long length; - size_t namelen; - int found = 0; - - /* Figure out how long name is */ - namelen = strnlen(name, CMOS_MAX_NAME_LENGTH); - - /* find the requested entry record */ - ct = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, "cmos_layout.bin", - CBFS_COMPONENT_CMOS_LAYOUT, NULL); - if (!ct) { - printk(BIOS_ERR, "cmos_layout.bin could not be found. " - "Options are disabled\n"); - return CB_CMOS_LAYOUT_NOT_FOUND; - } - ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length); - for(; ce->tag == LB_TAG_OPTION; - ce = (struct cmos_entries*)((unsigned char *)ce + ce->size)) { - if (memcmp(ce->name, name, namelen) == 0) { - found = 1; - break; - } - } - if (!found) { - printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name); - return CB_CMOS_OPTION_NOT_FOUND; - } - - length = ce->length; - if (ce->config == 's') { - length = MAX(strlen((const char *)value) * 8, ce->length - 8); - /* make sure the string is null terminated */ - if (set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0}) - != CB_SUCCESS) - return (CB_CMOS_ACCESS_ERROR); - } - - if (set_cmos_value(ce->bit, length, value) != CB_SUCCESS) - return (CB_CMOS_ACCESS_ERROR); - - return CB_SUCCESS; -} -#endif /* CONFIG_USE_OPTION_TABLE */ - /* - * If the CMOS is cleared, the rtc_reg has the invalid date. That - * hurts some OSes. Even if we don't set USE_OPTION_TABLE, we need - * to make sure the date is valid. + * This file is part of the coreboot project. + * + * Copyright 2014 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -void cmos_check_update_date(u8 has_century) -{ - u8 year, century; - - /* Note: Need to check if the hardware supports RTC_CLK_ALTCENTURY. */ - century = has_century ? cmos_read(RTC_CLK_ALTCENTURY) : 0; - year = cmos_read(RTC_CLK_YEAR);
- /* - * TODO: If century is 0xFF, 100% that the cmos is cleared. - * Other than that, so far rtc_year is the only entry to check - * if the date is valid. - */ - if (century > 0x99 || year > 0x99) /* Invalid date */ - cmos_update_date(has_century); +#include <bcd.h> +#include <pc80/mc146818rtc.h> +#include <rtc.h> + +int rtc_set(const struct rtc_time *time) +{ + cmos_write(bin2bcd(time->sec), RTC_CLK_SECOND); + cmos_write(bin2bcd(time->min), RTC_CLK_MINUTE); + cmos_write(bin2bcd(time->hour), RTC_CLK_HOUR); + cmos_write(bin2bcd(time->mday), RTC_CLK_DAYOFMONTH); + cmos_write(bin2bcd(time->mon), RTC_CLK_MONTH); + cmos_write(bin2bcd(time->year % 100), RTC_CLK_YEAR); + if (CONFIG_DRIVERS_RTC_HAS_ALTCENTURY) + cmos_write(bin2bcd(time->year / 100), + RTC_CLK_ALTCENTURY); + cmos_write(bin2bcd(time->wday + 1), RTC_CLK_DAYOFWEEK); + return 0; +} + +int rtc_get(struct rtc_time *time) +{ + time->sec = bcd2bin(cmos_read(RTC_CLK_SECOND)); + time->min = bcd2bin(cmos_read(RTC_CLK_MINUTE)); + time->hour = bcd2bin(cmos_read(RTC_CLK_HOUR)); + time->mday = bcd2bin(cmos_read(RTC_CLK_DAYOFMONTH)); + time->mon = bcd2bin(cmos_read(RTC_CLK_MONTH)); + time->year = bcd2bin(cmos_read(RTC_CLK_YEAR)); + if (CONFIG_DRIVERS_RTC_HAS_ALTCENTURY) + time->year += bcd2bin(cmos_read(RTC_CLK_ALTCENTURY)) * 100; + else + time->year += 2000; + time->wday = bcd2bin(cmos_read(RTC_CLK_DAYOFWEEK)) - 1; + return 0; } diff --git a/src/include/bcd.h b/src/include/bcd.h new file mode 100644 index 0000000..a085027 --- /dev/null +++ b/src/include/bcd.h @@ -0,0 +1,35 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#ifndef _BCD_H_ +#define _BCD_H_ + +#include <stdint.h> + +static inline uint8_t bcd2bin(uint8_t val) +{ + return ((val >> 4) & 0xf) * 10 + (val & 0xf); +} + +static inline uint8_t bin2bcd(uint8_t val) +{ + return ((val / 10) << 4) | (val % 10); +} + +#endif /* _BCD_H_ */ diff --git a/src/include/pc80/mc146818rtc.h b/src/include/pc80/mc146818rtc.h index 3d65909..90ae7ae 100644 --- a/src/include/pc80/mc146818rtc.h +++ b/src/include/pc80/mc146818rtc.h @@ -94,9 +94,6 @@ #define RTC_CLK_YEAR 9 #define RTC_CLK_ALTCENTURY 0x32
-#define RTC_HAS_ALTCENTURY 1 -#define RTC_HAS_NO_ALTCENTURY 0 - /* On PCs, the checksum is built only over bytes 16..45 */ #define PC_CKS_RANGE_START 16 #define PC_CKS_RANGE_END 45 @@ -173,7 +170,7 @@ static inline void cmos_write32(u8 offset, u32 value)
#if !defined(__ROMCC__) void cmos_init(int invalid); -void cmos_check_update_date(u8 has_century); +void cmos_check_update_date(void); #if CONFIG_USE_OPTION_TABLE enum cb_err set_option(const char *name, void *val); enum cb_err get_option(void *dest, const char *name); diff --git a/src/include/rtc.h b/src/include/rtc.h new file mode 100644 index 0000000..ba9a9c3 --- /dev/null +++ b/src/include/rtc.h @@ -0,0 +1,37 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#ifndef _RTC_H_ +#define _RTC_H_ + +struct rtc_time +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; +}; + +int rtc_set(const struct rtc_time *time); +int rtc_get(struct rtc_time *time); + +#endif /* _RTC_H_ */ diff --git a/src/southbridge/amd/agesa/hudson/lpc.c b/src/southbridge/amd/agesa/hudson/lpc.c index 836966e..b784fc4 100644 --- a/src/southbridge/amd/agesa/hudson/lpc.c +++ b/src/southbridge/amd/agesa/hudson/lpc.c @@ -71,7 +71,7 @@ static void lpc_init(device_t dev) byte |= 1 << 0 | 1 << 3; pci_write_config8(dev, 0xBB, byte);
- cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date();
/* Initialize the real time clock. * The 0 argument tells cmos_init not to diff --git a/src/southbridge/amd/cimx/sb700/late.c b/src/southbridge/amd/cimx/sb700/late.c index 9c70482..3fa6940 100644 --- a/src/southbridge/amd/cimx/sb700/late.c +++ b/src/southbridge/amd/cimx/sb700/late.c @@ -81,7 +81,7 @@ static void lpc_init(device_t dev) { printk(BIOS_DEBUG, "SB700 - Late.c - lpc_init - Start.\n");
- cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date();
/* Initialize the real time clock. * The 0 argument tells cmos_init not to diff --git a/src/southbridge/amd/cimx/sb800/late.c b/src/southbridge/amd/cimx/sb800/late.c index 9c4047f..d7f22d3 100644 --- a/src/southbridge/amd/cimx/sb800/late.c +++ b/src/southbridge/amd/cimx/sb800/late.c @@ -132,7 +132,7 @@ static void lpc_init(device_t dev) { printk(BIOS_DEBUG, "SB800 - Late.c - lpc_init - Start.\n");
- cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date();
/* Initialize the real time clock. * The 0 argument tells cmos_init not to diff --git a/src/southbridge/amd/cimx/sb900/late.c b/src/southbridge/amd/cimx/sb900/late.c index 6f6a501..e9f5ad6 100644 --- a/src/southbridge/amd/cimx/sb900/late.c +++ b/src/southbridge/amd/cimx/sb900/late.c @@ -103,7 +103,7 @@ static void lpc_init(device_t dev) printk(BIOS_DEBUG, "SB900 - Late.c - lpc_init - Start.\n"); /* SB Configure HPET base and enable bit */ //- hpetInit(sb_config, &(sb_config->BuildParameters)); - cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date();
/* Initialize the real time clock. * The 0 argument tells cmos_init not to diff --git a/src/southbridge/amd/pi/avalon/lpc.c b/src/southbridge/amd/pi/avalon/lpc.c index 1f60bc4..7b54cc3 100644 --- a/src/southbridge/amd/pi/avalon/lpc.c +++ b/src/southbridge/amd/pi/avalon/lpc.c @@ -70,7 +70,7 @@ static void lpc_init(device_t dev) byte |= 1 << 0 | 1 << 3; pci_write_config8(dev, 0xBB, byte);
- cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date();
/* Initialize the real time clock. * The 0 argument tells cmos_init not to diff --git a/src/southbridge/amd/sb600/lpc.c b/src/southbridge/amd/sb600/lpc.c index acee69d..dc2f31b 100644 --- a/src/southbridge/amd/sb600/lpc.c +++ b/src/southbridge/amd/sb600/lpc.c @@ -63,7 +63,7 @@ static void lpc_init(device_t dev) byte &= ~(1 << 1); pci_write_config8(dev, 0x78, byte);
- cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date(); }
static void sb600_lpc_read_resources(device_t dev) diff --git a/src/southbridge/amd/sb700/lpc.c b/src/southbridge/amd/sb700/lpc.c index 26478b7..8ebc765 100644 --- a/src/southbridge/amd/sb700/lpc.c +++ b/src/southbridge/amd/sb700/lpc.c @@ -90,7 +90,7 @@ static void lpc_init(device_t dev) } #endif
- cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date(); }
void backup_top_of_ram(uint64_t ramtop) diff --git a/src/southbridge/amd/sb800/lpc.c b/src/southbridge/amd/sb800/lpc.c index f862a97..87c09a2 100644 --- a/src/southbridge/amd/sb800/lpc.c +++ b/src/southbridge/amd/sb800/lpc.c @@ -74,7 +74,7 @@ static void lpc_init(device_t dev) byte |= 1 << 0 | 1 << 3; pci_write_config8(dev, 0xBB, byte);
- cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date(); }
static void sb800_lpc_read_resources(device_t dev)