summaryrefslogtreecommitdiff
path: root/src/pc80/mc146818rtc_early.c
blob: 455ed08ab9aa17624d89c72e6715ccb2ba9414fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <pc80/mc146818rtc.h>
#include <fallback.h>
#if CONFIG_USE_OPTION_TABLE
#include "option_table.h"
#endif

#ifndef CONFIG_MAX_REBOOT_CNT
#error "CONFIG_MAX_REBOOT_CNT not defined"
#endif
#if  CONFIG_MAX_REBOOT_CNT > 15
#error "CONFIG_MAX_REBOOT_CNT too high"
#endif

#if CONFIG_USE_CMOS_RECOVERY
#include <cbfs.h>
#include <console/loglevel.h>

int do_printk(int msg_level, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
#define printk_warning(fmt, arg...) do_printk(BIOS_WARNING ,fmt, ##arg)
#define printk_debug(fmt, arg...) do_printk(BIOS_DEBUG ,fmt, ##arg)
#endif

static int cmos_error(void)
{
	unsigned char reg_d;
	/* See if the cmos error condition has been flagged */
	reg_d = cmos_read(RTC_REG_D);
	return (reg_d & RTC_VRT) == 0;
}

static int cmos_chksum_valid(void)
{
#if CONFIG_USE_OPTION_TABLE
	unsigned char addr;
	unsigned long sum, old_sum;
	sum = 0;
	/* Comput the cmos checksum */
	for(addr = LB_CKS_RANGE_START; addr <= LB_CKS_RANGE_END; addr++) {
		sum += cmos_read(addr);
	}
	sum = (sum & 0xffff) ^ 0xffff;

	/* Read the stored checksum */
	old_sum = cmos_read(LB_CKS_LOC) << 8;
	old_sum |=  cmos_read(LB_CKS_LOC+1);

	return sum == old_sum;
#else
	return 0;
#endif
}


static inline int last_boot_normal(void)
{
	unsigned char byte;
	byte = cmos_read(RTC_BOOT_BYTE);
	return (byte & (1 << 1));
}

static inline int do_normal_boot(void)
{
	unsigned char byte;
	int i;

	if (cmos_error() || !cmos_chksum_valid()) {
#if CONFIG_USE_CMOS_RECOVERY
		char *cmos_default = cbfs_find_file("cmos.default", CBFS_COMPONENT_CMOS_DEFAULT);
		if (cmos_default) {
			printk_warning("WARNING - CMOS CORRUPTED. RESTORING DEFAULTS.\n");
			/* First 14 bytes are reserved for
			   RTC and ignored by nvramtool, too.
			   Only 128 bytes: 128+ requires cmos configuration and
			   contains only suspend-to-ram data, which isn't part
			   of the recovery procedure. */
			for (i = 14; i < 128; i++) {
				cmos_write(cmos_default[i], i);
			}
			/* Now reboot to run with default cmos. */
			outb(0x06, 0xcf9);
			for (;;) asm("hlt"); /* Wait for reset! */
		}
#endif

		/* There are no impossible values, no checksums so just
		 * trust whatever value we have in the the cmos,
		 * but clear the fallback bit.
		 */
		byte = cmos_read(RTC_BOOT_BYTE);
		byte &= 0x0c;
		byte |= CONFIG_MAX_REBOOT_CNT << 4;
		cmos_write(byte, RTC_BOOT_BYTE);
	}

	/* The RTC_BOOT_BYTE is now o.k. see where to go. */
	byte = cmos_read(RTC_BOOT_BYTE);

	/* Are we in normal mode? */
	if (byte & 1) {
		byte &= 0x0f; /* yes, clear the boot count */
	}

	/* Properly set the last boot flag */
	byte &= 0xfc;
	if ((byte >> 4) < CONFIG_MAX_REBOOT_CNT) {
		byte |= (1<<1);
	}

	/* Are we already at the max count? */
	if ((byte >> 4) < CONFIG_MAX_REBOOT_CNT) {
		byte += 1 << 4; /* No, add 1 to the count */
	}
	else {
		byte &= 0xfc;	/* Yes, put in fallback mode */
	}

	/* Save the boot byte */
	cmos_write(byte, RTC_BOOT_BYTE);

	return (byte & (1<<1));
}

unsigned read_option(unsigned start, unsigned size, unsigned def)
{
#if CONFIG_USE_OPTION_TABLE
	unsigned byte;
	byte = cmos_read(start/8);
	return (byte >> (start & 7U)) & ((1U << size) - 1U);
#else
	return def;
#endif
}