nmhw21: board: add user reset option via rs232
allow invoking factory reset and recovery boot via:
- uart break, followed by command ('r', 'f')
- using uart rxd as reset line
Co-authored-by: Rene Straub <rene.straub@netmodule.com>
Reviewed-by: Patrick Zysset <patrick.zysset@netmodule.com>
This commit is contained in:
parent
a3fd0500ec
commit
7b1000b59d
|
|
@ -104,6 +104,7 @@ DECLARE_GLOBAL_DATA_PTR;
|
||||||
#define GPIO_SIM_SEL GPIO_TO_PIN(1, 12)
|
#define GPIO_SIM_SEL GPIO_TO_PIN(1, 12)
|
||||||
#define GPIO_SIM_PRES_N GPIO_TO_PIN(2, 16)
|
#define GPIO_SIM_PRES_N GPIO_TO_PIN(2, 16)
|
||||||
|
|
||||||
|
#define GPIO_UART2_RX GPIO_TO_PIN(0, 2) /* UART Rx Pin as GPIO */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -133,6 +134,15 @@ DECLARE_GLOBAL_DATA_PTR;
|
||||||
#define DDR3_CLOCK_FREQUENCY (400)
|
#define DDR3_CLOCK_FREQUENCY (400)
|
||||||
|
|
||||||
|
|
||||||
|
#define DURATION_FACTORY_RESET (2000) /* Reset button time to initiate factory reset */
|
||||||
|
#define DURATION_RECOVERY_BOOT (12000) /* Reset button time to initiate recovery boot */
|
||||||
|
|
||||||
|
#if (DURATION_RECOVERY_BOOT < (DURATION_FACTORY_RESET+1000))
|
||||||
|
#error Recovery boot time must be larger than factory reset + 1 second
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if !defined(CONFIG_SPL_BUILD)
|
#if !defined(CONFIG_SPL_BUILD)
|
||||||
/* Hardware version information of mainboard, loaded by get_hw_version() */
|
/* Hardware version information of mainboard, loaded by get_hw_version() */
|
||||||
static int hw_ver = -1;
|
static int hw_ver = -1;
|
||||||
|
|
@ -1109,53 +1119,431 @@ static void blink_led(int pulses)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_reset_button(void)
|
|
||||||
{
|
|
||||||
int counter = 0;
|
|
||||||
|
|
||||||
/* Check how long button is pressed */
|
#define RESET_CHECK_PERIOD (100) /* 100 ms */
|
||||||
do {
|
|
||||||
if (!get_button_state())
|
typedef enum _action_t
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
WAIT,
|
||||||
|
FACTORY_RESET,
|
||||||
|
RECOVERY_BOOT
|
||||||
|
} action_t;
|
||||||
|
|
||||||
|
|
||||||
|
static void uart2_rx_gpio_pin_mux(void)
|
||||||
|
{
|
||||||
|
/* Configure UART2 rxd pin as GPIO */
|
||||||
|
static struct module_pin_mux uart2_gpio_pin_mux[] = {
|
||||||
|
{OFFSET(spi0_sclk), (MODE(7) | PULLUDEN | PULLUP_EN | RXACTIVE)}, /* (A17) uart2_rxd = gpio0_2 */
|
||||||
|
{-1}
|
||||||
|
};
|
||||||
|
|
||||||
|
configure_module_pin_mux(uart2_gpio_pin_mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart2_rx_rx_pin_mux(void)
|
||||||
|
{
|
||||||
|
/* Configure UART2 rxd pin as rxd function */
|
||||||
|
static struct module_pin_mux uart2_pin_mux[] = {
|
||||||
|
{OFFSET(spi0_sclk), (MODE(1) | PULLUDEN | PULLUP_EN | RXACTIVE)}, /* (A17) uart2_rxd */
|
||||||
|
{-1}
|
||||||
|
};
|
||||||
|
|
||||||
|
configure_module_pin_mux(uart2_pin_mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_rxd_state(void)
|
||||||
|
{
|
||||||
|
uart2_rx_gpio_pin_mux();
|
||||||
|
|
||||||
|
(void)gpio_request(GPIO_UART2_RX, "");
|
||||||
|
int val = gpio_get_value(GPIO_UART2_RX);
|
||||||
|
gpio_free(GPIO_UART2_RX);
|
||||||
|
|
||||||
|
uart2_rx_rx_pin_mux();
|
||||||
|
|
||||||
|
return (val == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_button(int time, bool button)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Hardware button logic
|
||||||
|
*
|
||||||
|
* input:
|
||||||
|
* button: current button press state (true = pressed)
|
||||||
|
* logic:
|
||||||
|
* - if button is not pressed initially, return action NONE for all
|
||||||
|
* invocations (state NO_ACTION)
|
||||||
|
* - if button pressed initially, return action WAIT and measure duration
|
||||||
|
* of press (state PRESSED).
|
||||||
|
* - while button is pressed, measure time, indicate actions with LED,
|
||||||
|
* - when button is released, check time and decide on factory reset
|
||||||
|
* or recovery boot.
|
||||||
|
*/
|
||||||
|
typedef enum _state_t { INIT = 0, PRESSED, DONE } state_t;
|
||||||
|
|
||||||
|
static state_t state = INIT; /* Current state */
|
||||||
|
static action_t act = NONE; /* Action to report to user, returned at function end */
|
||||||
|
static int duration = 0;
|
||||||
|
|
||||||
|
if (time == 0) {
|
||||||
|
state = INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Remove printf, puts */
|
||||||
|
/* printf("button: state %d button %d -> ", state, button); */
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case INIT:
|
||||||
|
if (button) {
|
||||||
|
/* Pressed, start to measure time until released */
|
||||||
|
duration = 0;
|
||||||
|
act = WAIT;
|
||||||
|
state = PRESSED;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* If not pressed, no further action is possible */
|
||||||
|
act = NONE;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
udelay(100*1000); /* 100ms */
|
case PRESSED:
|
||||||
counter += 100;
|
if (button) {
|
||||||
|
duration += RESET_CHECK_PERIOD;
|
||||||
|
if (duration == DURATION_FACTORY_RESET) {
|
||||||
|
/* Indicate factory reset threshold */
|
||||||
|
blink_led(1);
|
||||||
|
}
|
||||||
|
else if (duration == DURATION_RECOVERY_BOOT) {
|
||||||
|
/* Indicate recovery boot threshold and leave state */
|
||||||
|
blink_led(2);
|
||||||
|
act = RECOVERY_BOOT;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Check how long button was pressed */
|
||||||
|
if (duration >= DURATION_RECOVERY_BOOT) {
|
||||||
|
act = RECOVERY_BOOT;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
else if (duration >= DURATION_FACTORY_RESET) {
|
||||||
|
act = FACTORY_RESET;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* too short, nothing to do */
|
||||||
|
act = NONE;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (counter == 2000) {
|
case DONE:
|
||||||
|
/* Final state when an action has been chosen */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* printf("act %d\n", act); */
|
||||||
|
|
||||||
|
return act;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_break_length(int time, bool rx_line)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* RS232 RxD Reset input logic
|
||||||
|
*
|
||||||
|
* Treats RxD line as reset input. Line is considered active when
|
||||||
|
* break condition applies (RS232 line > 3.0 V)
|
||||||
|
*
|
||||||
|
* input:
|
||||||
|
* rx_line: true if rxd input is '0' (break condition)
|
||||||
|
* logic:
|
||||||
|
* see explanation in check_button()
|
||||||
|
* additional logic is added to debounce input
|
||||||
|
*/
|
||||||
|
#define BREAK_DEB_TIME 500
|
||||||
|
|
||||||
|
typedef enum _state_t { WAIT_BREAK = 0, HAVE_BREAK, DONE } state_t;
|
||||||
|
|
||||||
|
static state_t state = WAIT_BREAK; /* Current state */
|
||||||
|
static action_t act = NONE; /* Action to report to user, returned at function end */
|
||||||
|
static int duration = 0;
|
||||||
|
static int wait_time = 0;
|
||||||
|
|
||||||
|
if (time == 0) {
|
||||||
|
state = WAIT_BREAK;
|
||||||
|
wait_time = BREAK_DEB_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Remove */
|
||||||
|
/* printf("break length: state %d have_break_cond %d duration %d -> ", state, have_break_cond, duration); */
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case WAIT_BREAK:
|
||||||
|
if (rx_line) {
|
||||||
|
/* Break detected, start to measure time */
|
||||||
|
duration = 0;
|
||||||
|
wait_time = BREAK_DEB_TIME;
|
||||||
|
act = WAIT;
|
||||||
|
state = HAVE_BREAK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait_time > 0) {
|
||||||
|
wait_time -= RESET_CHECK_PERIOD;
|
||||||
|
act = WAIT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* If no break seen, stop here */
|
||||||
|
act = NONE;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HAVE_BREAK:
|
||||||
|
if (rx_line) {
|
||||||
|
wait_time = BREAK_DEB_TIME;
|
||||||
|
duration += RESET_CHECK_PERIOD;
|
||||||
|
|
||||||
|
if (duration == DURATION_FACTORY_RESET) {
|
||||||
/* Indicate factory reset threshold */
|
/* Indicate factory reset threshold */
|
||||||
blink_led(1);
|
blink_led(1);
|
||||||
}
|
}
|
||||||
else if (counter == 12000) {
|
else if (duration == DURATION_RECOVERY_BOOT) {
|
||||||
/* Indicate recovery boot threshold */
|
/* Indicate recovery boot threshold and leave state */
|
||||||
blink_led(2);
|
blink_led(2);
|
||||||
}
|
act = RECOVERY_BOOT; /* TODO: maybe remain in state until break removed */
|
||||||
} while (counter < 12000);
|
state = DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (counter < 2000) {
|
if (wait_time > 0) {
|
||||||
/* Don't do anything for duration < 2s */
|
wait_time -= RESET_CHECK_PERIOD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* puts("break condition released\n"); */
|
||||||
|
|
||||||
|
/* Check how long button was pressed */
|
||||||
|
if (duration >= DURATION_RECOVERY_BOOT) {
|
||||||
|
act = RECOVERY_BOOT;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
else if (duration >= DURATION_FACTORY_RESET) {
|
||||||
|
act = FACTORY_RESET;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* too short, nothing to do */
|
||||||
|
act = NONE;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DONE:
|
||||||
|
/* Final state when an action has been chosen */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (counter < 12000)
|
|
||||||
|
/* printf("act %d\n", act); */
|
||||||
|
|
||||||
|
return act;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_break_command(int time, int has_input, int c)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Break command logic
|
||||||
|
*
|
||||||
|
* Tries to receive the break button (system magic key) and a command to execute.
|
||||||
|
*
|
||||||
|
* input:
|
||||||
|
* has_input: true if a character is available
|
||||||
|
* c: character code (can be 0x00 for break condition, system magic key)
|
||||||
|
* logic:
|
||||||
|
* - waits up to 500 ms to receive a break character. if character is not seen,
|
||||||
|
* stops here, action = NONE. if character is detected goes to next state
|
||||||
|
* - waits up to 500 ms to receive command character. if character is not seen,
|
||||||
|
* stops here, action = NONE. if character is detected define action accordingly
|
||||||
|
* 'f': factory reset
|
||||||
|
* 'r': recovery boot
|
||||||
|
*/
|
||||||
|
#define BREAK_WAIT_TIME 1000 /* Initial wait time for break character */
|
||||||
|
#define CHARACTER_WAIT_TIME 1500 /* Subsequent wait time for command */
|
||||||
|
|
||||||
|
typedef enum _state_t { WAIT_BREAK = 1, WAIT_COMMAND, DONE } state_t;
|
||||||
|
|
||||||
|
static state_t state = WAIT_BREAK; /* Current state */
|
||||||
|
static action_t act = NONE; /* Action to report to user, returned at function end */
|
||||||
|
|
||||||
|
static int wait_time;
|
||||||
|
|
||||||
|
/* TODO: remove */
|
||||||
|
/* printf("break command: state %d, input %d %02x -> ", state, has_input, c); */
|
||||||
|
|
||||||
|
if (time == 0) {
|
||||||
|
state = WAIT_BREAK;
|
||||||
|
wait_time = BREAK_WAIT_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case WAIT_BREAK: /* Wait for break indication */
|
||||||
|
act = WAIT;
|
||||||
|
if (has_input) {
|
||||||
|
if (c == 0x00) {
|
||||||
|
/* puts("break detected\n"); */
|
||||||
|
wait_time = CHARACTER_WAIT_TIME;
|
||||||
|
state = WAIT_COMMAND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait_time > 0) {
|
||||||
|
wait_time -= RESET_CHECK_PERIOD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* No break received, nothing to do from our side */
|
||||||
|
act = NONE;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WAIT_COMMAND:
|
||||||
|
if (has_input) {
|
||||||
|
wait_time = CHARACTER_WAIT_TIME;
|
||||||
|
if (c == 0x00) {
|
||||||
|
/* puts("break detected\n"); */
|
||||||
|
act = WAIT;
|
||||||
|
}
|
||||||
|
else if (c == 'f') {
|
||||||
|
blink_led(1);
|
||||||
|
act = FACTORY_RESET;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
else if (c == 'r') {
|
||||||
|
blink_led(2);
|
||||||
|
act = RECOVERY_BOOT;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Unknown command */
|
||||||
|
act = NONE;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait_time > 0) {
|
||||||
|
wait_time -= RESET_CHECK_PERIOD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* puts("character timeout\n"); */
|
||||||
|
act = NONE;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DONE:
|
||||||
|
/* Final state when an action has been chosen */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* printf("act %d\n", act); */
|
||||||
|
|
||||||
|
return act;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_reset_button(void)
|
||||||
{
|
{
|
||||||
/* Do factory reset for duration between 2s and 12s */
|
/*
|
||||||
|
* Runs state machines of all reset sources to find out if one detects
|
||||||
|
* an action.
|
||||||
|
*
|
||||||
|
* SMs return
|
||||||
|
* - WAIT if they are still trying to get a result.
|
||||||
|
* - NONE if they know there is no action required.
|
||||||
|
* - FACTORY_RESET, RECOVERY_BOOT if detected.
|
||||||
|
*/
|
||||||
|
action_t action = NONE;
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
action_t a_[3];
|
||||||
|
bool button;
|
||||||
|
bool rx_line;
|
||||||
|
int has_input;
|
||||||
|
int c = -1;
|
||||||
|
|
||||||
|
/* Get state machine inputs */
|
||||||
|
button = get_button_state();
|
||||||
|
rx_line = get_rxd_state();
|
||||||
|
has_input = tstc();
|
||||||
|
if (has_input) {
|
||||||
|
c = getc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* run processing state machines */
|
||||||
|
a_[0] = NONE;
|
||||||
|
a_[1] = NONE;
|
||||||
|
a_[2] = NONE;
|
||||||
|
a_[0] = check_button(counter, button);
|
||||||
|
a_[1] = check_break_length(counter, rx_line);
|
||||||
|
a_[2] = check_break_command(counter, has_input, c);
|
||||||
|
|
||||||
|
/* if all SMs are sure there is not action, stop here */
|
||||||
|
if ((a_[0] == NONE) && (a_[1] == NONE) && (a_[2] == NONE)) {
|
||||||
|
action = NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if one of the SMs has an action, stop here */
|
||||||
|
for (int i=0; i<3; i++) {
|
||||||
|
if ((a_[i] == FACTORY_RESET) || (a_[i] == RECOVERY_BOOT)) {
|
||||||
|
action = a_[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mdelay(RESET_CHECK_PERIOD);
|
||||||
|
counter += RESET_CHECK_PERIOD;
|
||||||
|
|
||||||
|
} while ((action != FACTORY_RESET) && (action != RECOVERY_BOOT));
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case FACTORY_RESET: {
|
||||||
char new_bootargs[512];
|
char new_bootargs[512];
|
||||||
char *bootargs = getenv("bootargs");
|
char *bootargs = getenv("bootargs");
|
||||||
|
|
||||||
if (bootargs == 0) bootargs = "";
|
|
||||||
|
|
||||||
puts("Do factory reset during boot...\n");
|
puts("Do factory reset during boot...\n");
|
||||||
|
|
||||||
|
if (bootargs == 0) bootargs = "";
|
||||||
strncpy(new_bootargs, bootargs, sizeof(new_bootargs));
|
strncpy(new_bootargs, bootargs, sizeof(new_bootargs));
|
||||||
strncat(new_bootargs, " factory-reset", sizeof(new_bootargs));
|
strncat(new_bootargs, " factory-reset", sizeof(new_bootargs));
|
||||||
|
|
||||||
setenv("bootargs", new_bootargs);
|
setenv("bootargs", new_bootargs);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
case RECOVERY_BOOT:
|
||||||
/* Boot into recovery for duration > 12s */
|
|
||||||
puts("Booting recovery image...\n");
|
puts("Booting recovery image...\n");
|
||||||
|
|
||||||
/* Set bootcmd to run recovery */
|
|
||||||
setenv("bootcmd", "run recovery");
|
setenv("bootcmd", "run recovery");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
set_indicator_led(0, 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue