diff --git a/board/nm/nmhw21/board.c b/board/nm/nmhw21/board.c index 1ba63a67ce..ba13444836 100644 --- a/board/nm/nmhw21/board.c +++ b/board/nm/nmhw21/board.c @@ -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; + case PRESSED: + 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 */ blink_led(1); } - else if (counter == 12000) { - /* Indicate recovery boot threshold */ - blink_led(2); - } - } while (counter < 12000); + 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 (counter < 2000) { - /* Don't do anything for duration < 2s */ + 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; } - 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 *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; } }