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_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 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)
|
||||
/* Hardware version information of mainboard, loaded by get_hw_version() */
|
||||
static int hw_ver = -1;
|
||||
|
|
@ -1109,53 +1119,431 @@ static void blink_led(int pulses)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void check_reset_button(void)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
/* Check how long button is pressed */
|
||||
do {
|
||||
if (!get_button_state())
|
||||
#define RESET_CHECK_PERIOD (100) /* 100 ms */
|
||||
|
||||
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;
|
||||
|
||||
udelay(100*1000); /* 100ms */
|
||||
counter += 100;
|
||||
|
||||
if (counter == 2000) {
|
||||
case PRESSED:
|
||||
if (button) {
|
||||
duration += RESET_CHECK_PERIOD;
|
||||
if (duration == DURATION_FACTORY_RESET) {
|
||||
/* Indicate factory reset threshold */
|
||||
blink_led(1);
|
||||
}
|
||||
else if (counter == 12000) {
|
||||
/* Indicate recovery boot threshold */
|
||||
else if (duration == DURATION_RECOVERY_BOOT) {
|
||||
/* Indicate recovery boot threshold and leave state */
|
||||
blink_led(2);
|
||||
act = RECOVERY_BOOT;
|
||||
state = DONE;
|
||||
}
|
||||
} while (counter < 12000);
|
||||
}
|
||||
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) {
|
||||
/* Don't do anything for duration < 2s */
|
||||
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_length(int time, bool rx_line)
|
||||
{
|
||||
/* Do factory reset for duration between 2s and 12s */
|
||||
/*
|
||||
* 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 */
|
||||
blink_led(1);
|
||||
}
|
||||
else if (duration == DURATION_RECOVERY_BOOT) {
|
||||
/* Indicate recovery boot threshold and leave state */
|
||||
blink_led(2);
|
||||
act = RECOVERY_BOOT; /* TODO: maybe remain in state until break removed */
|
||||
state = DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (wait_time > 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
/*
|
||||
* 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 *bootargs = getenv("bootargs");
|
||||
|
||||
if (bootargs == 0) bootargs = "";
|
||||
|
||||
puts("Do factory reset during boot...\n");
|
||||
|
||||
if (bootargs == 0) bootargs = "";
|
||||
strncpy(new_bootargs, bootargs, sizeof(new_bootargs));
|
||||
strncat(new_bootargs, " factory-reset", sizeof(new_bootargs));
|
||||
|
||||
setenv("bootargs", new_bootargs);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Boot into recovery for duration > 12s */
|
||||
|
||||
case RECOVERY_BOOT:
|
||||
puts("Booting recovery image...\n");
|
||||
|
||||
/* Set bootcmd to run recovery */
|
||||
setenv("bootcmd", "run recovery");
|
||||
break;
|
||||
|
||||
default:
|
||||
set_indicator_led(0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue