diff --git a/chip/lm4/gpio.c b/chip/lm4/gpio.c index 81b7312f03..a7c4f96163 100644 --- a/chip/lm4/gpio.c +++ b/chip/lm4/gpio.c @@ -16,7 +16,7 @@ /* 0-terminated list of GPIO bases */ -const uint32_t gpio_bases[] = { +static const uint32_t gpio_bases[] = { LM4_GPIO_A, LM4_GPIO_B, LM4_GPIO_C, LM4_GPIO_D, LM4_GPIO_E, LM4_GPIO_F, LM4_GPIO_G, LM4_GPIO_H, LM4_GPIO_J, LM4_GPIO_K, LM4_GPIO_L, LM4_GPIO_M, @@ -45,12 +45,18 @@ int gpio_pre_init(void) { volatile uint32_t scratch __attribute__((unused)); const struct gpio_info *g = gpio_list; + int is_warm = 0; int i; - /* Enable clocks to all the GPIO blocks (since we use all of them as - * GPIOs) */ - LM4_SYSTEM_RCGCGPIO |= 0x7fff; - scratch = LM4_SYSTEM_RCGCGPIO; /* Delay a few clocks */ + if (LM4_SYSTEM_RCGCGPIO == 0x7fff) { + /* This is a warm reboot */ + is_warm = 1; + } else { + /* Enable clocks to all the GPIO blocks (since we use all of + * them as GPIOs) */ + LM4_SYSTEM_RCGCGPIO |= 0x7fff; + scratch = LM4_SYSTEM_RCGCGPIO; /* Delay a few clocks */ + } /* Disable GPIO commit control for PD7 and PF0, since we don't use the * NMI pin function. */ @@ -64,6 +70,10 @@ int gpio_pre_init(void) /* Clear SSI0 alternate function on PA2:5 */ LM4_GPIO_AFSEL(LM4_GPIO_A) &= ~0x3c; + /* Mask all GPIO interrupts */ + for (i = 0; gpio_bases[i]; i++) + LM4_GPIO_IM(gpio_bases[i]) = 0; + /* Set all GPIOs to defaults */ for (i = 0; i < GPIO_COUNT; i++, g++) { @@ -76,7 +86,11 @@ int gpio_pre_init(void) LM4_GPIO_DIR(g->port) |= g->mask; /* Must set level after direction; writes to GPIO_DATA * before direction is output appear to be ignored. */ - gpio_set_level(i, g->flags & GPIO_HIGH); + /* Only set level on a cold reboot; on a warm reboot we + * want to leave things where they were or we'll shut + * off the x86. */ + if (!is_warm) + gpio_set_level(i, g->flags & GPIO_HIGH); } else { /* Input */ if (g->flags & GPIO_PULL) { diff --git a/chip/lm4/peci.c b/chip/lm4/peci.c index f3479821e2..75946a4e61 100644 --- a/chip/lm4/peci.c +++ b/chip/lm4/peci.c @@ -97,6 +97,9 @@ int peci_init(void) /* Configure GPIOs */ configure_gpios(); + /* Disable polling while reconfiguring */ + LM4_PECI_CTL = 0; + /* Calculate baud setting from desired rate, compensating for internal * and external delays. */ baud = CPU_CLOCK / (4 * PECI_BAUD_RATE) - 2; diff --git a/chip/lm4/system.c b/chip/lm4/system.c index 200cd6c73c..4915bb242f 100644 --- a/chip/lm4/system.c +++ b/chip/lm4/system.c @@ -61,7 +61,8 @@ static void check_reset_cause(void) } else if (raw_reset_cause) { reset_cause = SYSTEM_RESET_OTHER; } else { - reset_cause = SYSTEM_RESET_UNKNOWN; + /* Reset cause is still 0, so this is a warm reset. */ + reset_cause = SYSTEM_RESET_SOFT_WARM; } system_set_reset_cause(reset_cause); } diff --git a/chip/lm4/uart.c b/chip/lm4/uart.c index fa9ddb005f..652f1edf37 100644 --- a/chip/lm4/uart.c +++ b/chip/lm4/uart.c @@ -19,6 +19,15 @@ /* Baud rate for UARTs */ #define BAUD_RATE 115200 + +static int init_done; + + +int uart_init_done(void) +{ + return init_done; +} + void uart_tx_start(void) { /* Re-enable the transmit interrupt, then forcibly trigger the @@ -186,6 +195,8 @@ int uart_init(void) */ task_enable_irq(LM4_IRQ_UART0); + init_done = 1; + return EC_SUCCESS; } diff --git a/chip/lm4/watchdog.c b/chip/lm4/watchdog.c index 5a3080dcdb..74fe3e5fbd 100644 --- a/chip/lm4/watchdog.c +++ b/chip/lm4/watchdog.c @@ -114,10 +114,13 @@ int watchdog_init(int period_ms) /* Enable watchdog 0 clock */ LM4_SYSTEM_RCGCWD |= 0x1; - /* wait 3 clock cycles before using the module */ + /* Wait 3 clock cycles before using the module */ scratch = LM4_SYSTEM_RCGCWD; - /* set the time-out period */ + /* Unlock watchdog registers */ + LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD; + + /* Set the time-out period */ watchdog_period = period_ms * (CPU_CLOCK / 1000); LM4_WATCHDOG_LOAD(0) = watchdog_period; @@ -129,7 +132,10 @@ int watchdog_init(int period_ms) */ LM4_WATCHDOG_CTL(0) = 0x3; - /* lock watchdog registers against unintended accesses */ + /* Reset watchdog interrupt bits */ + LM4_WATCHDOG_ICR(0) = LM4_WATCHDOG_RIS(0); + + /* Lock watchdog registers against unintended accesses */ LM4_WATCHDOG_LOCK(0) = 0xdeaddead; /* Enable watchdog interrupt */ diff --git a/chip/stm32l/uart.c b/chip/stm32l/uart.c index 9d5dd139f7..9da697debe 100644 --- a/chip/stm32l/uart.c +++ b/chip/stm32l/uart.c @@ -20,8 +20,14 @@ /* Console USART index */ #define UARTN CONFIG_CONSOLE_UART -/* record last TX control action */ -static int should_stop; +static int init_done; /* Initialization done? */ +static int should_stop; /* Last TX control action */ + + +int uart_init_done(void) +{ + return init_done; +} void uart_tx_start(void) { @@ -138,5 +144,7 @@ int uart_init(void) /* Enable interrupts */ task_enable_irq(STM32L_IRQ_USART(UARTN)); + init_done = 1; + return EC_SUCCESS; } diff --git a/common/main.c b/common/main.c index 7af2959317..303ca059db 100644 --- a/common/main.c +++ b/common/main.c @@ -49,6 +49,10 @@ int main(void) jtag_pre_init(); gpio_pre_init(); + /* Initialize interrupts, but don't enable any of them. Note that + * task scheduling is not enabled until task_start() below. */ + task_pre_init(); + #ifdef CONFIG_FLASH flash_pre_init(); #endif @@ -64,10 +68,6 @@ int main(void) /* Set the CPU clocks / PLLs. System is now running at full speed. */ clock_init(); - /* Initialize interrupts, but don't enable any of them. Note that - * task scheduling is not enabled until task_start() below. */ - task_init(); - /* Main initialization stage. Modules may enable interrupts here. */ /* Initialize UART. uart_printf(), etc. may now be used. */ diff --git a/common/system_common.c b/common/system_common.c index e52cac05b6..d75ac188ea 100644 --- a/common/system_common.c +++ b/common/system_common.c @@ -9,6 +9,7 @@ #include "host_command.h" #include "lpc_commands.h" #include "system.h" +#include "task.h" #include "uart.h" #include "util.h" #include "version.h" @@ -64,39 +65,60 @@ const char *system_get_image_copy_string(void) } +/* Jump to what we hope is the init address of an image. This function does + * not return. */ +static void jump_to_image(uint32_t init_addr) +{ + void (*resetvec)(void) = (void(*)(void))init_addr; + + /* Flush UART output unless the UART hasn't been initialized yet */ + if (uart_init_done()) + uart_flush_output(); + + /* Disable interrupts before jump */ + interrupt_disable(); + + /* Jump to the reset vector */ + resetvec(); +} + + int system_run_image_copy(enum system_image_copy_t copy) { + uint32_t base; uint32_t init_addr; - void (*resetvec)(void); - - /* Fail if we're not in RO firmware */ - if (system_get_image_copy() != SYSTEM_IMAGE_RO) - return EC_ERROR_UNKNOWN; - - /* Load the appropriate reset vector */ - if (copy == SYSTEM_IMAGE_RW_A) - init_addr = *(uint32_t *)(CONFIG_FW_A_OFF + 4); -#ifndef CONFIG_NO_RW_B - else if (copy == SYSTEM_IMAGE_RW_B) - init_addr = *(uint32_t *)(CONFIG_FW_B_OFF + 4); -#endif - else - return EC_ERROR_UNKNOWN; /* TODO: sanity checks (crosbug.com/p/7468) * - * Fail if called outside of pre-init. - * - * Fail if reboot reason is not soft reboot. Power-on - * reset cause must run RO firmware; if it wants to move to RW - * firmware, it must go through a soft reboot first - * - * Sanity check reset vector; must be inside the appropriate - * image. */ + * For this to be allowed either WP must be disabled, or ALL of the + * following must be true: + * - We must currently be running the RO image. + * - We must still be in init (that is, before task_start(). + * - The target image must be A or B. */ - /* Jump to the reset vector */ - resetvec = (void(*)(void))init_addr; - resetvec(); + /* Load the appropriate reset vector */ + switch (copy) { + case SYSTEM_IMAGE_RO: + base = CONFIG_FW_RO_OFF; + break; + case SYSTEM_IMAGE_RW_A: + base = CONFIG_FW_A_OFF; + break; +#ifndef CONFIG_NO_RW_B + case SYSTEM_IMAGE_RW_B: + base = CONFIG_FW_B_OFF; + break; +#endif + default: + return EC_ERROR_INVAL; + } + + /* Make sure the reset vector is inside the destination image */ + init_addr = *(uint32_t *)(base + 4); + if (init_addr < base || init_addr >= base + CONFIG_FW_IMAGE_SIZE) + return EC_ERROR_UNKNOWN; + + jump_to_image(init_addr); /* Should never get here */ return EC_ERROR_UNIMPLEMENTED; @@ -215,6 +237,46 @@ static int command_version(int argc, char **argv) } DECLARE_CONSOLE_COMMAND(version, command_version); + +static int command_sysjump(int argc, char **argv) +{ + uint32_t addr; + char *e; + + /* TODO: (crosbug.com/p/7468) For this command to be allowed, WP must + * be disabled. */ + + if (argc < 2) { + uart_puts("Usage: sysjump \n"); + return EC_ERROR_INVAL; + } + + /* Handle named images */ + if (!strcasecmp(argv[1], "RO")) { + uart_puts("Jumping directly to RO image...\n"); + return system_run_image_copy(SYSTEM_IMAGE_RO); + } else if (!strcasecmp(argv[1], "A")) { + uart_puts("Jumping directly to image A...\n"); + return system_run_image_copy(SYSTEM_IMAGE_RW_A); + } else if (!strcasecmp(argv[1], "B")) { + uart_puts("Jumping directly to image B...\n"); + return system_run_image_copy(SYSTEM_IMAGE_RW_B); + } + + /* Check for arbitrary address */ + addr = strtoi(argv[1], &e, 0); + if (e && *e) { + uart_puts("Invalid image address\n"); + return EC_ERROR_INVAL; + } + uart_printf("Jumping directly to 0x%08x...\n", addr); + uart_flush_output(); + jump_to_image(addr); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(sysjump, command_sysjump); + + /*****************************************************************************/ /* Host commands */ @@ -261,3 +323,41 @@ static enum lpc_status host_command_build_info(uint8_t *data) return EC_LPC_RESULT_SUCCESS; } DECLARE_HOST_COMMAND(EC_LPC_COMMAND_GET_BUILD_INFO, host_command_build_info); + + +#ifdef CONFIG_REBOOT_EC +enum lpc_status host_command_reboot(uint8_t *data) +{ + struct lpc_params_reboot_ec *p = + (struct lpc_params_reboot_ec *)data; + + /* TODO: (crosbug.com/p/7468) For this command to be allowed, WP must + * be disabled. */ + + switch (p->target) { + case EC_LPC_IMAGE_RO: + uart_puts("[Rebooting to image RO!\n]"); + system_run_image_copy(SYSTEM_IMAGE_RO); + break; + case EC_LPC_IMAGE_RW_A: + uart_puts("[Rebooting to image A!]\n"); + system_run_image_copy(SYSTEM_IMAGE_RW_A); + break; + case EC_LPC_IMAGE_RW_B: + uart_puts("[Rebooting to image B!]\n"); + system_run_image_copy(SYSTEM_IMAGE_RW_B); + break; + default: + return EC_LPC_RESULT_ERROR; + } + + /* We normally never get down here, because we'll have jumped to + * another image. To confirm this command worked, the host will need + * to check what image is current using GET_VERSION. + * + * If we DO get down here, something went wrong in the reboot, so + * return error. */ + return EC_LPC_RESULT_ERROR; +} +DECLARE_HOST_COMMAND(EC_LPC_COMMAND_REBOOT_EC, host_command_reboot); +#endif /* CONFIG_REBOOT_EC */ diff --git a/common/vboot.c b/common/vboot.c index 307dfac2d0..f6e8ff1644 100644 --- a/common/vboot.c +++ b/common/vboot.c @@ -90,35 +90,6 @@ static int command_reboot(int argc, char **argv) } DECLARE_CONSOLE_COMMAND(reboot, command_reboot); -#ifdef CONFIG_REBOOT_EC -enum lpc_status vboot_command_reboot(uint8_t *data) { - struct lpc_params_reboot_ec *p = - (struct lpc_params_reboot_ec *)data; - - switch (p->target) { - case EC_LPC_IMAGE_RW_A: - uart_puts("Rebooting to image A!\n\n\n"); - system_set_scratchpad(SCRATCHPAD_REQUEST_A); - break; - case EC_LPC_IMAGE_RW_B: - uart_puts("Rebooting to image B!\n\n\n"); - system_set_scratchpad(SCRATCHPAD_REQUEST_B); - break; - case EC_LPC_IMAGE_RO: /* do nothing */ - uart_puts("Rebooting to image RO!\n\n\n"); - break; - default: - return EC_LPC_RESULT_ERROR; - } - - uart_flush_output(); - /* TODO - param to specify warm/cold */ - system_reset(1); - return EC_LPC_RESULT_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_LPC_COMMAND_REBOOT_EC, vboot_command_reboot); -#endif /* CONFIG_REBOOT_EC */ - /*****************************************************************************/ /* Initialization */ diff --git a/common/x86_power.c b/common/x86_power.c index e58bbdf7d2..22641452b2 100644 --- a/common/x86_power.c +++ b/common/x86_power.c @@ -12,6 +12,7 @@ #include "gpio.h" #include "lpc.h" #include "pwm.h" +#include "system.h" #include "task.h" #include "timer.h" #include "uart.h" @@ -77,7 +78,9 @@ static const char * const state_names[] = { IN_PCH_SLP_S4n_DEASSERTED | \ IN_PCH_SLP_S5n_DEASSERTED | \ IN_PCH_SLP_An_DEASSERTED) - +/* All inputs in the right state for S0 */ +#define IN_ALL_S0 (IN_PGOOD_ALWAYS_ON | IN_PGOOD_ALL_NONCORE | \ + IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED) static enum x86_state state; /* Current state */ static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */ @@ -245,12 +248,35 @@ void x86_power_interrupt(enum gpio_signal signal) int x86_power_init(void) { + /* Default to G3 state unless proven otherwise */ state = X86_G3; /* Update input state */ update_in_signals(); in_want = 0; + /* If this is a warm reboot, see if the x86 is already powered on; if + * so, leave it there instead of cycling through G3. */ + if (system_get_reset_cause() == SYSTEM_RESET_SOFT_WARM) { + if ((in_signals & IN_ALL_S0) == IN_ALL_S0) { + uart_puts("[x86 already in S0]\n"); + state = X86_S0; + } else { + /* Force all signals to their G3 states */ + uart_puts("[x86 forcing G3]\n"); + gpio_set_level(GPIO_PCH_PWROK, 0); + gpio_set_level(GPIO_ENABLE_VCORE, 0); + gpio_set_level(GPIO_PCH_RCINn, 0); + gpio_set_level(GPIO_ENABLE_VS, 0); + gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0); + gpio_set_level(GPIO_TOUCHSCREEN_RESETn, 0); + gpio_set_level(GPIO_ENABLE_1_5V_DDR, 0); + gpio_set_level(GPIO_SHUNT_1_5V_DDR, 1); + gpio_set_level(GPIO_PCH_RSMRSTn, 0); + gpio_set_level(GPIO_PCH_DPWROK, 0); + } + } + /* Enable interrupts for our GPIOs */ gpio_enable_interrupt(GPIO_PCH_BKLTEN); gpio_enable_interrupt(GPIO_PCH_SLP_An); diff --git a/core/cortex-m/cpu.h b/core/cortex-m/cpu.h index 1ec95442fe..bd94e179fe 100644 --- a/core/cortex-m/cpu.h +++ b/core/cortex-m/cpu.h @@ -16,6 +16,7 @@ /* Nested Vectored Interrupt Controller */ #define CPU_NVIC_EN(x) CPUREG(0xe000e100 + 4 * (x)) #define CPU_NVIC_DIS(x) CPUREG(0xe000e180 + 4 * (x)) +#define CPU_NVIC_UNPEND(x) CPUREG(0xe000e280 + 4 * (x)) #define CPU_NVIC_PRI(x) CPUREG(0xe000e400 + 4 * (x)) #define CPU_NVIC_APINT CPUREG(0xe000ed0c) #define CPU_NVIC_SWTRIG CPUREG(0xe000ef00) diff --git a/core/cortex-m/init.S b/core/cortex-m/init.S index f8fa196dee..a2c0d0b71e 100644 --- a/core/cortex-m/init.S +++ b/core/cortex-m/init.S @@ -302,7 +302,13 @@ vector_irq 254 @ IRQ 254 handler .global reset .thumb_func reset: - /* set the vector table on our current code */ + /* Ensure we're in privileged mode with main stack. + * Necessary if we've jumped directly here from another image + * after task_start(). */ + mov r0, #0 + msr control, r0 @ use : priv. mode / main stack / no floating point + isb @ ensure the write is done + /* Set the vector table on our current code */ ldr r1, =vectors ldr r2, =0xE000ED08 /* VTABLE register in SCB*/ str r1, [r2] diff --git a/core/cortex-m/task.c b/core/cortex-m/task.c index 41b91de348..246147b7c7 100644 --- a/core/cortex-m/task.c +++ b/core/cortex-m/task.c @@ -299,18 +299,28 @@ void task_trigger_irq(int irq) } -/** - * Enable all used IRQ in the NVIC and set their priorities - * as defined by the DECLARE_IRQ statements - */ +/* Initialize IRQs in the NVIC and set their priorities as defined by the + * DECLARE_IRQ statements. */ static void __nvic_init_irqs(void) { - /* get the IRQ priorities section from the linker */ + /* Get the IRQ priorities section from the linker */ extern struct irq_priority __irqprio[]; extern struct irq_priority __irqprio_end[]; int irq_count = __irqprio_end - __irqprio; int i; + /* Mask and clear all pending interrupts */ + for (i = 0; i < 5; i++) { + CPU_NVIC_DIS(i) = 0xffffffff; + CPU_NVIC_UNPEND(i) = 0xffffffff; + } + + /* Re-enable global interrupts in case they're disabled. On a reboot, + * they're already enabled; if we've jumped here from another image, + * they're not. */ + interrupt_enable(); + + /* Set priorities */ for (i = 0; i < irq_count; i++) { uint8_t irq = __irqprio[i].irq; uint8_t prio = __irqprio[i].priority; @@ -405,7 +415,7 @@ DECLARE_CONSOLE_COMMAND(taskready, command_task_ready); #endif -int task_init(void) +int task_pre_init(void) { /* sanity checks about static task invariants */ BUILD_ASSERT(TASK_ID_COUNT <= sizeof(unsigned) * 8); diff --git a/include/task.h b/include/task.h index 83c1d73555..b5e57e736b 100644 --- a/include/task.h +++ b/include/task.h @@ -74,7 +74,7 @@ uint32_t task_wait_msg(int timeout_us); void task_resched_if_needed(void *excep_return); /* Initializes tasks and interrupt controller. */ -int task_init(void); +int task_pre_init(void); /* Starts task scheduling. */ int task_start(void); diff --git a/include/uart.h b/include/uart.h index 1d43206731..dc64d9ad6f 100644 --- a/include/uart.h +++ b/include/uart.h @@ -14,6 +14,8 @@ /* Initializes the UART module. */ int uart_init(void); +/* Return non-zero if UART init has completed. */ +int uart_init_done(void); /* Enables console mode if !=0. In console mode: * - Input is echoed