blob: 9670e9ce6b1a42dff4b6a9e8e69b9b87ab3cfa3c [file] [log] [blame]
Stefan Reinauerea5c2b62011-10-27 18:42:53 +02001#include <stdint.h>
Vincent Palatinfa90fd42012-08-07 17:03:40 -07002#include <build.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +00003#include <console/console.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +00004#include <pc80/mc146818rtc.h>
Stefan Reinauerca374d42008-01-18 16:16:45 +00005#include <boot/coreboot_tables.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +00006#include <string.h>
Stefan Reinauer10ec0fe2010-09-25 10:40:47 +00007#if CONFIG_USE_OPTION_TABLE
8#include "option_table.h"
Patrick Georgi24479372011-01-18 13:56:36 +00009#include <cbfs.h>
Stefan Reinauer10ec0fe2010-09-25 10:40:47 +000010#endif
Duncan Laurie4b1610d2012-09-05 10:52:44 -070011#include <arch/acpi.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +000012
Gabe Blackb3f08c62014-04-30 17:12:25 -070013
14static void cmos_update_date(u8 has_century)
zbaoa1e6a9c2012-08-02 19:02:26 +080015{
Vincent Palatinfa90fd42012-08-07 17:03:40 -070016 /* Now setup a default date equals to the build date */
zbaoa1e6a9c2012-08-02 19:02:26 +080017 cmos_write(0, RTC_CLK_SECOND);
18 cmos_write(0, RTC_CLK_MINUTE);
19 cmos_write(1, RTC_CLK_HOUR);
Zheng Bao0e6d0ed2012-11-09 19:55:04 +080020 cmos_write(COREBOOT_BUILD_WEEKDAY_BCD + 1, RTC_CLK_DAYOFWEEK);
21 cmos_write(COREBOOT_BUILD_DAY_BCD, RTC_CLK_DAYOFMONTH);
Dave Frodin832452a2013-02-18 08:34:49 -070022 cmos_write(COREBOOT_BUILD_MONTH_BCD, RTC_CLK_MONTH);
Zheng Bao0e6d0ed2012-11-09 19:55:04 +080023 cmos_write(COREBOOT_BUILD_YEAR_BCD, RTC_CLK_YEAR);
zbaoa1e6a9c2012-08-02 19:02:26 +080024 if (has_century) cmos_write(0x20, RTC_CLK_ALTCENTURY);
25}
26
Edwin Beasanteb50c7d2010-07-06 21:05:04 +000027#if CONFIG_USE_OPTION_TABLE
Gabe Blackb3f08c62014-04-30 17:12:25 -070028static int cmos_checksum_valid(int range_start, int range_end, int cks_loc)
Eric Biederman8ca8d762003-04-22 19:02:15 +000029{
30 int i;
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020031 u16 sum, old_sum;
Eric Biederman8ca8d762003-04-22 19:02:15 +000032 sum = 0;
Gabe Blackb3f08c62014-04-30 17:12:25 -070033 for (i = range_start; i <= range_end; i++)
Stefan Reinauer73d5daa2009-04-22 09:03:08 +000034 sum += cmos_read(i);
Gabe Blackb3f08c62014-04-30 17:12:25 -070035 old_sum = ((cmos_read(cks_loc) << 8) | cmos_read(cks_loc + 1)) &
36 0x0ffff;
Eric Biederman8ca8d762003-04-22 19:02:15 +000037 return sum == old_sum;
38}
39
Gabe Blackb3f08c62014-04-30 17:12:25 -070040static void cmos_set_checksum(int range_start, int range_end, int cks_loc)
Eric Biederman8ca8d762003-04-22 19:02:15 +000041{
42 int i;
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020043 u16 sum;
Eric Biederman8ca8d762003-04-22 19:02:15 +000044 sum = 0;
Gabe Blackb3f08c62014-04-30 17:12:25 -070045 for (i = range_start; i <= range_end; i++)
Stefan Reinauer73d5daa2009-04-22 09:03:08 +000046 sum += cmos_read(i);
Stefan Reinauer73d5daa2009-04-22 09:03:08 +000047 cmos_write(((sum >> 8) & 0x0ff), cks_loc);
Gabe Blackb3f08c62014-04-30 17:12:25 -070048 cmos_write(((sum >> 0) & 0x0ff), cks_loc + 1);
Eric Biederman8ca8d762003-04-22 19:02:15 +000049}
Stefan Reinauer2549d522010-03-17 03:44:45 +000050#endif
Eric Biederman8ca8d762003-04-22 19:02:15 +000051
Stefan Reinauer2fbbea02010-01-16 13:27:39 +000052#if CONFIG_ARCH_X86
Eric Biederman8ca8d762003-04-22 19:02:15 +000053#define RTC_CONTROL_DEFAULT (RTC_24H)
54#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
Stefan Reinauer2fbbea02010-01-16 13:27:39 +000055#else
56#if CONFIG_ARCH_ALPHA
Eric Biederman8ca8d762003-04-22 19:02:15 +000057#define RTC_CONTROL_DEFAULT (RTC_SQWE | RTC_24H)
58#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
59#endif
Stefan Reinauer2fbbea02010-01-16 13:27:39 +000060#endif
Li-Ta Lo84a80282005-01-07 02:42:53 +000061
Duncan Laurie4b1610d2012-09-05 10:52:44 -070062#ifndef __SMM__
Gabe Blackb3f08c62014-04-30 17:12:25 -070063void cmos_init(int invalid)
Eric Biederman8ca8d762003-04-22 19:02:15 +000064{
Vincent Palatinfa90fd42012-08-07 17:03:40 -070065 int cmos_invalid = 0;
66 int checksum_invalid = 0;
Edwin Beasanteb50c7d2010-07-06 21:05:04 +000067#if CONFIG_USE_OPTION_TABLE
Eric Biederman8ca8d762003-04-22 19:02:15 +000068 unsigned char x;
Maciej Pijankaea921852009-10-27 14:29:29 +000069#endif
Eric Biederman8ca8d762003-04-22 19:02:15 +000070
Martin Roth7312c542014-05-12 21:52:54 -060071#ifndef __PRE_RAM__
Stefan Reinauer86ce7f92013-05-28 12:37:08 -070072 /*
73 * Avoid clearing pending interrupts and resetting the RTC control
74 * register in the resume path because the Linux kernel relies on
75 * this to know if it should restart the RTC timer queue if the wake
76 * was due to the RTC alarm.
77 */
Kyösti Mälkki9d9eb1e2014-06-20 05:38:07 +030078 if (acpi_is_wakeup_s3())
Stefan Reinauer86ce7f92013-05-28 12:37:08 -070079 return;
Martin Roth7312c542014-05-12 21:52:54 -060080#endif /* __PRE_RAM__ */
Stefan Reinauer86ce7f92013-05-28 12:37:08 -070081
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000082 printk(BIOS_DEBUG, "RTC Init\n");
Steven J. Magnani8cbc4752005-09-12 18:43:27 +000083
Edwin Beasanteb50c7d2010-07-06 21:05:04 +000084#if CONFIG_USE_OPTION_TABLE
Eric Biederman8ca8d762003-04-22 19:02:15 +000085 /* See if there has been a CMOS power problem. */
Stefan Reinauer73d5daa2009-04-22 09:03:08 +000086 x = cmos_read(RTC_VALID);
Eric Biederman8ca8d762003-04-22 19:02:15 +000087 cmos_invalid = !(x & RTC_VRT);
88
89 /* See if there is a CMOS checksum error */
Gabe Blackb3f08c62014-04-30 17:12:25 -070090 checksum_invalid = !cmos_checksum_valid(PC_CKS_RANGE_START,
Eric Biederman8ca8d762003-04-22 19:02:15 +000091 PC_CKS_RANGE_END,PC_CKS_LOC);
92
Stefan Reinauer1babddb2011-10-14 15:22:52 -070093#define CLEAR_CMOS 0
Vincent Palatinfa90fd42012-08-07 17:03:40 -070094#else
95#define CLEAR_CMOS 1
96#endif
97
Eric Biederman8ca8d762003-04-22 19:02:15 +000098 if (invalid || cmos_invalid || checksum_invalid) {
Stefan Reinauer1babddb2011-10-14 15:22:52 -070099#if CLEAR_CMOS
Vincent Palatinfa90fd42012-08-07 17:03:40 -0700100 int i;
101
Stefan Reinauer73d5daa2009-04-22 09:03:08 +0000102 cmos_write(0, 0x01);
103 cmos_write(0, 0x03);
104 cmos_write(0, 0x05);
Gabe Blackb3f08c62014-04-30 17:12:25 -0700105 for (i = 10; i < 128; i++)
Stefan Reinauer73d5daa2009-04-22 09:03:08 +0000106 cmos_write(0, i);
Stefan Reinauerfeadfb72012-11-06 18:39:41 -0800107#endif
Gabe Blackb3f08c62014-04-30 17:12:25 -0700108 if (cmos_invalid)
109 cmos_update_date(RTC_HAS_NO_ALTCENTURY);
Stefan Reinauerfeadfb72012-11-06 18:39:41 -0800110
Vincent Palatinfa90fd42012-08-07 17:03:40 -0700111 printk(BIOS_WARNING, "RTC:%s%s%s%s\n",
112 invalid?" Clear requested":"",
113 cmos_invalid?" Power Problem":"",
114 checksum_invalid?" Checksum invalid":"",
115 CLEAR_CMOS?" zeroing cmos":"");
Eric Biederman8ca8d762003-04-22 19:02:15 +0000116 }
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000117
118 /* Setup the real time clock */
Stefan Reinauer73d5daa2009-04-22 09:03:08 +0000119 cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL);
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000120 /* Setup the frequency it operates at */
Stefan Reinauer73d5daa2009-04-22 09:03:08 +0000121 cmos_write(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT);
Vincent Palatinfc1b9ee2012-08-07 16:05:14 -0700122 /* Ensure all reserved bits are 0 in register D */
123 cmos_write(RTC_VRT, RTC_VALID);
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000124
Edwin Beasanteb50c7d2010-07-06 21:05:04 +0000125#if CONFIG_USE_OPTION_TABLE
Eric Biederman8ca8d762003-04-22 19:02:15 +0000126 /* See if there is a LB CMOS checksum error */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700127 checksum_invalid = !cmos_checksum_valid(LB_CKS_RANGE_START,
Stefan Reinauerb5828d72010-03-29 17:14:28 +0000128 LB_CKS_RANGE_END,LB_CKS_LOC);
Gabe Blackb3f08c62014-04-30 17:12:25 -0700129 if (checksum_invalid)
Stefan Reinauer1babddb2011-10-14 15:22:52 -0700130 printk(BIOS_DEBUG, "RTC: coreboot checksum invalid\n");
Eric Biederman8ca8d762003-04-22 19:02:15 +0000131
Eric Biederman8ca8d762003-04-22 19:02:15 +0000132 /* Make certain we have a valid checksum */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700133 cmos_set_checksum(PC_CKS_RANGE_START, PC_CKS_RANGE_END, PC_CKS_LOC);
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000134#endif
135
Eric Biederman8ca8d762003-04-22 19:02:15 +0000136 /* Clear any pending interrupts */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700137 cmos_read(RTC_INTR_FLAGS);
Eric Biederman8ca8d762003-04-22 19:02:15 +0000138}
Duncan Laurie4b1610d2012-09-05 10:52:44 -0700139#endif
Eric Biederman8ca8d762003-04-22 19:02:15 +0000140
141
Edwin Beasanteb50c7d2010-07-06 21:05:04 +0000142#if CONFIG_USE_OPTION_TABLE
Gabe Blackb3f08c62014-04-30 17:12:25 -0700143/*
144 * This routine returns the value of the requested bits.
145 * input bit = bit count from the beginning of the cmos image
146 * length = number of bits to include in the value
147 * ret = a character pointer to where the value is to be returned
148 * returns CB_SUCCESS = successful, cb_err code if an error occurred
149 */
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600150static enum cb_err get_cmos_value(unsigned long bit, unsigned long length,
151 void *vret)
Eric Biederman8ca8d762003-04-22 19:02:15 +0000152{
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000153 unsigned char *ret;
154 unsigned long byte,byte_bit;
155 unsigned long i;
156 unsigned char uchar;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000157
Gabe Blackb3f08c62014-04-30 17:12:25 -0700158 /*
159 * The table is checked when it is built to ensure all
160 * values are valid.
161 */
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000162 ret = vret;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700163 byte = bit / 8; /* find the byte where the data starts */
164 byte_bit = bit % 8; /* find the bit in the byte where the data starts */
165 if (length < 9) { /* one byte or less */
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000166 uchar = cmos_read(byte); /* load the byte */
167 uchar >>= byte_bit; /* shift the bits to byte align */
168 /* clear unspecified bits */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700169 ret[0] = uchar & ((1 << length) - 1);
170 } else { /* more that one byte so transfer the whole bytes */
171 for (i = 0; length; i++, length -= 8, byte++) {
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000172 /* load the byte */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700173 ret[i] = cmos_read(byte);
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000174 }
175 }
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600176 return CB_SUCCESS;
Luc Verhaegen9ceae902009-06-03 10:47:19 +0000177}
178
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600179enum cb_err get_option(void *dest, const char *name)
Eric Biederman8ca8d762003-04-22 19:02:15 +0000180{
Eric Biederman8ca8d762003-04-22 19:02:15 +0000181 struct cmos_option_table *ct;
182 struct cmos_entries *ce;
183 size_t namelen;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700184 int found = 0;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000185
186 /* Figure out how long name is */
187 namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
Stefan Reinauer14e22772010-04-27 06:56:47 +0000188
Eric Biederman8ca8d762003-04-22 19:02:15 +0000189 /* find the requested entry record */
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800190 ct = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, "cmos_layout.bin",
Vladimir Serbinenko0af61b62014-01-12 13:45:52 +0100191 CBFS_COMPONENT_CMOS_LAYOUT, NULL);
Patrick Georgicef3b892011-01-18 14:28:45 +0000192 if (!ct) {
Stefan Reinauer1babddb2011-10-14 15:22:52 -0700193 printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. "
194 "Options are disabled\n");
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600195 return CB_CMOS_LAYOUT_NOT_FOUND;
Patrick Georgicef3b892011-01-18 14:28:45 +0000196 }
Gabe Blackb3f08c62014-04-30 17:12:25 -0700197 ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length);
198 for(; ce->tag == LB_TAG_OPTION;
199 ce = (struct cmos_entries*)((unsigned char *)ce + ce->size)) {
Eric Biederman8ca8d762003-04-22 19:02:15 +0000200 if (memcmp(ce->name, name, namelen) == 0) {
Gabe Blackb3f08c62014-04-30 17:12:25 -0700201 found = 1;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000202 break;
203 }
204 }
Gabe Blackb3f08c62014-04-30 17:12:25 -0700205 if (!found) {
Stefan Reinauer8e726b72010-03-29 23:01:35 +0000206 printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600207 return CB_CMOS_OPTION_NOT_FOUND;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000208 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000209
Gabe Blackb3f08c62014-04-30 17:12:25 -0700210 if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS)
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600211 return CB_CMOS_ACCESS_ERROR;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700212 if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC))
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600213 return CB_CMOS_CHECKSUM_INVALID;
214 return CB_SUCCESS;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000215}
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200216
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600217static enum cb_err set_cmos_value(unsigned long bit, unsigned long length,
218 void *vret)
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200219{
220 unsigned char *ret;
221 unsigned long byte,byte_bit;
222 unsigned long i;
223 unsigned char uchar, mask;
224 unsigned int chksum_update_needed = 0;
225
226 ret = vret;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700227 byte = bit / 8; /* find the byte where the data starts */
228 byte_bit = bit % 8; /* find the bit where the data starts */
229 if (length <= 8) { /* one byte or less */
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200230 mask = (1 << length) - 1;
231 mask <<= byte_bit;
232
233 uchar = cmos_read(byte);
234 uchar &= ~mask;
235 uchar |= (ret[0] << byte_bit);
236 cmos_write(uchar, byte);
237 if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END)
238 chksum_update_needed = 1;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700239 } else { /* more that one byte so transfer the whole bytes */
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200240 if (byte_bit || length % 8)
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600241 return CB_ERR_ARG;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200242
Gabe Blackb3f08c62014-04-30 17:12:25 -0700243 for (i = 0; length; i++, length -= 8, byte++)
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200244 cmos_write(ret[i], byte);
Gabe Blackb3f08c62014-04-30 17:12:25 -0700245 if (byte >= LB_CKS_RANGE_START &&
246 byte <= LB_CKS_RANGE_END)
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200247 chksum_update_needed = 1;
248 }
249
250 if (chksum_update_needed) {
Gabe Blackb3f08c62014-04-30 17:12:25 -0700251 cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END,
252 LB_CKS_LOC);
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200253 }
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600254 return CB_SUCCESS;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200255}
256
257
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600258enum cb_err set_option(const char *name, void *value)
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200259{
260 struct cmos_option_table *ct;
261 struct cmos_entries *ce;
262 unsigned long length;
263 size_t namelen;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700264 int found = 0;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200265
266 /* Figure out how long name is */
267 namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
268
269 /* find the requested entry record */
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800270 ct = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, "cmos_layout.bin",
Vladimir Serbinenko0af61b62014-01-12 13:45:52 +0100271 CBFS_COMPONENT_CMOS_LAYOUT, NULL);
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200272 if (!ct) {
Gabe Blackb3f08c62014-04-30 17:12:25 -0700273 printk(BIOS_ERR, "cmos_layout.bin could not be found. "
274 "Options are disabled\n");
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600275 return CB_CMOS_LAYOUT_NOT_FOUND;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200276 }
Gabe Blackb3f08c62014-04-30 17:12:25 -0700277 ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length);
278 for(; ce->tag == LB_TAG_OPTION;
279 ce = (struct cmos_entries*)((unsigned char *)ce + ce->size)) {
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200280 if (memcmp(ce->name, name, namelen) == 0) {
Gabe Blackb3f08c62014-04-30 17:12:25 -0700281 found = 1;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200282 break;
283 }
284 }
Gabe Blackb3f08c62014-04-30 17:12:25 -0700285 if (!found) {
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200286 printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600287 return CB_CMOS_OPTION_NOT_FOUND;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200288 }
289
290 length = ce->length;
291 if (ce->config == 's') {
292 length = MAX(strlen((const char *)value) * 8, ce->length - 8);
293 /* make sure the string is null terminated */
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600294 if (set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0})
295 != CB_SUCCESS)
296 return (CB_CMOS_ACCESS_ERROR);
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200297 }
298
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600299 if (set_cmos_value(ce->bit, length, value) != CB_SUCCESS)
300 return (CB_CMOS_ACCESS_ERROR);
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200301
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600302 return CB_SUCCESS;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200303}
Stefan Reinauer08670622009-06-30 15:17:49 +0000304#endif /* CONFIG_USE_OPTION_TABLE */
zbaoa1e6a9c2012-08-02 19:02:26 +0800305
306/*
307 * If the CMOS is cleared, the rtc_reg has the invalid date. That
308 * hurts some OSes. Even if we don't set USE_OPTION_TABLE, we need
309 * to make sure the date is valid.
310 */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700311void cmos_check_update_date(u8 has_century)
zbaoa1e6a9c2012-08-02 19:02:26 +0800312{
313 u8 year, century;
314
Gabe Blackb3f08c62014-04-30 17:12:25 -0700315 /* Note: Need to check if the hardware supports RTC_CLK_ALTCENTURY. */
316 century = has_century ? cmos_read(RTC_CLK_ALTCENTURY) : 0;
317 year = cmos_read(RTC_CLK_YEAR);
zbaoa1e6a9c2012-08-02 19:02:26 +0800318
Gabe Blackb3f08c62014-04-30 17:12:25 -0700319 /*
320 * TODO: If century is 0xFF, 100% that the cmos is cleared.
321 * Other than that, so far rtc_year is the only entry to check
322 * if the date is valid.
323 */
324 if (century > 0x99 || year > 0x99) /* Invalid date */
325 cmos_update_date(has_century);
zbaoa1e6a9c2012-08-02 19:02:26 +0800326}