1#include <console/console.h>
2#include <arch/io.h>
3#include <pc80/mc146818rtc.h>
4#include <boot/coreboot_tables.h>
5#include <string.h>
6
7
8
9#define RTC_REG_A 10
10#define RTC_REG_B 11
11#define RTC_REG_C 12
12#define RTC_REG_D 13
13
14
15
16
17
18#define RTC_FREQ_SELECT RTC_REG_A
19
20
21
22
23
24# define RTC_UIP 0x80
25# define RTC_DIV_CTL 0x70
26
27# define RTC_REF_CLCK_4MHZ 0x00
28# define RTC_REF_CLCK_1MHZ 0x10
29# define RTC_REF_CLCK_32KHZ 0x20
30
31# define RTC_DIV_RESET1 0x60
32# define RTC_DIV_RESET2 0x70
33
34# define RTC_RATE_SELECT 0x0F
35# define RTC_RATE_NONE 0x00
36# define RTC_RATE_32786HZ 0x01
37# define RTC_RATE_16384HZ 0x02
38# define RTC_RATE_8192HZ 0x03
39# define RTC_RATE_4096HZ 0x04
40# define RTC_RATE_2048HZ 0x05
41# define RTC_RATE_1024HZ 0x06
42# define RTC_RATE_512HZ 0x07
43# define RTC_RATE_256HZ 0x08
44# define RTC_RATE_128HZ 0x09
45# define RTC_RATE_64HZ 0x0a
46# define RTC_RATE_32HZ 0x0b
47# define RTC_RATE_16HZ 0x0c
48# define RTC_RATE_8HZ 0x0d
49# define RTC_RATE_4HZ 0x0e
50# define RTC_RATE_2HZ 0x0f
51
52
53#define RTC_CONTROL RTC_REG_B
54# define RTC_SET 0x80
55# define RTC_PIE 0x40
56# define RTC_AIE 0x20
57# define RTC_UIE 0x10
58# define RTC_SQWE 0x08
59# define RTC_DM_BINARY 0x04
60# define RTC_24H 0x02
61# define RTC_DST_EN 0x01
62
63
64#define RTC_INTR_FLAGS RTC_REG_C
65
66# define RTC_IRQF 0x80
67# define RTC_PF 0x40
68# define RTC_AF 0x20
69# define RTC_UF 0x10
70
71
72#define RTC_VALID RTC_REG_D
73# define RTC_VRT 0x80
74
75
76static inline unsigned char cmos_read(unsigned char addr)
77{
78 int offs = 0;
79 if (addr >= 128) {
80 offs = 2;
81 addr -= 128;
82 }
83 outb(addr, RTC_BASE_PORT + offs + 0);
84 return inb(RTC_BASE_PORT + offs + 1);
85}
86
87static inline void cmos_write(unsigned char val, unsigned char addr)
88{
89 int offs = 0;
90 if (addr >= 128) {
91 offs = 2;
92 addr -= 128;
93 }
94 outb(addr, RTC_BASE_PORT + offs + 0);
95 outb(val, RTC_BASE_PORT + offs + 1);
96}
97
98static int rtc_checksum_valid(int range_start, int range_end, int cks_loc)
99{
100 int i;
101 unsigned sum, old_sum;
102 sum = 0;
103 for(i = range_start; i <= range_end; i++) {
104 sum += cmos_read(i);
105 }
106 sum = (~sum)&0x0ffff;
107 old_sum = ((cmos_read(cks_loc)<<8) | cmos_read(cks_loc+1))&0x0ffff;
108 return sum == old_sum;
109}
110
111static void rtc_set_checksum(int range_start, int range_end, int cks_loc)
112{
113 int i;
114 unsigned sum;
115 sum = 0;
116 for(i = range_start; i <= range_end; i++) {
117 sum += cmos_read(i);
118 }
119 sum = ~(sum & 0x0ffff);
120 cmos_write(((sum >> 8) & 0x0ff), cks_loc);
121 cmos_write(((sum >> 0) & 0x0ff), cks_loc+1);
122}
123
124#define RTC_CONTROL_DEFAULT (RTC_24H)
125#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
126
127#if 0
128#undef RTC_CONTROL_DEFAULT
129#undef RTC_FREQ_SELECT_DEFAULT
130#define RTC_CONTROL_DEFAULT (RTC_SQWE | RTC_24H)
131#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
132#endif
133
134void rtc_init(int invalid)
135{
136#if CONFIG_HAVE_OPTION_TABLE
137 unsigned char x;
138 int cmos_invalid, checksum_invalid;
139#endif
140
141 printk_debug("RTC Init\n");
142
143#if CONFIG_HAVE_OPTION_TABLE
144
145 x = cmos_read(RTC_VALID);
146 cmos_invalid = !(x & RTC_VRT);
147
148
149 checksum_invalid = !rtc_checksum_valid(PC_CKS_RANGE_START,
150 PC_CKS_RANGE_END,PC_CKS_LOC);
151
152 if (invalid || cmos_invalid || checksum_invalid) {
153 printk_warning("RTC:%s%s%s zeroing cmos\n",
154 invalid?" Clear requested":"",
155 cmos_invalid?" Power Problem":"",
156 checksum_invalid?" Checksum invalid":"");
157#if 0
158 cmos_write(0, 0x01);
159 cmos_write(0, 0x03);
160 cmos_write(0, 0x05);
161 for(i = 10; i < 48; i++) {
162 cmos_write(0, i);
163 }
164
165 if (cmos_invalid) {
166
167 cmos_write(0, 0x00);
168 cmos_write(0, 0x02);
169 cmos_write(1, 0x04);
170 cmos_write(7, 0x06);
171 cmos_write(1, 0x07);
172 cmos_write(1, 0x08);
173 cmos_write(0, 0x09);
174 }
175#endif
176 }
177#endif
178
179
180 cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL);
181
182 cmos_write(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT);
183
184#if CONFIG_HAVE_OPTION_TABLE
185
186 checksum_invalid = !rtc_checksum_valid(CONFIG_LB_CKS_RANGE_START,
187 CONFIG_LB_CKS_RANGE_END,CONFIG_LB_CKS_LOC);
188 if(checksum_invalid)
189 printk_debug("Invalid CMOS LB checksum\n");
190
191
192 rtc_set_checksum(PC_CKS_RANGE_START,
193 PC_CKS_RANGE_END,PC_CKS_LOC);
194#endif
195
196
197 (void) cmos_read(RTC_INTR_FLAGS);
198}
199
200
201#if CONFIG_USE_OPTION_TABLE == 1
202
203
204
205
206
207
208
209static int get_cmos_value(unsigned long bit, unsigned long length, void *vret)
210{
211 unsigned char *ret;
212 unsigned long byte,byte_bit;
213 unsigned long i;
214 unsigned char uchar;
215
216
217
218 ret = vret;
219 byte=bit/8;
220 byte_bit=bit%8;
221 if(length<9) {
222 uchar = cmos_read(byte);
223 uchar >>= byte_bit;
224
225 ret[0] = uchar & ((1 << length) -1);
226 }
227 else {
228 for(i=0;length;i++,length-=8,byte++) {
229
230 ret[i]=cmos_read(byte);
231 }
232 }
233 return 0;
234}
235
236int get_option(void *dest, const char *name)
237{
238 extern struct cmos_option_table option_table;
239 struct cmos_option_table *ct;
240 struct cmos_entries *ce;
241 size_t namelen;
242 int found=0;
243
244
245 namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
246
247
248 ct=&option_table;
249 ce=(struct cmos_entries*)((unsigned char *)ct + ct->header_length);
250 for(;ce->tag==LB_TAG_OPTION;
251 ce=(struct cmos_entries*)((unsigned char *)ce + ce->size)) {
252 if (memcmp(ce->name, name, namelen) == 0) {
253 found=1;
254 break;
255 }
256 }
257 if(!found) {
258 printk_debug("WARNING: No cmos option '%s'\n", name);
259 return(-2);
260 }
261
262 if(get_cmos_value(ce->bit, ce->length, dest))
263 return(-3);
264 if(!rtc_checksum_valid(CONFIG_LB_CKS_RANGE_START,
265 CONFIG_LB_CKS_RANGE_END,CONFIG_LB_CKS_LOC))
266 return(-4);
267 return(0);
268}
269#endif
270