blob: 6c39f812b91c7bf5bf4774ae6cd76251552da38e [file] [log] [blame]
Marc Jones3cc685f2014-12-29 21:31:44 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2014 The Chromium OS Authors. All rights reserved.
Timothy Pearson7b22d842015-08-28 19:52:05 -05005 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
Marc Jones3cc685f2014-12-29 21:31:44 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Marc Jones3cc685f2014-12-29 21:31:44 -070015 */
16
17#include <arch/acpi.h>
18#include <bcd.h>
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020019#include <stdint.h>
Kyösti Mälkkic36af7b2014-11-18 12:41:16 +020020#include <version.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +000021#include <console/console.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +000022#include <pc80/mc146818rtc.h>
Stefan Reinauerca374d42008-01-18 16:16:45 +000023#include <boot/coreboot_tables.h>
Marc Jones3cc685f2014-12-29 21:31:44 -070024#include <rtc.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +000025#include <string.h>
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -060026#include <cbfs.h>
27
28/* There's no way around this include guard. option_table.h is autogenerated */
Stefan Reinauer10ec0fe2010-09-25 10:40:47 +000029#if CONFIG_USE_OPTION_TABLE
30#include "option_table.h"
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -060031#else
32#define LB_CKS_RANGE_START 0
33#define LB_CKS_RANGE_END 0
34#define LB_CKS_LOC 0
Stefan Reinauer10ec0fe2010-09-25 10:40:47 +000035#endif
Eric Biederman8ca8d762003-04-22 19:02:15 +000036
Timothy Pearson7b22d842015-08-28 19:52:05 -050037#include <smp/spinlock.h>
38
39#if (defined(__PRE_RAM__) && \
40IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK))
41 #define LOCK_NVRAM_CBFS_SPINLOCK spin_lock(romstage_nvram_cbfs_lock());
42 #define UNLOCK_NVRAM_CBFS_SPINLOCK spin_unlock(romstage_nvram_cbfs_lock());
43#else
44 #define LOCK_NVRAM_CBFS_SPINLOCK
45 #define UNLOCK_NVRAM_CBFS_SPINLOCK
46#endif
Gabe Blackb3f08c62014-04-30 17:12:25 -070047
Gabe Black03abaee212014-04-30 21:31:44 -070048static void cmos_reset_date(void)
zbaoa1e6a9c2012-08-02 19:02:26 +080049{
Vincent Palatinfa90fd42012-08-07 17:03:40 -070050 /* Now setup a default date equals to the build date */
Marc Jones3cc685f2014-12-29 21:31:44 -070051 struct rtc_time time = {
52 .sec = 0,
53 .min = 0,
54 .hour = 1,
55 .mday = bcd2bin(coreboot_build_date.day),
56 .mon = bcd2bin(coreboot_build_date.month),
57 .year = (bcd2bin(coreboot_build_date.century) * 100) +
58 bcd2bin(coreboot_build_date.year),
59 .wday = bcd2bin(coreboot_build_date.weekday)
60 };
Gabe Black03abaee212014-04-30 21:31:44 -070061 rtc_set(&time);
zbaoa1e6a9c2012-08-02 19:02:26 +080062}
63
Gabe Blackb3f08c62014-04-30 17:12:25 -070064static int cmos_checksum_valid(int range_start, int range_end, int cks_loc)
Eric Biederman8ca8d762003-04-22 19:02:15 +000065{
Timothy Pearsonf20c6e82015-02-14 16:15:31 -060066 if (IS_ENABLED(CONFIG_STATIC_OPTION_TABLE))
67 return 1;
68
Eric Biederman8ca8d762003-04-22 19:02:15 +000069 int i;
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020070 u16 sum, old_sum;
Eric Biederman8ca8d762003-04-22 19:02:15 +000071 sum = 0;
Gabe Blackb3f08c62014-04-30 17:12:25 -070072 for (i = range_start; i <= range_end; i++)
Stefan Reinauer73d5daa2009-04-22 09:03:08 +000073 sum += cmos_read(i);
Gabe Blackb3f08c62014-04-30 17:12:25 -070074 old_sum = ((cmos_read(cks_loc) << 8) | cmos_read(cks_loc + 1)) &
75 0x0ffff;
Eric Biederman8ca8d762003-04-22 19:02:15 +000076 return sum == old_sum;
77}
78
Gabe Blackb3f08c62014-04-30 17:12:25 -070079static void cmos_set_checksum(int range_start, int range_end, int cks_loc)
Eric Biederman8ca8d762003-04-22 19:02:15 +000080{
81 int i;
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020082 u16 sum;
Eric Biederman8ca8d762003-04-22 19:02:15 +000083 sum = 0;
Gabe Blackb3f08c62014-04-30 17:12:25 -070084 for (i = range_start; i <= range_end; i++)
Stefan Reinauer73d5daa2009-04-22 09:03:08 +000085 sum += cmos_read(i);
Stefan Reinauer73d5daa2009-04-22 09:03:08 +000086 cmos_write(((sum >> 8) & 0x0ff), cks_loc);
Gabe Blackb3f08c62014-04-30 17:12:25 -070087 cmos_write(((sum >> 0) & 0x0ff), cks_loc + 1);
Eric Biederman8ca8d762003-04-22 19:02:15 +000088}
89
90#define RTC_CONTROL_DEFAULT (RTC_24H)
91#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
Li-Ta Lo84a80282005-01-07 02:42:53 +000092
Duncan Laurie4b1610d2012-09-05 10:52:44 -070093#ifndef __SMM__
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -060094void cmos_init(bool invalid)
Eric Biederman8ca8d762003-04-22 19:02:15 +000095{
Werner Zeha8b03da2015-02-09 08:17:40 +010096 bool cmos_invalid = invalid;
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -060097 bool checksum_invalid = false;
98 bool clear_cmos;
99 size_t i;
100 uint8_t x;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000101
Martin Roth7312c542014-05-12 21:52:54 -0600102#ifndef __PRE_RAM__
Stefan Reinauer86ce7f92013-05-28 12:37:08 -0700103 /*
104 * Avoid clearing pending interrupts and resetting the RTC control
105 * register in the resume path because the Linux kernel relies on
106 * this to know if it should restart the RTC timer queue if the wake
107 * was due to the RTC alarm.
108 */
Kyösti Mälkki9d9eb1e2014-06-20 05:38:07 +0300109 if (acpi_is_wakeup_s3())
Stefan Reinauer86ce7f92013-05-28 12:37:08 -0700110 return;
Martin Roth7312c542014-05-12 21:52:54 -0600111#endif /* __PRE_RAM__ */
Stefan Reinauer86ce7f92013-05-28 12:37:08 -0700112
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000113 printk(BIOS_DEBUG, "RTC Init\n");
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000114
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600115 if (IS_ENABLED(CONFIG_USE_OPTION_TABLE)) {
116 /* See if there has been a CMOS power problem. */
117 x = cmos_read(RTC_VALID);
118 cmos_invalid = !(x & RTC_VRT);
Eric Biederman8ca8d762003-04-22 19:02:15 +0000119
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600120 /* See if there is a CMOS checksum error */
121 checksum_invalid = !cmos_checksum_valid(PC_CKS_RANGE_START,
122 PC_CKS_RANGE_END, PC_CKS_LOC);
Eric Biederman8ca8d762003-04-22 19:02:15 +0000123
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600124 clear_cmos = false;
125 } else {
126 clear_cmos = true;
127 }
Vincent Palatinfa90fd42012-08-07 17:03:40 -0700128
Eric Biederman8ca8d762003-04-22 19:02:15 +0000129 if (invalid || cmos_invalid || checksum_invalid) {
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600130 if (clear_cmos) {
131 cmos_write(0, 0x01);
132 cmos_write(0, 0x03);
133 cmos_write(0, 0x05);
134 for (i = 10; i < 128; i++)
135 cmos_write(0, i);
136 }
Vincent Palatinfa90fd42012-08-07 17:03:40 -0700137
Gabe Blackb3f08c62014-04-30 17:12:25 -0700138 if (cmos_invalid)
Gabe Black03abaee212014-04-30 21:31:44 -0700139 cmos_reset_date();
Stefan Reinauerfeadfb72012-11-06 18:39:41 -0800140
Vincent Palatinfa90fd42012-08-07 17:03:40 -0700141 printk(BIOS_WARNING, "RTC:%s%s%s%s\n",
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600142 invalid ? " Clear requested":"",
143 cmos_invalid ? " Power Problem":"",
144 checksum_invalid ? " Checksum invalid":"",
145 clear_cmos ? " zeroing cmos":"");
Eric Biederman8ca8d762003-04-22 19:02:15 +0000146 }
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000147
148 /* Setup the real time clock */
Stefan Reinauer73d5daa2009-04-22 09:03:08 +0000149 cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL);
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000150 /* Setup the frequency it operates at */
Stefan Reinauer73d5daa2009-04-22 09:03:08 +0000151 cmos_write(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT);
Vincent Palatinfc1b9ee2012-08-07 16:05:14 -0700152 /* Ensure all reserved bits are 0 in register D */
153 cmos_write(RTC_VRT, RTC_VALID);
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000154
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600155 if (IS_ENABLED(CONFIG_USE_OPTION_TABLE)) {
156 /* See if there is a LB CMOS checksum error */
157 checksum_invalid = !cmos_checksum_valid(LB_CKS_RANGE_START,
158 LB_CKS_RANGE_END,LB_CKS_LOC);
159 if (checksum_invalid)
160 printk(BIOS_DEBUG, "RTC: coreboot checksum invalid\n");
Eric Biederman8ca8d762003-04-22 19:02:15 +0000161
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600162 /* Make certain we have a valid checksum */
163 cmos_set_checksum(PC_CKS_RANGE_START, PC_CKS_RANGE_END, PC_CKS_LOC);
164 }
Steven J. Magnani8cbc4752005-09-12 18:43:27 +0000165
Eric Biederman8ca8d762003-04-22 19:02:15 +0000166 /* Clear any pending interrupts */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700167 cmos_read(RTC_INTR_FLAGS);
Eric Biederman8ca8d762003-04-22 19:02:15 +0000168}
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600169#endif /* __SMM__ */
Eric Biederman8ca8d762003-04-22 19:02:15 +0000170
171
Gabe Blackb3f08c62014-04-30 17:12:25 -0700172/*
173 * This routine returns the value of the requested bits.
174 * input bit = bit count from the beginning of the cmos image
175 * length = number of bits to include in the value
176 * ret = a character pointer to where the value is to be returned
177 * returns CB_SUCCESS = successful, cb_err code if an error occurred
178 */
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600179static enum cb_err get_cmos_value(unsigned long bit, unsigned long length,
180 void *vret)
Eric Biederman8ca8d762003-04-22 19:02:15 +0000181{
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000182 unsigned char *ret;
183 unsigned long byte,byte_bit;
184 unsigned long i;
185 unsigned char uchar;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000186
Gabe Blackb3f08c62014-04-30 17:12:25 -0700187 /*
188 * The table is checked when it is built to ensure all
189 * values are valid.
190 */
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000191 ret = vret;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700192 byte = bit / 8; /* find the byte where the data starts */
193 byte_bit = bit % 8; /* find the bit in the byte where the data starts */
194 if (length < 9) { /* one byte or less */
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000195 uchar = cmos_read(byte); /* load the byte */
196 uchar >>= byte_bit; /* shift the bits to byte align */
197 /* clear unspecified bits */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700198 ret[0] = uchar & ((1 << length) - 1);
199 } else { /* more that one byte so transfer the whole bytes */
200 for (i = 0; length; i++, length -= 8, byte++) {
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000201 /* load the byte */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700202 ret[i] = cmos_read(byte);
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000203 }
204 }
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600205 return CB_SUCCESS;
Luc Verhaegen9ceae902009-06-03 10:47:19 +0000206}
207
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600208enum cb_err get_option(void *dest, const char *name)
Eric Biederman8ca8d762003-04-22 19:02:15 +0000209{
Eric Biederman8ca8d762003-04-22 19:02:15 +0000210 struct cmos_option_table *ct;
211 struct cmos_entries *ce;
212 size_t namelen;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700213 int found = 0;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000214
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600215 if (!IS_ENABLED(CONFIG_USE_OPTION_TABLE))
216 return CB_CMOS_OTABLE_DISABLED;
217
Timothy Pearson7b22d842015-08-28 19:52:05 -0500218 LOCK_NVRAM_CBFS_SPINLOCK
219
Eric Biederman8ca8d762003-04-22 19:02:15 +0000220 /* Figure out how long name is */
221 namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
Stefan Reinauer14e22772010-04-27 06:56:47 +0000222
Eric Biederman8ca8d762003-04-22 19:02:15 +0000223 /* find the requested entry record */
Aaron Durbin899d13d2015-05-15 23:39:23 -0500224 ct = cbfs_boot_map_with_leak("cmos_layout.bin",
225 CBFS_COMPONENT_CMOS_LAYOUT, NULL);
Patrick Georgicef3b892011-01-18 14:28:45 +0000226 if (!ct) {
Stefan Reinauer1babddb2011-10-14 15:22:52 -0700227 printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. "
228 "Options are disabled\n");
Timothy Pearson7b22d842015-08-28 19:52:05 -0500229
230 UNLOCK_NVRAM_CBFS_SPINLOCK
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600231 return CB_CMOS_LAYOUT_NOT_FOUND;
Patrick Georgicef3b892011-01-18 14:28:45 +0000232 }
Gabe Blackb3f08c62014-04-30 17:12:25 -0700233 ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length);
234 for(; ce->tag == LB_TAG_OPTION;
235 ce = (struct cmos_entries*)((unsigned char *)ce + ce->size)) {
Eric Biederman8ca8d762003-04-22 19:02:15 +0000236 if (memcmp(ce->name, name, namelen) == 0) {
Gabe Blackb3f08c62014-04-30 17:12:25 -0700237 found = 1;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000238 break;
239 }
240 }
Gabe Blackb3f08c62014-04-30 17:12:25 -0700241 if (!found) {
Stefan Reinauer8e726b72010-03-29 23:01:35 +0000242 printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
Timothy Pearson7b22d842015-08-28 19:52:05 -0500243 UNLOCK_NVRAM_CBFS_SPINLOCK
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600244 return CB_CMOS_OPTION_NOT_FOUND;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000245 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000246
Timothy Pearson7b22d842015-08-28 19:52:05 -0500247 if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS) {
248 UNLOCK_NVRAM_CBFS_SPINLOCK
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600249 return CB_CMOS_ACCESS_ERROR;
Timothy Pearson7b22d842015-08-28 19:52:05 -0500250 }
251 if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC)) {
252 UNLOCK_NVRAM_CBFS_SPINLOCK
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600253 return CB_CMOS_CHECKSUM_INVALID;
Timothy Pearson7b22d842015-08-28 19:52:05 -0500254 }
255 UNLOCK_NVRAM_CBFS_SPINLOCK
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600256 return CB_SUCCESS;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000257}
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200258
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600259static enum cb_err set_cmos_value(unsigned long bit, unsigned long length,
260 void *vret)
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200261{
262 unsigned char *ret;
263 unsigned long byte,byte_bit;
264 unsigned long i;
265 unsigned char uchar, mask;
266 unsigned int chksum_update_needed = 0;
267
268 ret = vret;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700269 byte = bit / 8; /* find the byte where the data starts */
270 byte_bit = bit % 8; /* find the bit where the data starts */
271 if (length <= 8) { /* one byte or less */
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200272 mask = (1 << length) - 1;
273 mask <<= byte_bit;
274
275 uchar = cmos_read(byte);
276 uchar &= ~mask;
277 uchar |= (ret[0] << byte_bit);
278 cmos_write(uchar, byte);
279 if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END)
280 chksum_update_needed = 1;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700281 } else { /* more that one byte so transfer the whole bytes */
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200282 if (byte_bit || length % 8)
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600283 return CB_ERR_ARG;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200284
Gabe Blackb3f08c62014-04-30 17:12:25 -0700285 for (i = 0; length; i++, length -= 8, byte++)
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200286 cmos_write(ret[i], byte);
Gabe Blackb3f08c62014-04-30 17:12:25 -0700287 if (byte >= LB_CKS_RANGE_START &&
288 byte <= LB_CKS_RANGE_END)
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200289 chksum_update_needed = 1;
290 }
291
292 if (chksum_update_needed) {
Gabe Blackb3f08c62014-04-30 17:12:25 -0700293 cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END,
294 LB_CKS_LOC);
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200295 }
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600296 return CB_SUCCESS;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200297}
298
299
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600300enum cb_err set_option(const char *name, void *value)
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200301{
302 struct cmos_option_table *ct;
303 struct cmos_entries *ce;
304 unsigned long length;
305 size_t namelen;
Gabe Blackb3f08c62014-04-30 17:12:25 -0700306 int found = 0;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200307
Alexandru Gagniucb5669ba2015-01-30 00:07:12 -0600308 if (!IS_ENABLED(CONFIG_USE_OPTION_TABLE))
309 return CB_CMOS_OTABLE_DISABLED;
310
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200311 /* Figure out how long name is */
312 namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
313
314 /* find the requested entry record */
Aaron Durbin899d13d2015-05-15 23:39:23 -0500315 ct = cbfs_boot_map_with_leak("cmos_layout.bin",
316 CBFS_COMPONENT_CMOS_LAYOUT, NULL);
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200317 if (!ct) {
Gabe Blackb3f08c62014-04-30 17:12:25 -0700318 printk(BIOS_ERR, "cmos_layout.bin could not be found. "
319 "Options are disabled\n");
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600320 return CB_CMOS_LAYOUT_NOT_FOUND;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200321 }
Gabe Blackb3f08c62014-04-30 17:12:25 -0700322 ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length);
323 for(; ce->tag == LB_TAG_OPTION;
324 ce = (struct cmos_entries*)((unsigned char *)ce + ce->size)) {
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200325 if (memcmp(ce->name, name, namelen) == 0) {
Gabe Blackb3f08c62014-04-30 17:12:25 -0700326 found = 1;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200327 break;
328 }
329 }
Gabe Blackb3f08c62014-04-30 17:12:25 -0700330 if (!found) {
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200331 printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600332 return CB_CMOS_OPTION_NOT_FOUND;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200333 }
334
335 length = ce->length;
336 if (ce->config == 's') {
337 length = MAX(strlen((const char *)value) * 8, ce->length - 8);
338 /* make sure the string is null terminated */
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600339 if (set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0})
340 != CB_SUCCESS)
341 return (CB_CMOS_ACCESS_ERROR);
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200342 }
343
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600344 if (set_cmos_value(ce->bit, length, value) != CB_SUCCESS)
345 return (CB_CMOS_ACCESS_ERROR);
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200346
Alexandru Gagniucd7134e02013-11-23 18:54:44 -0600347 return CB_SUCCESS;
Sven Schnelled29e5bb2011-06-06 15:58:54 +0200348}
zbaoa1e6a9c2012-08-02 19:02:26 +0800349
350/*
351 * If the CMOS is cleared, the rtc_reg has the invalid date. That
352 * hurts some OSes. Even if we don't set USE_OPTION_TABLE, we need
353 * to make sure the date is valid.
354 */
Gabe Black03abaee212014-04-30 21:31:44 -0700355void cmos_check_update_date()
zbaoa1e6a9c2012-08-02 19:02:26 +0800356{
357 u8 year, century;
358
Gabe Black03abaee212014-04-30 21:31:44 -0700359 /* Assume hardware always supports RTC_CLK_ALTCENTURY. */
360 century = cmos_read(RTC_CLK_ALTCENTURY);
Gabe Blackb3f08c62014-04-30 17:12:25 -0700361 year = cmos_read(RTC_CLK_YEAR);
zbaoa1e6a9c2012-08-02 19:02:26 +0800362
Gabe Blackb3f08c62014-04-30 17:12:25 -0700363 /*
364 * TODO: If century is 0xFF, 100% that the cmos is cleared.
365 * Other than that, so far rtc_year is the only entry to check
366 * if the date is valid.
367 */
368 if (century > 0x99 || year > 0x99) /* Invalid date */
Gabe Black03abaee212014-04-30 21:31:44 -0700369 cmos_reset_date();
Marc Jones3cc685f2014-12-29 21:31:44 -0700370}
371
Gabe Black03abaee212014-04-30 21:31:44 -0700372int rtc_set(const struct rtc_time *time){
Marc Jones3cc685f2014-12-29 21:31:44 -0700373 cmos_write(bin2bcd(time->sec), RTC_CLK_SECOND);
374 cmos_write(bin2bcd(time->min), RTC_CLK_MINUTE);
375 cmos_write(bin2bcd(time->hour), RTC_CLK_HOUR);
376 cmos_write(bin2bcd(time->mday), RTC_CLK_DAYOFMONTH);
377 cmos_write(bin2bcd(time->mon), RTC_CLK_MONTH);
378 cmos_write(bin2bcd(time->year % 100), RTC_CLK_YEAR);
Gabe Black03abaee212014-04-30 21:31:44 -0700379 /* Same assumption as above: We always have RTC_CLK_ALTCENTURY */
380 cmos_write(bin2bcd(time->year / 100), RTC_CLK_ALTCENTURY);
Marc Jones3cc685f2014-12-29 21:31:44 -0700381 cmos_write(bin2bcd(time->wday + 1), RTC_CLK_DAYOFWEEK);
382 return 0;
383}
384
Gabe Black03abaee212014-04-30 21:31:44 -0700385int rtc_get(struct rtc_time *time)
Marc Jones3cc685f2014-12-29 21:31:44 -0700386{
387 time->sec = bcd2bin(cmos_read(RTC_CLK_SECOND));
388 time->min = bcd2bin(cmos_read(RTC_CLK_MINUTE));
389 time->hour = bcd2bin(cmos_read(RTC_CLK_HOUR));
390 time->mday = bcd2bin(cmos_read(RTC_CLK_DAYOFMONTH));
391 time->mon = bcd2bin(cmos_read(RTC_CLK_MONTH));
392 time->year = bcd2bin(cmos_read(RTC_CLK_YEAR));
Gabe Black03abaee212014-04-30 21:31:44 -0700393 /* Same assumption as above: We always have RTC_CLK_ALTCENTURY */
394 time->year += bcd2bin(cmos_read(RTC_CLK_ALTCENTURY)) * 100;
Marc Jones3cc685f2014-12-29 21:31:44 -0700395 time->wday = bcd2bin(cmos_read(RTC_CLK_DAYOFWEEK)) - 1;
396 return 0;
zbaoa1e6a9c2012-08-02 19:02:26 +0800397}