coreboot-v2/src/pc80/mc146818rtc.c
<<
>>
Prefs
   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/* control registers - Moto names
   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 * register details
  17 **********************************************************************/
  18#define RTC_FREQ_SELECT RTC_REG_A
  19
  20/* update-in-progress  - set to "1" 244 microsecs before RTC goes off the bus,
  21 * reset after update (may take 1.984ms @ 32768Hz RefClock) is complete,
  22 * totalling to a max high interval of 2.228 ms.
  23 */
  24# define RTC_UIP                0x80
  25# define RTC_DIV_CTL            0x70
  26   /* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */
  27#  define RTC_REF_CLCK_4MHZ     0x00
  28#  define RTC_REF_CLCK_1MHZ     0x10
  29#  define RTC_REF_CLCK_32KHZ    0x20
  30   /* 2 values for divider stage reset, others for "testing purposes only" */
  31#  define RTC_DIV_RESET1        0x60
  32#  define RTC_DIV_RESET2        0x70
  33  /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
  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           /* disable updates for clock setting */
  55# define RTC_PIE 0x40           /* periodic interrupt enable */
  56# define RTC_AIE 0x20           /* alarm interrupt enable */
  57# define RTC_UIE 0x10           /* update-finished interrupt enable */
  58# define RTC_SQWE 0x08          /* enable square-wave output */
  59# define RTC_DM_BINARY 0x04     /* all time/date values are BCD if clear */
  60# define RTC_24H 0x02           /* 24 hour mode - else hours bit 7 means pm */
  61# define RTC_DST_EN 0x01        /* auto switch DST - works f. USA only */
  62
  63/**********************************************************************/
  64#define RTC_INTR_FLAGS  RTC_REG_C
  65/* caution - cleared by read */
  66# define RTC_IRQF 0x80          /* any of the following 3 is active */
  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           /* valid RAM and time */
  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 /* alpha setup */
 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        /* See if there has been a CMOS power problem. */
 145        x = cmos_read(RTC_VALID);
 146        cmos_invalid = !(x & RTC_VRT);
 147
 148        /* See if there is a CMOS checksum error */
 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                        /* Now setup a default date of Sat 1 January 2000 */
 167                        cmos_write(0, 0x00); /* seconds */
 168                        cmos_write(0, 0x02); /* minutes */
 169                        cmos_write(1, 0x04); /* hours */
 170                        cmos_write(7, 0x06); /* day of week */
 171                        cmos_write(1, 0x07); /* day of month */
 172                        cmos_write(1, 0x08); /* month */
 173                        cmos_write(0, 0x09); /* year */
 174                }
 175#endif
 176        }
 177#endif
 178
 179        /* Setup the real time clock */
 180        cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL);
 181        /* Setup the frequency it operates at */
 182        cmos_write(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT);
 183
 184#if CONFIG_HAVE_OPTION_TABLE
 185        /* See if there is a LB CMOS checksum error */
 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        /* Make certain we have a valid checksum */
 192        rtc_set_checksum(PC_CKS_RANGE_START,
 193                        PC_CKS_RANGE_END,PC_CKS_LOC);
 194#endif
 195
 196        /* Clear any pending interrupts */
 197        (void) cmos_read(RTC_INTR_FLAGS);
 198}
 199
 200
 201#if CONFIG_USE_OPTION_TABLE == 1
 202/* This routine returns the value of the requested bits
 203        input bit = bit count from the beginning of the cmos image
 204              length = number of bits to include in the value
 205              ret = a character pointer to where the value is to be returned
 206        output the value placed in ret
 207              returns 0 = successful, -1 = an error occurred
 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        /* The table is checked when it is built to ensure all 
 217                values are valid. */
 218        ret = vret;
 219        byte=bit/8;     /* find the byte where the data starts */
 220        byte_bit=bit%8; /* find the bit in the byte where the data starts */
 221        if(length<9) {  /* one byte or less */
 222                uchar = cmos_read(byte); /* load the byte */
 223                uchar >>= byte_bit;     /* shift the bits to byte align */
 224                /* clear unspecified bits */
 225                ret[0] = uchar & ((1 << length) -1);
 226        }
 227        else {  /* more that one byte so transfer the whole bytes */
 228                for(i=0;length;i++,length-=8,byte++) {
 229                        /* load the byte */
 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        /* Figure out how long name is */
 245        namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
 246        
 247        /* find the requested entry record */
 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 /* CONFIG_USE_OPTION_TABLE */
 270
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.