hw21/26: add reset/start reason detection (#67)
Co-authored-by: Rene Straub <straub@see5.ch> Reviewed-on: https://git.netmodule.intranet/nmrouter/u-boot/pulls/67 Co-Authored-By: Rene Straub <rene.straub@netmodule.com> Co-Committed-By: Rene Straub <rene.straub@netmodule.com>
This commit is contained in:
parent
e8475b4f80
commit
2997b916c1
|
|
@ -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 <ver.rev> */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <common.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 */
|
||||
Loading…
Reference in New Issue