diff --git a/board/nm/common/da9063.h b/board/nm/common/da9063.h index 344d6ce518..e5a42c3a5a 100644 --- a/board/nm/common/da9063.h +++ b/board/nm/common/da9063.h @@ -23,6 +23,7 @@ #define PMIC_FAULT_POR_MASK 0x02 /* Startup from No-Power/RTC/Delivery mode */ #define PMIC_REG_EVENT_A 0x06 +#define PMIC_REG_EVENT_ONKEY_MASK 0x01 #define PMIC_REG_EVENT_RTC_ALARM_MASK 0x02 #define PMIC_REG_EVENT_RTC_TICK_MASK 0x04 #define PMIC_REG_EVENT_EVENTS_B_MASK 0x20 @@ -77,6 +78,7 @@ #define PMIC_REG_TRIM_CLDR 0x120 /* Calendar Trim register, 2's complement, 1.9ppm per bit */ #define PMIC_GP_ID_0 0x121 /* General purpose ID 0 (R/W) */ +#define PMIC_GP_ID_1 0x122 /* General purpose ID 1 (R/W) */ #define PMIC_REG_CONFIG_ID 0x184 /* OTP Config ID */ diff --git a/board/nm/nmhw21/Makefile b/board/nm/nmhw21/Makefile index 3d736b1aee..51c8f3bc7b 100644 --- a/board/nm/nmhw21/Makefile +++ b/board/nm/nmhw21/Makefile @@ -10,4 +10,4 @@ ifeq ($(CONFIG_SKIP_LOWLEVEL_INIT),) obj-y := mux.o endif -obj-y += board.o ../common/bdparser.o ../common/board_descriptor.o ../common/da9063.o ../common/ether_crc.o fileaccess.o sja1105.o ui.o um.o +obj-y += board.o ../common/bdparser.o ../common/board_descriptor.o ../common/da9063.o ../common/ether_crc.o fileaccess.o sja1105.o ui.o um.o reset_reason.o diff --git a/board/nm/nmhw21/board.c b/board/nm/nmhw21/board.c index c74256f945..78f31b79d0 100644 --- a/board/nm/nmhw21/board.c +++ b/board/nm/nmhw21/board.c @@ -37,8 +37,8 @@ #include "../common/bdparser.h" #include "../common/board_descriptor.h" #include "../common/da9063.h" -#include "../common/ether_crc.h" #include "board.h" +#include "reset_reason.h" #include "sja1105.h" #include "ui.h" #include "um.h" @@ -455,78 +455,26 @@ static void init_pmic_spl(void) da9063_release_i2c_bus(bus); } - -struct reset_registers { - /* Reboot Reasons, set by OS, expect watchdog set by bootloader */ - uint32_t rr_value; - uint32_t rr_value_crc; - - /* Start Events */ - uint32_t se_magic; /* Token to check presence of following fields */ - uint32_t se_events; /* Events bitmask, see SE_... defines */ - uint32_t se_checksum; /* Checksum over se_events */ -}; - -/* Watchdog reboot reason event */ -#define RR_EXTERNAL_WATCHDOG_PATTERN 0x781f9ce2 - -/* Start event token 'SRTE' */ -#define SE_MAGIC 0x53525445 - -/* Possible start events (see se_events) */ -#define SE_POR 0x00000001 -#define SE_WATCHDOG 0x00000010 -#define SE_IGNITION 0x00000100 -#define SE_RTC_ALARM 0x00000200 -#define SE_RTC_TICK 0x00000400 - - -static void print_start_reason(uint32_t events) -{ - puts("\nStart Events: "); - - if (events == 0) { - puts("-\n"); - } - else { - static char buffer[10+11+11+6+6+1]; - - buffer[0] = 0; - if (events & SE_POR) - strncat(buffer, "PowerOn, ", sizeof(buffer)); - if (events & SE_WATCHDOG) - strncat(buffer, "Watchdog, ", sizeof(buffer)); - if (events & SE_IGNITION) - strncat(buffer, "Ignition, ", sizeof(buffer)); - if (events & SE_RTC_ALARM) - strncat(buffer, "RTC, ", sizeof(buffer)); - if (events & SE_RTC_TICK) - strncat(buffer, "Tick, ", sizeof(buffer)); - - /* Trim last comma, no 0 len check required, at least one entry is present */ - buffer[strlen(buffer)-2] = 0; - printf("%s\n", buffer); - } -} - -static void check_pmic_reset_reason(unsigned int reset_reason_shm_location) +static void check_reset_reason(unsigned int reset_reason_shm_location) { volatile struct reset_registers* reset_regs = (struct reset_registers*)reset_reason_shm_location; - uint32_t start_event = 0; + uint32_t start_reason = 0; + uint32_t reset_reason = 0; uint8_t state = 0x00; int bus; int ret; + char strbuf[256]; bus = da9063_claim_i2c_bus(); /* - * Check/write boot marker to GP_ID_0 + * Check/write boot marker to PMIC register GP_ID_1 * If this marker is not present, we have a power on reset */ - ret = da9063_get_reg(PMIC_GP_ID_0, &state); + ret = da9063_get_reg(PMIC_GP_ID_1, &state); if ((ret == 0) && (state != 0xC5)) { - start_event |= SE_POR; - (void)da9063_set_reg(PMIC_GP_ID_0, 0xC5); + (void)da9063_set_reg(PMIC_GP_ID_1, 0xC5); + start_reason |= SR_POR; } /* @@ -536,72 +484,118 @@ static void check_pmic_reset_reason(unsigned int reset_reason_shm_location) */ ret = da9063_get_reg(PMIC_REG_FAULT_LOG, &state); if ((ret == 0) && (state != 0)) { - // PMIC Watchdog - if (state & PMIC_FAULT_TWD_ERROR_MASK) { - start_event |= SE_WATCHDOG; - - reset_regs->rr_value = RR_EXTERNAL_WATCHDOG_PATTERN; - reset_regs->rr_value_crc = ether_crc(sizeof(reset_regs->rr_value), - (const uint8_t*)&(reset_regs->rr_value)); - } - - // PMIC Power On Reset (only when RTC battery is removed) - if (state & PMIC_FAULT_POR_MASK) { - start_event |= SE_POR; - } - /* clear pmic fault log by writing back all bits currently set */ (void)da9063_set_reg(PMIC_REG_FAULT_LOG, state); + + /* PMIC Power On Reset (only when RTC battery is removed) */ + if (state & PMIC_FAULT_POR_MASK) { + start_reason |= SR_POR; + } + + /* PMIC Watchdog */ + if (state & PMIC_FAULT_TWD_ERROR_MASK) { + start_reason |= SR_WATCHDOG; + } } /* - * Event Register A - * - Event B Activity + * Check Wakeup Events + * Event Register A holds: + * - Event B, C Activity + * - nONKEY: Button * - RTC Alarm * - RTC Tick + * Event Register B + * - COMP 1V2: Ignition */ ret = da9063_get_reg(PMIC_REG_EVENT_A, &state); if ((ret == 0) && (state != 0)) { (void)da9063_set_reg(PMIC_REG_EVENT_A, state); + if (state & PMIC_REG_EVENT_ONKEY_MASK) { + start_reason |= (SR_WAKEUP | SR_EVT_BUTTON); + } + if (state & PMIC_REG_EVENT_RTC_ALARM_MASK) { - start_event |= SE_RTC_ALARM; + start_reason |= (SR_WAKEUP | SR_EVT_RTC_ALARM); } if (state & PMIC_REG_EVENT_RTC_TICK_MASK) { - start_event |= SE_RTC_TICK; + start_reason |= (SR_WAKEUP | SR_EVT_RTC_TICK); } if (state & PMIC_REG_EVENT_EVENTS_B_MASK) { - /* - * Event Register B - * - COMP 1V2: Ignition - */ - ret = da9063_get_reg(PMIC_REG_EVENT_B, &state); - if ((ret == 0) && (state != 0)) { - (void)da9063_set_reg(PMIC_REG_EVENT_B, state); + uint8_t state_b; - if (state & PMIC_REG_EVENT_COMP1V2_MASK) { - start_event |= SE_IGNITION; + ret = da9063_get_reg(PMIC_REG_EVENT_B, &state_b); + if ((ret == 0) && (state_b != 0)) { + (void)da9063_set_reg(PMIC_REG_EVENT_B, state_b); + + if (state_b & PMIC_REG_EVENT_COMP1V2_MASK) { + start_reason |= (SR_WAKEUP | SR_EVT_IGNITION); } } } - - /* TODO: Should we clear events here or leave them for Linux driver? */ } - sys_start_event = start_event; + /* + * Check for software reboot indicated by OS via shared memory + * - checksum valid? + * - Reason == 'REBO' or 'OOPS' + */ + if (rr_is_reset_reason_valid(reset_regs)) + { + if (reset_regs->rr_value == RR_REBOOT_PATTERN) { + start_reason |= SR_REBOOT; + } + else if (reset_regs->rr_value == RR_OOPS_PATTERN) { + /* Treat kernel oops as reboot */ + start_reason |= SR_REBOOT; + } + else if (reset_regs->rr_value == RR_WAKE_PATTERN) { + start_reason |= SR_WAKEUP; + } + else { + printf("unknown reset reason\n"); + } + } - /* Store start events in shared memory region for OS */ - reset_regs->se_magic = SE_MAGIC; - reset_regs->se_events = start_event; - reset_regs->se_checksum = 0; - reset_regs->se_checksum = ether_crc(sizeof(reset_regs->se_events), - (const uint8_t*)&(reset_regs->se_events)); + /* + * Priority decoder for start and reset reason + */ + if (start_reason & SR_WATCHDOG) { + /* Watchdog has highest priority as it also sets POR and other events */ + start_reason = SR_WATCHDOG; + reset_reason = RR_EXTERNAL_WATCHDOG_PATTERN; + } + else if (start_reason & SR_POR) { + start_reason = SR_POR; + reset_reason = RR_POWEROFF_PATTERN; + } + else if (start_reason & SR_WAKEUP) { + /* Include start events when wakeup is detected */ + start_reason = SR_WAKEUP | (start_reason & SR_EVT_WAKE_MASK); + reset_reason = RR_WAKE_PATTERN; + } + else if (start_reason & SR_REBOOT) { + start_reason = SR_REBOOT; + reset_reason = reset_regs->rr_value; + } + else { + puts("Unknown start reason\n"); + start_reason = SR_REBOOT; + reset_reason = reset_regs->rr_value; + } + + sys_start_event = start_reason; + + rr_set_reset_reason(reset_regs, reset_reason); + rr_set_start_reason(reset_regs, start_reason); da9063_release_i2c_bus(bus); - print_start_reason(reset_regs->se_events); + rr_start_reason_to_str(reset_regs->sr_events, strbuf, sizeof(strbuf)); + printf("\nStart Events: %s\n", strbuf); } static void pmic_ignition_gate_on(void) @@ -738,7 +732,7 @@ void am33xx_spl_board_init(void) init_bd_spl(); /* Detect reset/Wakeup reason */ - check_pmic_reset_reason(RESET_REASON_SHM_LOCATION); + check_reset_reason(RESET_REASON_SHM_LOCATION); /* Switch on ignition gate so we can read state later */ pmic_ignition_gate_on(); @@ -747,7 +741,7 @@ void am33xx_spl_board_init(void) * If this is a power-on start, see if ignition is active. * If not, power down as this is considered an unwanted system start. */ - if (sys_start_event & SE_POR) { + if (sys_start_event & SR_POR) { stop_if_ignition_is_off(); } @@ -2153,6 +2147,21 @@ static void ft_user_interface(void *blob) } } +static void ft_start_event(void *blob, uint32_t reset_reason_shm_location) +{ + volatile struct reset_registers* reset_regs = (struct reset_registers*)reset_reason_shm_location; + + if (rr_is_start_reason_valid(reset_regs)) { + int node_offset; + + node_offset = fdt_path_offset(blob, "/sysstate-start/"); + if (node_offset != -1) { + + fdt_setprop_u32(blob, node_offset, "start-reason", reset_regs->sr_events); + } + } +} + #endif static void ft_eth(void *blob) @@ -2192,6 +2201,7 @@ int ft_board_setup(void *blob, bd_t *bd) ft_user_module(blob); #endif ft_eth(blob); + ft_start_event(blob, RESET_REASON_SHM_LOCATION); return 0; } diff --git a/board/nm/nmhw21/reset_reason.c b/board/nm/nmhw21/reset_reason.c new file mode 100644 index 0000000000..35b50d06ce --- /dev/null +++ b/board/nm/nmhw21/reset_reason.c @@ -0,0 +1,81 @@ +/* + * reset_reason.c + * + * Reset/start reason handling + * + * Copyright (C) 2021 NetModule AG - https://www.netmodule.com/ + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include "../common/ether_crc.h" +#include "reset_reason.h" + + +void rr_set_reset_reason(volatile struct reset_registers* reset_regs, uint32_t reason) +{ + reset_regs->rr_value = reason; + reset_regs->rr_value_crc = ether_crc(sizeof(reset_regs->rr_value), + (const uint8_t*)&(reset_regs->rr_value)); +} + +bool rr_is_reset_reason_valid(volatile const struct reset_registers* reset_regs) +{ + const uint32_t crc = ether_crc(sizeof(reset_regs->rr_value), + (const uint8_t*)&(reset_regs->rr_value)); + return crc == reset_regs->rr_value_crc; +} + +void rr_set_start_reason(volatile struct reset_registers* reset_regs, uint32_t event) +{ + /* Store start events in shared memory region for OS */ + reset_regs->sr_magic = SR_MAGIC; + reset_regs->sr_events = event; + reset_regs->sr_checksum = ether_crc(sizeof(reset_regs->sr_events), + (const uint8_t*)&(reset_regs->sr_events)); +} + +bool rr_is_start_reason_valid(volatile const struct reset_registers* reset_regs) +{ + if (reset_regs->sr_magic == SR_MAGIC) { + const uint32_t crc = ether_crc(sizeof(reset_regs->sr_events), + (const uint8_t*)&(reset_regs->sr_events)); + if (crc == reset_regs->sr_checksum) { + return true; + } + } + + return false; +} + +void rr_start_reason_to_str(uint32_t events, char* buffer, size_t bufsize) +{ + if (events == 0) { + strncpy(buffer, "-\n", bufsize); + } + else { + buffer[0] = 0; + if (events & SR_POR) + strncat(buffer, "PowerOn, ", bufsize); + if (events & SR_WATCHDOG) + strncat(buffer, "Watchdog, ", bufsize); + if (events & SR_REBOOT) + strncat(buffer, "Reboot, ", bufsize); + if (events & SR_WAKEUP) + strncat(buffer, "Wakeup, ", bufsize); + + if (events & SR_EVT_IGNITION) + strncat(buffer, "Ignition, ", bufsize); + if (events & SR_EVT_RTC_ALARM) + strncat(buffer, "RTC, ", bufsize); + if (events & SR_EVT_RTC_TICK) + strncat(buffer, "Tick, ", bufsize); + if (events & SR_EVT_GPI) + strncat(buffer, "GPI, ", bufsize); + if (events & SR_EVT_BUTTON) + strncat(buffer, "Button, ", bufsize); + + /* Trim last comma, no 0 len check required, at least one entry is present */ + buffer[strlen(buffer)-2] = 0; + } +} diff --git a/board/nm/nmhw21/reset_reason.h b/board/nm/nmhw21/reset_reason.h new file mode 100644 index 0000000000..09b36b022e --- /dev/null +++ b/board/nm/nmhw21/reset_reason.h @@ -0,0 +1,60 @@ +/* + * reset_reason.h + * + * Reset/start reason handling + * + * Copyright (C) 2021 NetModule AG - https://www.netmodule.com/ + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef RESET_REASON_H +#define RESET_REASON_H + +struct reset_registers { + /* Reboot Reasons, set by OS, expect watchdog set by bootloader */ + uint32_t rr_value; + uint32_t rr_value_crc; + + /* Start Reasons as determined by hardware */ + uint32_t sr_magic; /* Token to check presence of following fields */ + uint32_t sr_events; /* Events bitmask, see SE_... defines */ + uint32_t sr_checksum; /* Checksum over se_events */ +}; + +/* Watchdog reboot reason event */ +#define RR_POWEROFF_PATTERN 0x00000000 +#define RR_EXTERNAL_WATCHDOG_PATTERN 0x781f9ce2 +#define RR_BOOT_PATTERN 0x424f4f54 /* ‘BOOT’, 0xb9808470 */ +#define RR_REBOOT_PATTERN 0x5245424f /* ‘REBO’, 0x7d5d9d66 */ +#define RR_OOPS_PATTERN 0x4F4F5053 /* ‘OOPS’, 0x2b85bc5f */ +#define RR_WAKE_PATTERN 0x57414B45 /* 'WAKE', 0x7b0acb48 */ + +/* Start reason token 'SRTE' */ +#define SR_MAGIC 0x53525445 + +/* Possible start events (see sr_events) */ +#define SR_POR 0x00000001 +#define SR_WATCHDOG 0x00000010 +#define SR_REBOOT 0x00000020 +#define SR_WAKEUP 0x00000080 /* See SR_EVT_xx bits */ + +/* In case of wake-up, these are the events that caused the start */ +#define SR_EVT_IGNITION 0x00000100 +#define SR_EVT_RTC_ALARM 0x00000200 /* RTC date/time alarm */ +#define SR_EVT_RTC_TICK 0x00000400 /* RTC tick based alarm */ +#define SR_EVT_GPI 0x00000800 /* General purpose input(s) */ +#define SR_EVT_BUTTON 0x00001000 +#define SR_EVT_WAKE_MASK 0x00001F00 + + + +extern void rr_set_reset_reason(volatile struct reset_registers* reset_regs, uint32_t reason); +extern bool rr_is_reset_reason_valid(volatile const struct reset_registers* reset_regs); + +extern void rr_set_start_reason(volatile struct reset_registers* reset_regs, uint32_t event); +extern bool rr_is_start_reason_valid(volatile const struct reset_registers* reset_regs); +extern void rr_start_reason_to_str(uint32_t events, char* buffer, size_t bufsize); + + +#endif /* RESET_REASON_H */