diff options
-rw-r--r-- | docs/zephyr_init.md | 53 | ||||
-rw-r--r-- | zephyr/Kconfig | 2 | ||||
-rw-r--r-- | zephyr/Kconfig.system | 18 | ||||
-rw-r--r-- | zephyr/drivers/cros_bbram/Kconfig | 16 | ||||
-rw-r--r-- | zephyr/drivers/cros_bbram/cros_bbram_npcx.c | 8 | ||||
-rw-r--r-- | zephyr/drivers/cros_system/Kconfig | 13 | ||||
-rw-r--r-- | zephyr/drivers/cros_system/cros_system_npcx.c | 5 | ||||
-rw-r--r-- | zephyr/shim/chip/npcx/Kconfig.npcx | 18 | ||||
-rw-r--r-- | zephyr/shim/chip/npcx/system.c | 78 | ||||
-rw-r--r-- | zephyr/shim/src/system.c | 173 |
10 files changed, 341 insertions, 43 deletions
diff --git a/docs/zephyr_init.md b/docs/zephyr_init.md new file mode 100644 index 0000000000..8822736efb --- /dev/null +++ b/docs/zephyr_init.md @@ -0,0 +1,53 @@ +# Zephyr OS-based EC Initialization Order + +Zephyr provides Z_INIT_ENTRY_DEFINE() & the extend macro to install the initial +function. The initialize flow for different levels would be like the following +(not very detailed): +* architecture-specific initialization +* `PRE_KERNEL_1` level +* `PRE_KERNEL_2` level +* `POST_KERNEL` level +* `APPLICATION` level +* main() + +The kernel and driver initial functions separate into specific initialize +levels. It couldn't put all initial functions in main() for the Zephyr OS-based +EC. It is also hard to maintain those initial priority which separates into +different files. + +This file defines some Zephyr OS-based EC initial priorities which have critical +sequence requirement for initializing: + +## PRE_KERNEL_1 +* Priority (0-9) - Reserved for system testability: + + The highest priority could be used in zephyr. Don't use it when system + development. Buffer it for the following system development & testing. + +* Priority (10-19) - Chip level system pre-initialization: + + These priorities in this range are used for critical chip initialization, + including determining the reset cause and initializing the battery-backed + RAM driver. Most chip drivers should only be initialized after + `PLATFORM_EC_SYSTEM_PRE_INIT`. + +* Priority (20) - PLATFORM_EC_SYSTEM_PRE_INIT: + + At this initialization priority, the CROS system checks the reset cause and + initializing the system reset flags. Any chip level drivers related to + determining the reset type must be at a higher priority. + +* TODO + +## PRE_KERNEL_2 +* TODO + +## POST_KERNEL +* TODO + +## APPLICATION +* TODO + +## main() +* TODO +* Start the tasks. diff --git a/zephyr/Kconfig b/zephyr/Kconfig index b054d85372..4c3dc5849a 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -31,6 +31,7 @@ menuconfig PLATFORM_EC if PLATFORM_EC +rsource "shim/chip/npcx/Kconfig.npcx" rsource "Kconfig.adc" rsource "Kconfig.battery" rsource "Kconfig.console" @@ -39,6 +40,7 @@ rsource "Kconfig.keyboard" rsource "Kconfig.led" rsource "Kconfig.powerseq" rsource "Kconfig.motionsense" +rsource "Kconfig.system" rsource "Kconfig.tasks" rsource "Kconfig.temperature" rsource "Kconfig.usbc" diff --git a/zephyr/Kconfig.system b/zephyr/Kconfig.system new file mode 100644 index 0000000000..d8ae87b149 --- /dev/null +++ b/zephyr/Kconfig.system @@ -0,0 +1,18 @@ +# Copyright 2021 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if PLATFORM_EC + +config PLATFORM_EC_SYSTEM_PRE_INIT_PRIORITY + int "System pre-initialization priority" + default 20 + range 0 99 + help + This defines the initialization priority for the CROS + system_pre_init() function. system_pre_init() reads chip level reset + cause and stores it into the system reset flags. All drivers, except + those critical to determining the reset type, should be initialized at + lower priority so that the system reset flags are valid. + +endif # PLATFORM_EC diff --git a/zephyr/drivers/cros_bbram/Kconfig b/zephyr/drivers/cros_bbram/Kconfig index ba7ce8b1ad..43850b032a 100644 --- a/zephyr/drivers/cros_bbram/Kconfig +++ b/zephyr/drivers/cros_bbram/Kconfig @@ -7,4 +7,18 @@ menuconfig CROS_BBRAM_NPCX depends on SOC_FAMILY_NPCX default y help - This options enables stuff.
\ No newline at end of file + This options enables stuff. + +if CROS_BBRAM_NPCX + +config CROS_BBRAM_NPCX_INIT_PRIORITY + int "cros_bbram npcx initialization priority" + default 11 + range 10 19 + help + This sets the npcx cros_bbram driver initialization priority. NPCX + chip uses BBRAM to save some system information that persists across + chip resets. The priority should be higher than + SYSTEM_PRE_INIT_PRIORITY & lower than CROS_SYSTEM_NPCX_INIT_PRIORITY. + +endif # CROS_BBRAM_NPCX diff --git a/zephyr/drivers/cros_bbram/cros_bbram_npcx.c b/zephyr/drivers/cros_bbram/cros_bbram_npcx.c index f6fa64fdd2..20698df85b 100644 --- a/zephyr/drivers/cros_bbram/cros_bbram_npcx.c +++ b/zephyr/drivers/cros_bbram/cros_bbram_npcx.c @@ -126,6 +126,12 @@ static int bbram_npcx_init(const struct device *dev) /* * The priority of bbram_npcx_init() should lower than cros_system_npcx_init(). */ +#if (CONFIG_CROS_BBRAM_NPCX_INIT_PRIORITY <= \ + CONFIG_CROS_SYSTEM_NPCX_INIT_PRIORITY) +#error CONFIG_CROS_BBRAM_NPCX_INIT_PRIORITY must greater than \ + CONFIG_CROS_SYSTEM_NPCX_INIT_PRIORITY +#endif + #define CROS_BBRAM_INIT(inst) \ static struct { \ } cros_bbram_data_##inst; \ @@ -137,7 +143,7 @@ static int bbram_npcx_init(const struct device *dev) DEVICE_DEFINE(cros_bbram_npcx_##inst, DT_INST_LABEL(inst), \ bbram_npcx_init, NULL, &cros_bbram_data_##inst, \ &cros_bbram_cfg_##inst, PRE_KERNEL_1, \ - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + CONFIG_CROS_BBRAM_NPCX_INIT_PRIORITY, \ &cros_bbram_npcx_driver_api); DT_INST_FOREACH_STATUS_OKAY(CROS_BBRAM_INIT); diff --git a/zephyr/drivers/cros_system/Kconfig b/zephyr/drivers/cros_system/Kconfig index e1e7163f99..09d47ad7e7 100644 --- a/zephyr/drivers/cros_system/Kconfig +++ b/zephyr/drivers/cros_system/Kconfig @@ -11,3 +11,16 @@ menuconfig CROS_SYSTEM_NPCX processors. Currently, Zephyr doesn't provide the system related API. The cros system driver provides the low-level driver related to chromium ec system functionality. + +if CROS_SYSTEM_NPCX + +config CROS_SYSTEM_NPCX_INIT_PRIORITY + int "cros_system npcx initialization priority" + default 10 + range 10 19 + help + This sets the npcx cros_system driver initialization priority. The + cros_system driver provides access to the NPCX reset cause and must be + higher priority than CONFIG_SYSTEM_PRE_INIT_PRIORITY. + +endif # CROS_SYSTEM_NPCX diff --git a/zephyr/drivers/cros_system/cros_system_npcx.c b/zephyr/drivers/cros_system/cros_system_npcx.c index b21219355d..48e33188cf 100644 --- a/zephyr/drivers/cros_system/cros_system_npcx.c +++ b/zephyr/drivers/cros_system/cros_system_npcx.c @@ -128,12 +128,13 @@ static const struct cros_system_driver_api cros_system_driver_npcx_api = { }; /* - * The priority of cros_system_npcx_init() should be lower than watchdog init + * The priority of cros_system_npcx_init() should be higher than watchdog init * for reset cause check. */ DEVICE_DEFINE(cros_system_npcx_0, "CROS_SYSTEM", cros_system_npcx_init, NULL, &cros_system_npcx_dev_data, &cros_system_dev_cfg, PRE_KERNEL_1, - 30, &cros_system_driver_npcx_api); + CONFIG_CROS_SYSTEM_NPCX_INIT_PRIORITY, + &cros_system_driver_npcx_api); #define HAL_DBG_REG_BASE_ADDR \ ((struct dbg_reg *)DT_REG_ADDR(DT_INST(0, nuvoton_npcx_cros_dbg))) diff --git a/zephyr/shim/chip/npcx/Kconfig.npcx b/zephyr/shim/chip/npcx/Kconfig.npcx new file mode 100644 index 0000000000..1107a846a8 --- /dev/null +++ b/zephyr/shim/chip/npcx/Kconfig.npcx @@ -0,0 +1,18 @@ +# Copyright 2021 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if PLATFORM_EC + +config CROS_SYSTEM_NPCX_PRE_INIT_PRIORITY + int "System pre-initialization priority" + default 15 + range 10 19 + depends on SOC_FAMILY_NPCX + help + This sets the priority of the NPCX chip system initialization. The + chip system initialization verifies the integrity of the BBRAM and + must be a lower priority than CONFIG_CROS_BBRAM_NPCX_INIT_PRIORITY and + must be a higher priority than PLATFORM_EC_SYSTEM_PRE_INIT. + +endif # PLATFORM_EC diff --git a/zephyr/shim/chip/npcx/system.c b/zephyr/shim/chip/npcx/system.c index d776733827..96fccfebca 100644 --- a/zephyr/shim/chip/npcx/system.c +++ b/zephyr/shim/chip/npcx/system.c @@ -4,10 +4,8 @@ */ #include <drivers/cros_bbram.h> -#include <drivers/cros_system.h> #include <logging/log.h> -#include "watchdog.h" #include "system.h" #define GET_BBRAM_OFFSET(node) \ @@ -16,7 +14,7 @@ LOG_MODULE_REGISTER(shim_npcx_system, LOG_LEVEL_ERR); -const struct device *bbram_dev; +const static struct device *bbram_dev; void chip_save_reset_flags(uint32_t flags) { @@ -44,51 +42,53 @@ uint32_t chip_read_reset_flags(void) return flags; } -void system_reset(int flags) +void chip_bbram_status_check(void) { - const struct device *sys_dev = device_get_binding("CROS_SYSTEM"); - int err; - uint32_t save_flags; - - if (!sys_dev) - LOG_ERR("sys_dev get binding failed"); - - /* Disable interrupts to avoid task swaps during reboot */ - interrupt_disable_all(); - - /* Get flags to be saved in BBRAM */ - system_encode_save_flags(flags, &save_flags); - - /* Store flags to battery backed RAM. */ - chip_save_reset_flags(save_flags); - - /* If WAIT_EXT is set, then allow 10 seconds for external reset */ - if (flags & SYSTEM_RESET_WAIT_EXT) { - int i; - - /* Wait 10 seconds for external reset */ - for (i = 0; i < 1000; i++) { - watchdog_reload(); - udelay(10000); - } + if (!bbram_dev) { + LOG_DBG("bbram_dev doesn't binding"); + return; } - err = cros_system_soc_reset(sys_dev); - - if (err < 0) - LOG_ERR("soc reset failed"); - - /* should never return */ - while (1) - ; + if (cros_bbram_get_ibbr(bbram_dev)) { + LOG_ERR("VBAT power drop!"); + cros_bbram_reset_ibbr(bbram_dev); + } + if (cros_bbram_get_vsby(bbram_dev)) { + LOG_ERR("VSBY power drop!"); + cros_bbram_reset_vsby(bbram_dev); + } + if (cros_bbram_get_vcc1(bbram_dev)) { + LOG_ERR("VCC1 power drop!"); + cros_bbram_reset_vcc1(bbram_dev); + } } static int chip_system_init(const struct device *unused) { ARG_UNUSED(unused); + /* + * NPCX chip uses BBRAM to save the reset flag. Binding & check BBRAM + * here. + */ bbram_dev = device_get_binding(DT_LABEL(DT_NODELABEL(bbram))); + if (!bbram_dev) { + LOG_ERR("bbram_dev gets binding failed"); + return -1; + } + + /* check the BBRAM status */ + chip_bbram_status_check(); + return 0; } - -SYS_INIT(chip_system_init, PRE_KERNEL_1, 50); +/* + * The priority should be lower than CROS_BBRAM_NPCX_INIT_PRIORITY. + */ +#if (CONFIG_CROS_SYSTEM_NPCX_PRE_INIT_PRIORITY <= \ + CONFIG_CROS_BBRAM_NPCX_INIT_PRIORITY) +#error CONFIG_CROS_SYSTEM_NPCX_PRE_INIT_PRIORITY must greater than \ + CONFIG_CROS_BBRAM_NPCX_INIT_PRIORITY +#endif +SYS_INIT(chip_system_init, PRE_KERNEL_1, + CONFIG_CROS_SYSTEM_NPCX_PRE_INIT_PRIORITY); diff --git a/zephyr/shim/src/system.c b/zephyr/shim/src/system.c index bf421ad8ae..1e17dcea13 100644 --- a/zephyr/shim/src/system.c +++ b/zephyr/shim/src/system.c @@ -5,18 +5,24 @@ #include <device.h> #include <drivers/cros_bbram.h> +#include <drivers/cros_system.h> +#include <logging/log.h> #include "bbram.h" #include "common.h" #include "cros_version.h" #include "system.h" +#include "watchdog.h" #define BBRAM_REGION_PD0 DT_PATH(named_bbram_regions, pd0) #define BBRAM_REGION_PD1 DT_PATH(named_bbram_regions, pd1) #define BBRAM_REGION_PD2 DT_PATH(named_bbram_regions, pd2) #define BBRAM_REGION_TRY_SLOT DT_PATH(named_bbram_regions, try_slot) +LOG_MODULE_REGISTER(shim_system, LOG_LEVEL_ERR); + STATIC_IF_NOT(CONFIG_ZTEST) const struct device *bbram_dev; +STATIC_IF_NOT(CONFIG_ZTEST) const struct device *sys_dev; #if DT_NODE_EXISTS(DT_NODELABEL(bbram)) static int system_init(const struct device *unused) @@ -95,3 +101,170 @@ const char *system_get_chip_revision(void) { return ""; } + +void system_reset(int flags) +{ + int err; + uint32_t save_flags; + + if (!sys_dev) + LOG_ERR("sys_dev get binding failed"); + + /* Disable interrupts to avoid task swaps during reboot */ + interrupt_disable_all(); + + /* Get flags to be saved in BBRAM */ + system_encode_save_flags(flags, &save_flags); + + /* Store flags to battery backed RAM. */ + chip_save_reset_flags(save_flags); + + /* If WAIT_EXT is set, then allow 10 seconds for external reset */ + if (flags & SYSTEM_RESET_WAIT_EXT) { + int i; + + /* Wait 10 seconds for external reset */ + for (i = 0; i < 1000; i++) { + watchdog_reload(); + udelay(10000); + } + } + + err = cros_system_soc_reset(sys_dev); + + if (err < 0) + LOG_ERR("soc reset failed"); + + /* should never return */ + while (1) + continue; +} + +static int check_reset_cause(void) +{ + uint32_t chip_flags = 0; /* used to write back to the BBRAM */ + uint32_t system_flags = chip_read_reset_flags(); /* system reset flag */ + int chip_reset_cause = 0; /* chip-level reset cause */ + + chip_reset_cause = cros_system_get_reset_cause(sys_dev); + if (chip_reset_cause < 0) + return -1; + + /* + * TODO(b/182876692): Implement CONFIG_POWER_BUTTON_INIT_IDLE & + * CONFIG_BOARD_FORCE_RESET_PIN. Also, check the hibernate flow if PSL + * merge into tot. + */ + + switch (chip_reset_cause) { + case POWERUP: + system_flags |= EC_RESET_FLAG_POWER_ON; + if (IS_ENABLED(CONFIG_BOARD_RESET_AFTER_POWER_ON)) { + /* + * Power-on restart, so set a flag and save it for the + * next imminent reset. Later code will check for this + * flag and wait for the second reset. + */ + system_flags |= EC_RESET_FLAG_INITIAL_PWR; + chip_flags |= EC_RESET_FLAG_INITIAL_PWR; + } + break; + + case VCC1_RST_PIN: + /* + * If configured, check the saved flags to see whether the + * previous restart was a power-on, in which case treat this + * restart as a power-on as well. This is to workaround the fact + * that the H1 will reset the EC at power up. + */ + if (IS_ENABLED(CONFIG_BOARD_RESET_AFTER_POWER_ON)) { + if (system_flags & EC_RESET_FLAG_INITIAL_PWR) { + /* + * The previous restart was a power-on so treat + * this restart as that, and clear the flag so + * later code will not wait for the second + * reset. + */ + system_flags = (system_flags & + ~EC_RESET_FLAG_INITIAL_PWR) | + EC_RESET_FLAG_POWER_ON; + } else { + /* + * No previous reset flag, so this is a + * subsequent restart i.e any restarts after the + * second restart caused by the H1. + */ + system_flags |= EC_RESET_FLAG_RESET_PIN; + } + } else { + system_flags |= EC_RESET_FLAG_RESET_PIN; + } + break; + + case DEBUG_RST: + system_flags |= EC_RESET_FLAG_SOFT; + break; + + case WATCHDOG_RST: + /* + * Don't set EC_RESET_FLAG_WATCHDOG flag if watchdog is issued + * by system_reset in order to distinguish reset cause is panic + * reason or not. + */ + if (!(system_flags & (EC_RESET_FLAG_SOFT | EC_RESET_FLAG_HARD))) + system_flags |= EC_RESET_FLAG_WATCHDOG; + break; + } + + /* Clear & set the reset flags for the following reset. */ + chip_save_reset_flags(chip_flags); + + /* Set the system reset flags. */ + system_set_reset_flags(system_flags); + + return 0; +} + +static int system_preinitialize(const struct device *unused) +{ + ARG_UNUSED(unused); + + sys_dev = device_get_binding("CROS_SYSTEM"); + if (!sys_dev) { + /* + * TODO(b/183022804): This should not happen in normal + * operation. Check whether the error check can be change to + * build-time error, or at least a fatal run-time error. + */ + LOG_ERR("sys_dev gets binding failed"); + return -1; + } + + /* check the reset cause */ + if (check_reset_cause() != 0) { + LOG_ERR("check the reset cause failed"); + return -1; + } + + /* + * For some boards on power-on, the EC is reset by the H1 after + * power-on, so the EC sees 2 resets. This config enables the EC to save + * a flag on the first power-up restart, and then wait for the second + * reset before any other setup is done (such as GPIOs, timers, UART + * etc.) On the second reset, the saved flag is used to detect the + * previous power-on, and treat the second reset as a power-on instead + * of a reset. + */ + if (IS_ENABLED(CONFIG_BOARD_RESET_AFTER_POWER_ON) && + system_get_reset_flags() & EC_RESET_FLAG_INITIAL_PWR) { + /* TODO(b/182875520): Change to use 2 second delay. */ + while (1) + continue; + } + + return 0; +} +#if (!defined(CONFIG_ZTEST)) +SYS_INIT(system_preinitialize, PRE_KERNEL_1, + CONFIG_PLATFORM_EC_SYSTEM_PRE_INIT_PRIORITY); +#endif |