From 9e0fd75142d29afe34f6c6b9ce0099f478ca5a93 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Wed, 30 Apr 2014 21:31:44 -0700 Subject: [PATCH] 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 Change-Id: Ifa807898e583254e57167fd44932ea86627a02ee Signed-off-by: Gabe Black Reviewed-on: https://chromium-review.googlesource.com/197795 Reviewed-by: David Hendricks Tested-by: Gabe Black Commit-Queue: Gabe Black --- src/drivers/pc80/Kconfig | 15 ++ src/drivers/pc80/Makefile.inc | 11 +- src/drivers/pc80/mc146818.c | 323 ++++++++++++++++++++++ src/drivers/pc80/mc146818rtc.c | 360 +++---------------------- 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/sb600/lpc.c | 2 +- src/southbridge/amd/sb700/lpc.c | 2 +- src/southbridge/amd/sb800/lpc.c | 2 +- 14 files changed, 471 insertions(+), 329 deletions(-) create mode 100644 src/drivers/pc80/mc146818.c create mode 100644 src/include/bcd.h create mode 100644 src/include/rtc.h diff --git a/src/drivers/pc80/Kconfig b/src/drivers/pc80/Kconfig index d9ff99151c..99e66d33e0 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 9985433265..2b80d00f06 100644 --- a/src/drivers/pc80/Makefile.inc +++ b/src/drivers/pc80/Makefile.inc @@ -1,19 +1,26 @@ -ramstage-y += mc146818rtc.c +ramstage-$(CONFIG_DRIVERS_MC146818_RTC) += mc146818rtc.c +ramstage-$(CONFIG_DRIVERS_MC146818_CMOS) += mc146818.c ramstage-y += isa-dma.c ramstage-y += i8254.c ramstage-y += i8259.c ramstage-$(CONFIG_UDELAY_IO) += udelay_io.c ramstage-y += keyboard.c -romstage-$(CONFIG_LPC_TPM) += tpm.c +romstage-$(CONFIG_LPC_TPM) += tpm.c +ifeq ($(CONFIG_DRIVERS_MC146818_CMOS),y) romstage-$(CONFIG_USE_OPTION_TABLE) += mc146818rtc_early.c +endif + subdirs-y += vga 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 $(obj)/drivers/pc80/mc146818rtc.ramstage.o : $(obj)/build.h +$(obj)/drivers/pc80/mc146818.ramstage.o : $(obj)/build.h $(obj)/drivers/pc80/mc146818rtc.smm.o : $(obj)/build.h +$(obj)/drivers/pc80/mc146818.smm.o : $(obj)/build.h diff --git a/src/drivers/pc80/mc146818.c b/src/drivers/pc80/mc146818.c new file mode 100644 index 0000000000..1500191509 --- /dev/null +++ b/src/drivers/pc80/mc146818.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#if CONFIG_USE_OPTION_TABLE +#include "option_table.h" +#include +#endif +#if CONFIG_HAVE_ACPI_RESUME +#include +#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_slp_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); + 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); + 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 ae97379262..7954af41ba 100644 --- a/src/drivers/pc80/mc146818rtc.c +++ b/src/drivers/pc80/mc146818rtc.c @@ -1,325 +1,53 @@ -#include -#include -#include +/* + * 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 + */ + +#include #include -#include -#include -#if CONFIG_USE_OPTION_TABLE -#include "option_table.h" -#include -#endif -#if CONFIG_HAVE_ACPI_RESUME -#include -#endif +#include - -static void cmos_update_date(u8 has_century) +int rtc_set(const struct rtc_time *time) { - /* 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_WEEKDAY_BCD + 1, RTC_CLK_DAYOFWEEK); - cmos_write(COREBOOT_BUILD_DAY_BCD, RTC_CLK_DAYOFMONTH); - cmos_write(COREBOOT_BUILD_MONTH_BCD, RTC_CLK_MONTH); - cmos_write(COREBOOT_BUILD_YEAR_BCD, RTC_CLK_YEAR); - if (has_century) cmos_write(0x20, 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 - -#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_slp_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_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 - * 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); - } - } + 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 get_option(void *dest, const char *name) +int rtc_get(struct rtc_time *time) { - 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); - 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); - } + 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; } - - -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); - 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(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); -} diff --git a/src/include/bcd.h b/src/include/bcd.h new file mode 100644 index 0000000000..a0850271b8 --- /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 + +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 69841dd27a..549368a604 100644 --- a/src/include/pc80/mc146818rtc.h +++ b/src/include/pc80/mc146818rtc.h @@ -91,9 +91,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 @@ -171,7 +168,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 int set_option(const char *name, void *val); int get_option(void *dest, const char *name); diff --git a/src/include/rtc.h b/src/include/rtc.h new file mode 100644 index 0000000000..ba9a9c351d --- /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 b2f37bd132..4a504cd5b8 100644 --- a/src/southbridge/amd/agesa/hudson/lpc.c +++ b/src/southbridge/amd/agesa/hudson/lpc.c @@ -68,7 +68,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 bbb6242c63..7b9cf61ad1 100644 --- a/src/southbridge/amd/cimx/sb700/late.c +++ b/src/southbridge/amd/cimx/sb700/late.c @@ -77,7 +77,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 68fa5e553a..bf1c71f2cd 100644 --- a/src/southbridge/amd/cimx/sb800/late.c +++ b/src/southbridge/amd/cimx/sb800/late.c @@ -126,7 +126,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 5b78490db9..1951ad5807 100644 --- a/src/southbridge/amd/cimx/sb900/late.c +++ b/src/southbridge/amd/cimx/sb900/late.c @@ -99,7 +99,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/sb600/lpc.c b/src/southbridge/amd/sb600/lpc.c index 517ab59af3..fc96b83a94 100644 --- a/src/southbridge/amd/sb600/lpc.c +++ b/src/southbridge/amd/sb600/lpc.c @@ -59,7 +59,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 2a1ffa8f69..54181fdbdc 100644 --- a/src/southbridge/amd/sb700/lpc.c +++ b/src/southbridge/amd/sb700/lpc.c @@ -81,7 +81,7 @@ static void lpc_init(device_t dev) } #endif - cmos_check_update_date(RTC_HAS_ALTCENTURY); + cmos_check_update_date(); } void set_cbmem_toc(struct cbmem_entry *toc) diff --git a/src/southbridge/amd/sb800/lpc.c b/src/southbridge/amd/sb800/lpc.c index 7a4dd831da..989139f125 100644 --- a/src/southbridge/amd/sb800/lpc.c +++ b/src/southbridge/amd/sb800/lpc.c @@ -67,7 +67,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)