blob: 3ff5d4fb3e6f1c1e58d0d936fbd840670b2b908a [file] [log] [blame]
Stefan Reinauerea5c2b62011-10-27 18:42:53 +02001#include <stdint.h>
Eric Biederman05f26fc2003-06-11 21:55:00 +00002#include <pc80/mc146818rtc.h>
Stefan Reinauerde3206a2010-02-22 06:09:43 +00003#include <fallback.h>
Stefan Reinauer10ec0fe2010-09-25 10:40:47 +00004#if CONFIG_USE_OPTION_TABLE
5#include "option_table.h"
6#endif
Eric Biederman05f26fc2003-06-11 21:55:00 +00007
Stefan Reinauer08670622009-06-30 15:17:49 +00008#if CONFIG_MAX_REBOOT_CNT > 15
9#error "CONFIG_MAX_REBOOT_CNT too high"
Eric Biederman8d9c1232003-06-17 08:42:17 +000010#endif
11
Eric Biederman05f26fc2003-06-11 21:55:00 +000012static int cmos_error(void)
13{
14 unsigned char reg_d;
15 /* See if the cmos error condition has been flagged */
16 reg_d = cmos_read(RTC_REG_D);
17 return (reg_d & RTC_VRT) == 0;
18}
19
20static int cmos_chksum_valid(void)
21{
Edwin Beasanteb50c7d2010-07-06 21:05:04 +000022#if CONFIG_USE_OPTION_TABLE
Eric Biederman05f26fc2003-06-11 21:55:00 +000023 unsigned char addr;
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020024 u16 sum, old_sum;
Eric Biederman05f26fc2003-06-11 21:55:00 +000025 sum = 0;
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020026 /* Compute the cmos checksum */
Stefan Reinauerb5828d72010-03-29 17:14:28 +000027 for(addr = LB_CKS_RANGE_START; addr <= LB_CKS_RANGE_END; addr++) {
Eric Biederman05f26fc2003-06-11 21:55:00 +000028 sum += cmos_read(addr);
29 }
Eric Biederman05f26fc2003-06-11 21:55:00 +000030
31 /* Read the stored checksum */
Stefan Reinauerb5828d72010-03-29 17:14:28 +000032 old_sum = cmos_read(LB_CKS_LOC) << 8;
33 old_sum |= cmos_read(LB_CKS_LOC+1);
Eric Biederman05f26fc2003-06-11 21:55:00 +000034
35 return sum == old_sum;
Stefan Reinauer8e726b72010-03-29 23:01:35 +000036#else
37 return 0;
38#endif
Eric Biederman05f26fc2003-06-11 21:55:00 +000039}
40
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060041static inline __attribute__((unused)) int boot_count(uint8_t rtc_byte)
Eric Biederman9b4336c2003-07-19 04:28:22 +000042{
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060043 return rtc_byte >> 4;
44}
45
46static inline __attribute__((unused)) uint8_t increment_boot_count(uint8_t rtc_byte)
47{
48 return rtc_byte + (1 << 4);
49}
50
51static inline __attribute__((unused)) uint8_t boot_set_fallback(uint8_t rtc_byte)
52{
53 return rtc_byte & ~RTC_BOOT_NORMAL;
54}
55
56static inline __attribute__((unused)) int boot_use_normal(uint8_t rtc_byte)
57{
58 return rtc_byte & RTC_BOOT_NORMAL;
Eric Biederman9b4336c2003-07-19 04:28:22 +000059}
60
Edward O'Callaghanec79d7a2014-06-26 18:14:04 +100061static inline __attribute__((unused)) int do_normal_boot(void)
Eric Biederman05f26fc2003-06-11 21:55:00 +000062{
63 unsigned char byte;
64
65 if (cmos_error() || !cmos_chksum_valid()) {
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060066 /* Invalid CMOS checksum detected!
67 * Force fallback boot...
Eric Biederman05f26fc2003-06-11 21:55:00 +000068 */
69 byte = cmos_read(RTC_BOOT_BYTE);
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060070 byte &= boot_set_fallback(byte) & 0x0f;
71 byte |= 0xf << 4;
Eric Biederman05f26fc2003-06-11 21:55:00 +000072 cmos_write(byte, RTC_BOOT_BYTE);
73 }
74
75 /* The RTC_BOOT_BYTE is now o.k. see where to go. */
76 byte = cmos_read(RTC_BOOT_BYTE);
Stefan Reinauer14e22772010-04-27 06:56:47 +000077
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060078 /* Are we attempting to boot normally? */
79 if (boot_use_normal(byte)) {
80 /* Are we already at the max count? */
81 if (boot_count(byte) < CONFIG_MAX_REBOOT_CNT)
82 byte = increment_boot_count(byte);
83 else
84 byte = boot_set_fallback(byte);
Eric Biederman05f26fc2003-06-11 21:55:00 +000085 }
86
Eric Biederman05f26fc2003-06-11 21:55:00 +000087 /* Save the boot byte */
88 cmos_write(byte, RTC_BOOT_BYTE);
89
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060090 /* Return selected code path for this boot attempt */
91 return boot_use_normal(byte);
Eric Biederman05f26fc2003-06-11 21:55:00 +000092}
Eric Biederman5cd81732004-03-11 15:01:31 +000093
Patrick Georgib2517532011-05-10 21:53:13 +000094unsigned read_option_lowlevel(unsigned start, unsigned size, unsigned def)
Eric Biederman5cd81732004-03-11 15:01:31 +000095{
Edwin Beasanteb50c7d2010-07-06 21:05:04 +000096#if CONFIG_USE_OPTION_TABLE
Eric Biederman5cd81732004-03-11 15:01:31 +000097 unsigned byte;
98 byte = cmos_read(start/8);
99 return (byte >> (start & 7U)) & ((1U << size) - 1U);
100#else
101 return def;
102#endif
103}