Merge "Support warm reboot from one EC image to another."

This commit is contained in:
Gerrit
2012-03-20 09:26:16 -07:00
committed by Gerrit Code Review
15 changed files with 239 additions and 80 deletions

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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. */

View File

@@ -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 <RO | A | B | addr>\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 */

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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)

View File

@@ -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]

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 <enable>!=0. In console mode:
* - Input is echoed