blob: 268cfc2fe6a6a93e4e5c13ecb71a044474431599 [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#ifndef CONFIG_MAX_REBOOT_CNT
9#error "CONFIG_MAX_REBOOT_CNT not defined"
Eric Biederman8d9c1232003-06-17 08:42:17 +000010#endif
Stefan Reinauer08670622009-06-30 15:17:49 +000011#if CONFIG_MAX_REBOOT_CNT > 15
12#error "CONFIG_MAX_REBOOT_CNT too high"
Eric Biederman8d9c1232003-06-17 08:42:17 +000013#endif
14
Eric Biederman05f26fc2003-06-11 21:55:00 +000015static int cmos_error(void)
16{
17 unsigned char reg_d;
18 /* See if the cmos error condition has been flagged */
19 reg_d = cmos_read(RTC_REG_D);
20 return (reg_d & RTC_VRT) == 0;
21}
22
23static int cmos_chksum_valid(void)
24{
Edwin Beasanteb50c7d2010-07-06 21:05:04 +000025#if CONFIG_USE_OPTION_TABLE
Eric Biederman05f26fc2003-06-11 21:55:00 +000026 unsigned char addr;
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020027 u16 sum, old_sum;
Eric Biederman05f26fc2003-06-11 21:55:00 +000028 sum = 0;
Stefan Reinauerea5c2b62011-10-27 18:42:53 +020029 /* Compute the cmos checksum */
Stefan Reinauerb5828d72010-03-29 17:14:28 +000030 for(addr = LB_CKS_RANGE_START; addr <= LB_CKS_RANGE_END; addr++) {
Eric Biederman05f26fc2003-06-11 21:55:00 +000031 sum += cmos_read(addr);
32 }
Eric Biederman05f26fc2003-06-11 21:55:00 +000033
34 /* Read the stored checksum */
Stefan Reinauerb5828d72010-03-29 17:14:28 +000035 old_sum = cmos_read(LB_CKS_LOC) << 8;
36 old_sum |= cmos_read(LB_CKS_LOC+1);
Eric Biederman05f26fc2003-06-11 21:55:00 +000037
38 return sum == old_sum;
Stefan Reinauer8e726b72010-03-29 23:01:35 +000039#else
40 return 0;
41#endif
Eric Biederman05f26fc2003-06-11 21:55:00 +000042}
43
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060044static inline __attribute__((unused)) int boot_count(uint8_t rtc_byte)
Eric Biederman9b4336c2003-07-19 04:28:22 +000045{
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060046 return rtc_byte >> 4;
47}
48
49static inline __attribute__((unused)) uint8_t increment_boot_count(uint8_t rtc_byte)
50{
51 return rtc_byte + (1 << 4);
52}
53
54static inline __attribute__((unused)) uint8_t boot_set_fallback(uint8_t rtc_byte)
55{
56 return rtc_byte & ~RTC_BOOT_NORMAL;
57}
58
59static inline __attribute__((unused)) int boot_use_normal(uint8_t rtc_byte)
60{
61 return rtc_byte & RTC_BOOT_NORMAL;
Eric Biederman9b4336c2003-07-19 04:28:22 +000062}
63
Edward O'Callaghanec79d7a2014-06-26 18:14:04 +100064static inline __attribute__((unused)) int do_normal_boot(void)
Eric Biederman05f26fc2003-06-11 21:55:00 +000065{
66 unsigned char byte;
67
68 if (cmos_error() || !cmos_chksum_valid()) {
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060069 /* Invalid CMOS checksum detected!
70 * Force fallback boot...
Eric Biederman05f26fc2003-06-11 21:55:00 +000071 */
72 byte = cmos_read(RTC_BOOT_BYTE);
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060073 byte &= boot_set_fallback(byte) & 0x0f;
74 byte |= 0xf << 4;
Eric Biederman05f26fc2003-06-11 21:55:00 +000075 cmos_write(byte, RTC_BOOT_BYTE);
76 }
77
78 /* The RTC_BOOT_BYTE is now o.k. see where to go. */
79 byte = cmos_read(RTC_BOOT_BYTE);
Stefan Reinauer14e22772010-04-27 06:56:47 +000080
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060081 /* Are we attempting to boot normally? */
82 if (boot_use_normal(byte)) {
83 /* Are we already at the max count? */
84 if (boot_count(byte) < CONFIG_MAX_REBOOT_CNT)
85 byte = increment_boot_count(byte);
86 else
87 byte = boot_set_fallback(byte);
Eric Biederman05f26fc2003-06-11 21:55:00 +000088 }
89
Eric Biederman05f26fc2003-06-11 21:55:00 +000090 /* Save the boot byte */
91 cmos_write(byte, RTC_BOOT_BYTE);
92
Timothy Pearson3bfd7cc2015-11-01 02:13:17 -060093 /* Return selected code path for this boot attempt */
94 return boot_use_normal(byte);
Eric Biederman05f26fc2003-06-11 21:55:00 +000095}
Eric Biederman5cd81732004-03-11 15:01:31 +000096
Patrick Georgib2517532011-05-10 21:53:13 +000097unsigned read_option_lowlevel(unsigned start, unsigned size, unsigned def)
Eric Biederman5cd81732004-03-11 15:01:31 +000098{
Edwin Beasanteb50c7d2010-07-06 21:05:04 +000099#if CONFIG_USE_OPTION_TABLE
Eric Biederman5cd81732004-03-11 15:01:31 +0000100 unsigned byte;
101 byte = cmos_read(start/8);
102 return (byte >> (start & 7U)) & ((1U << size) - 1U);
103#else
104 return def;
105#endif
106}