Factor out common mptable code to mptable_init().
[coreboot.git] / src / pc80 / mc146818rtc.c
1 #include <console/console.h>
2 #include <pc80/mc146818rtc.h>
3 #include <boot/coreboot_tables.h>
4 #include <string.h>
5 #if CONFIG_USE_OPTION_TABLE
6 #include "option_table.h"
7 #endif
8
9 /* control registers - Moto names
10  */
11 #define RTC_REG_A               10
12 #define RTC_REG_B               11
13 #define RTC_REG_C               12
14 #define RTC_REG_D               13
15
16
17 /**********************************************************************
18  * register details
19  **********************************************************************/
20 #define RTC_FREQ_SELECT RTC_REG_A
21
22 /* update-in-progress  - set to "1" 244 microsecs before RTC goes off the bus,
23  * reset after update (may take 1.984ms @ 32768Hz RefClock) is complete,
24  * totalling to a max high interval of 2.228 ms.
25  */
26 # define RTC_UIP                0x80
27 # define RTC_DIV_CTL            0x70
28    /* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */
29 #  define RTC_REF_CLCK_4MHZ     0x00
30 #  define RTC_REF_CLCK_1MHZ     0x10
31 #  define RTC_REF_CLCK_32KHZ    0x20
32    /* 2 values for divider stage reset, others for "testing purposes only" */
33 #  define RTC_DIV_RESET1        0x60
34 #  define RTC_DIV_RESET2        0x70
35   /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
36 # define RTC_RATE_SELECT        0x0F
37 #  define RTC_RATE_NONE         0x00
38 #  define RTC_RATE_32786HZ      0x01
39 #  define RTC_RATE_16384HZ      0x02
40 #  define RTC_RATE_8192HZ       0x03
41 #  define RTC_RATE_4096HZ       0x04
42 #  define RTC_RATE_2048HZ       0x05
43 #  define RTC_RATE_1024HZ       0x06
44 #  define RTC_RATE_512HZ        0x07
45 #  define RTC_RATE_256HZ        0x08
46 #  define RTC_RATE_128HZ        0x09
47 #  define RTC_RATE_64HZ         0x0a
48 #  define RTC_RATE_32HZ         0x0b
49 #  define RTC_RATE_16HZ         0x0c
50 #  define RTC_RATE_8HZ          0x0d
51 #  define RTC_RATE_4HZ          0x0e
52 #  define RTC_RATE_2HZ          0x0f
53
54 /**********************************************************************/
55 #define RTC_CONTROL     RTC_REG_B
56 # define RTC_SET 0x80           /* disable updates for clock setting */
57 # define RTC_PIE 0x40           /* periodic interrupt enable */
58 # define RTC_AIE 0x20           /* alarm interrupt enable */
59 # define RTC_UIE 0x10           /* update-finished interrupt enable */
60 # define RTC_SQWE 0x08          /* enable square-wave output */
61 # define RTC_DM_BINARY 0x04     /* all time/date values are BCD if clear */
62 # define RTC_24H 0x02           /* 24 hour mode - else hours bit 7 means pm */
63 # define RTC_DST_EN 0x01        /* auto switch DST - works f. USA only */
64
65 /**********************************************************************/
66 #define RTC_INTR_FLAGS  RTC_REG_C
67 /* caution - cleared by read */
68 # define RTC_IRQF 0x80          /* any of the following 3 is active */
69 # define RTC_PF 0x40
70 # define RTC_AF 0x20
71 # define RTC_UF 0x10
72
73 /**********************************************************************/
74 #define RTC_VALID       RTC_REG_D
75 # define RTC_VRT 0x80           /* valid RAM and time */
76 /**********************************************************************/
77
78 #if CONFIG_USE_OPTION_TABLE
79 static int rtc_checksum_valid(int range_start, int range_end, int cks_loc)
80 {
81         int i;
82         unsigned sum, old_sum;
83         sum = 0;
84         for(i = range_start; i <= range_end; i++) {
85                 sum += cmos_read(i);
86         }
87         sum = (~sum)&0x0ffff;
88         old_sum = ((cmos_read(cks_loc)<<8) | cmos_read(cks_loc+1))&0x0ffff;
89         return sum == old_sum;
90 }
91
92 static void rtc_set_checksum(int range_start, int range_end, int cks_loc)
93 {
94         int i;
95         unsigned sum;
96         sum = 0;
97         for(i = range_start; i <= range_end; i++) {
98                 sum += cmos_read(i);
99         }
100         sum = ~(sum & 0x0ffff);
101         cmos_write(((sum >> 8) & 0x0ff), cks_loc);
102         cmos_write(((sum >> 0) & 0x0ff), cks_loc+1);
103 }
104 #endif
105
106 #if CONFIG_ARCH_X86
107 #define RTC_CONTROL_DEFAULT (RTC_24H)
108 #define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
109 #else
110 #if CONFIG_ARCH_ALPHA
111 #define RTC_CONTROL_DEFAULT (RTC_SQWE | RTC_24H)
112 #define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
113 #endif
114 #endif
115
116 void rtc_init(int invalid)
117 {
118 #if CONFIG_USE_OPTION_TABLE
119         unsigned char x;
120         int cmos_invalid, checksum_invalid;
121 #endif
122
123         printk(BIOS_DEBUG, "RTC Init\n");
124
125 #if CONFIG_USE_OPTION_TABLE
126         /* See if there has been a CMOS power problem. */
127         x = cmos_read(RTC_VALID);
128         cmos_invalid = !(x & RTC_VRT);
129
130         /* See if there is a CMOS checksum error */
131         checksum_invalid = !rtc_checksum_valid(PC_CKS_RANGE_START,
132                         PC_CKS_RANGE_END,PC_CKS_LOC);
133
134         if (invalid || cmos_invalid || checksum_invalid) {
135                 printk(BIOS_WARNING, "RTC:%s%s%s zeroing cmos\n",
136                         invalid?" Clear requested":"",
137                         cmos_invalid?" Power Problem":"",
138                         checksum_invalid?" Checksum invalid":"");
139 #if 0
140                 cmos_write(0, 0x01);
141                 cmos_write(0, 0x03);
142                 cmos_write(0, 0x05);
143                 for(i = 10; i < 48; i++) {
144                         cmos_write(0, i);
145                 }
146
147                 if (cmos_invalid) {
148                         /* Now setup a default date of Sat 1 January 2000 */
149                         cmos_write(0, 0x00); /* seconds */
150                         cmos_write(0, 0x02); /* minutes */
151                         cmos_write(1, 0x04); /* hours */
152                         cmos_write(7, 0x06); /* day of week */
153                         cmos_write(1, 0x07); /* day of month */
154                         cmos_write(1, 0x08); /* month */
155                         cmos_write(0, 0x09); /* year */
156                 }
157 #endif
158         }
159 #endif
160
161         /* Setup the real time clock */
162         cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL);
163         /* Setup the frequency it operates at */
164         cmos_write(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT);
165
166 #if CONFIG_USE_OPTION_TABLE
167         /* See if there is a LB CMOS checksum error */
168         checksum_invalid = !rtc_checksum_valid(LB_CKS_RANGE_START,
169                         LB_CKS_RANGE_END,LB_CKS_LOC);
170         if(checksum_invalid)
171                 printk(BIOS_DEBUG, "Invalid CMOS LB checksum\n");
172
173         /* Make certain we have a valid checksum */
174         rtc_set_checksum(PC_CKS_RANGE_START,
175                         PC_CKS_RANGE_END,PC_CKS_LOC);
176 #endif
177
178         /* Clear any pending interrupts */
179         (void) cmos_read(RTC_INTR_FLAGS);
180 }
181
182
183 #if CONFIG_USE_OPTION_TABLE
184 /* This routine returns the value of the requested bits
185         input bit = bit count from the beginning of the cmos image
186               length = number of bits to include in the value
187               ret = a character pointer to where the value is to be returned
188         output the value placed in ret
189               returns 0 = successful, -1 = an error occurred
190 */
191 static int get_cmos_value(unsigned long bit, unsigned long length, void *vret)
192 {
193         unsigned char *ret;
194         unsigned long byte,byte_bit;
195         unsigned long i;
196         unsigned char uchar;
197
198         /* The table is checked when it is built to ensure all
199                 values are valid. */
200         ret = vret;
201         byte=bit/8;     /* find the byte where the data starts */
202         byte_bit=bit%8; /* find the bit in the byte where the data starts */
203         if(length<9) {  /* one byte or less */
204                 uchar = cmos_read(byte); /* load the byte */
205                 uchar >>= byte_bit;     /* shift the bits to byte align */
206                 /* clear unspecified bits */
207                 ret[0] = uchar & ((1 << length) -1);
208         }
209         else {  /* more that one byte so transfer the whole bytes */
210                 for(i=0;length;i++,length-=8,byte++) {
211                         /* load the byte */
212                         ret[i]=cmos_read(byte);
213                 }
214         }
215         return 0;
216 }
217
218 int get_option(void *dest, const char *name)
219 {
220         extern struct cmos_option_table option_table;
221         struct cmos_option_table *ct;
222         struct cmos_entries *ce;
223         size_t namelen;
224         int found=0;
225
226         /* Figure out how long name is */
227         namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
228
229         /* find the requested entry record */
230         ct=&option_table;
231         ce=(struct cmos_entries*)((unsigned char *)ct + ct->header_length);
232         for(;ce->tag==LB_TAG_OPTION;
233                 ce=(struct cmos_entries*)((unsigned char *)ce + ce->size)) {
234                 if (memcmp(ce->name, name, namelen) == 0) {
235                         found=1;
236                         break;
237                 }
238         }
239         if(!found) {
240                 printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
241                 return(-2);
242         }
243
244         if(get_cmos_value(ce->bit, ce->length, dest))
245                 return(-3);
246         if(!rtc_checksum_valid(LB_CKS_RANGE_START,
247                         LB_CKS_RANGE_END,LB_CKS_LOC))
248                 return(-4);
249         return(0);
250 }
251 #endif /* CONFIG_USE_OPTION_TABLE */