summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zephyr/drivers/cros_system/cros_system_npcx.c364
-rw-r--r--zephyr/dts/bindings/gpio/gpio-enum-name.yaml1
-rw-r--r--zephyr/dts/bindings/gpio/hibernate-wake-pins.yaml13
-rw-r--r--zephyr/linker/CMakeLists.txt4
-rw-r--r--zephyr/linker/npcx-lfw.ld10
-rw-r--r--zephyr/projects/trogdor/lazor/gpio.dts1
-rw-r--r--zephyr/shim/src/system.c10
7 files changed, 359 insertions, 44 deletions
diff --git a/zephyr/drivers/cros_system/cros_system_npcx.c b/zephyr/drivers/cros_system/cros_system_npcx.c
index 82d95427af..07a7cf8c80 100644
--- a/zephyr/drivers/cros_system/cros_system_npcx.c
+++ b/zephyr/drivers/cros_system/cros_system_npcx.c
@@ -3,14 +3,61 @@
* found in the LICENSE file.
*/
+#include <arch/arm/aarch32/cortex_m/cmsis.h>
#include <drivers/cros_system.h>
+#include <drivers/gpio.h>
#include <drivers/watchdog.h>
#include <logging/log.h>
#include <soc.h>
+#include <soc/nuvoton_npcx/reg_def_cros.h>
+#include <sys/util.h>
+
+#include "gpio.h"
#include "rom_chip.h"
+#include "soc_gpio.h"
+#include "soc_miwu.h"
+#include "system.h"
LOG_MODULE_REGISTER(cros_system, LOG_LEVEL_ERR);
+/**
+ * @brief Get a node from path '/hibernate_wakeup_pins' which has a property
+ * 'wakeup-pins' contains GPIO list for hibernate wake-up
+ *
+ * @return node identifier with that path.
+ */
+#define SYSTEM_DT_NODE_HIBERNATE_CONFIG DT_INST(0, cros_ec_hibernate_wake_pins)
+
+/**
+ * @brief Get the length of 'wakeup-pins' property
+ *
+ * @return length of 'wakeup-pins' prop which type is 'phandles'
+ */
+#define SYSTEM_DT_NODE_WAKEUP_PIN_LEN \
+ DT_PROP_LEN(SYSTEM_DT_NODE_HIBERNATE_CONFIG, wakeup_pins)
+
+/**
+ * @brief Get a node identifier from a phandle in property 'wakeup-pins' at
+ * index i.
+ *
+ * @param i index of 'wakeup-pins' prop which type is 'phandles'
+ * @return node identifier with that path.
+ */
+#define SYSTEM_DT_NODE_WAKEUP_PIN_BY_IDX(i) \
+ DT_PHANDLE_BY_IDX(SYSTEM_DT_NODE_HIBERNATE_CONFIG, wakeup_pins, i)
+
+/**
+ * @brief Get the enum using in chromium system by index i in 'wakeup-pins'
+ * list.
+ *
+ * @param i index of 'wakeup-pins' prop which type is 'phandles'
+ * @return GPIO enumeration
+ */
+#define SYSTEM_DT_WAKEUP_GPIO_ENUM_BY_IDX(i, _) \
+ COND_CODE_1(DT_NODE_HAS_PROP(SYSTEM_DT_NODE_WAKEUP_PIN_BY_IDX(i), \
+ enum_name), \
+ (GPIO_SIGNAL(SYSTEM_DT_NODE_WAKEUP_PIN_BY_IDX(i)), ), ())
+
/* Driver config */
struct cros_system_npcx_config {
/* hardware module base address */
@@ -45,14 +92,245 @@ enum npcx_chip_id {
DEVICE_ID_NPCX797W_C = 0x2C,
};
+/* RAM block size in npcx family (Unit: bytes) */
+#define NPCX_RAM_BLOCK_SIZE (32 * 1024)
+/* RAM block number in npcx7 series */
+#define NPCX7_RAM_BLOCK_NUM 12
+/* RAM block mask for power down in npcx7 series */
+#define NPCX7_RAM_BLOCK_PD_MASK (BIT(12) - 1)
+/* Get saved reset flag address in battery-backed ram */
+#define BBRAM_SAVED_RESET_FLAG_ADDR \
+ (DT_REG_ADDR(DT_INST(0, nuvoton_npcx_cros_bbram)) + \
+ DT_PROP(DT_PATH(named_bbram_regions, saved_reset_flags), offset))
+
+/* Soc specific system local functions */
+static int system_npcx_watchdog_stop(void)
+{
+ if (IS_ENABLED(CONFIG_WATCHDOG)) {
+ const struct device *wdt_dev = device_get_binding(
+ DT_LABEL(DT_INST(0, nuvoton_npcx_watchdog)));
+
+ if (!wdt_dev) {
+ LOG_ERR("wdt_dev get binding failed");
+ return -ENODEV;
+ }
+
+ wdt_disable(wdt_dev);
+ }
+
+ return 0;
+}
+
+static void system_npcx_set_flash_pins_tri_state(const struct device *dev)
+{
+ struct scfg_reg *const inst_scfg = HAL_SCFG_INST(dev);
+
+ inst_scfg->DEVCNT |= BIT(NPCX_DEVCNT_F_SPI_TRIS);
+}
+
+static void system_npcx_init_watchdog_reset(const struct device *dev)
+{
+ struct twd_reg *const inst_twd = HAL_TWD_INST(dev);
+
+ /* Enable early touch */
+ inst_twd->T0CSR &= ~BIT(NPCX_T0CSR_TESDIS);
+ /* watchdog touched by writing 5Ch to WDSDM */
+ inst_twd->TWCFG |= BIT(NPCX_TWCFG_WDSDME);
+}
+
+static void system_npcx_turn_off_adc(void)
+{
+ struct adc_reg *const inst_adc =
+ (struct adc_reg *)(DT_REG_ADDR(DT_INST(0, nuvoton_npcx_adc)));
+
+ inst_adc->ADCCNF = 0;
+ /* Wait for 1000 us to make sure conversion is completed. */
+ k_busy_wait(1000);
+}
+
+static void system_npcx_turn_off_kernel_timer(void)
+{
+ static struct itim32_reg *const evt_tmr =
+ (struct itim32_reg *)DT_REG_ADDR_BY_NAME(
+ DT_INST(0, nuvoton_npcx_itim_timer), evt_itim);
+
+ evt_tmr->ITCTS32 &= ~BIT(NPCX_ITCTSXX_ITEN);
+}
+
+static void system_npcx_disable_instant_wakeup(void)
+{
+ struct pmc_reg *const inst_pmc = (struct pmc_reg *)(DT_REG_ADDR_BY_NAME(
+ DT_INST(0, nuvoton_npcx_pcc), pmc));
+
+ inst_pmc->ENIDL_CTL &= ~BIT(NPCX_ENIDL_CTL_LP_WK_CTL);
+}
+
+static void system_npcx_set_wakeup_gpios_before_hibernate(void)
+{
+ const uintptr_t miwu_base[] = {
+ DT_REG_ADDR(DT_INST(0, nuvoton_npcx_miwu)),
+ DT_REG_ADDR(DT_INST(1, nuvoton_npcx_miwu)),
+ DT_REG_ADDR(DT_INST(2, nuvoton_npcx_miwu)),
+ };
+
+ /* Disable all MIWU inputs before entering hibernate */
+ for (int table = 0; table < ARRAY_SIZE(miwu_base); table++) {
+ for (int group = 0; group < NPCX_MIWU_GROUP_COUNT; group++) {
+ /* Disable all wake-ups */
+ NPCX_WKEN(miwu_base[table], group) = 0x00;
+ /* Clear all pending bits of wake-ups */
+ NPCX_WKPCL(miwu_base[table], group) = 0xFF;
+ /*
+ * Disable all inputs of wake-ups to prevent leakage
+ * caused by input floating.
+ */
+ NPCX_WKINEN(miwu_base[table], group) = 0x00;
+ }
+ }
+
+ static const int wakeup_pin_list[] = {
+#if DT_NODE_EXISTS(SYSTEM_DT_NODE_HIBERNATE_CONFIG)
+ UTIL_LISTIFY(SYSTEM_DT_NODE_WAKEUP_PIN_LEN,
+ SYSTEM_DT_WAKEUP_GPIO_ENUM_BY_IDX, _)
+#endif
+ };
+
+ /* Reconfigure wake-up GPIOs */
+ for (int i = 0; i < ARRAY_SIZE(wakeup_pin_list); i++) {
+ gpio_reset(wakeup_pin_list[i]);
+ /* Re-enable interrupt for wake-up inputs */
+ gpio_enable_interrupt(wakeup_pin_list[i]);
+ }
+}
+
/*
- * For cortex-m we cannot use irq_lock() for disabling all the interrupts
- * because it leaves some (NMI and faults) still enabled. Use "cpsid i" to
- * replace it.
+ * Hibernate function locates in the last 32K ram block in npcx7 series.
+ * Do not use global variables or call functions since we have turned off
+ * the other ram blocks.
*/
-static inline void interrupt_disable_all(void)
+noreturn void __keep __attribute__((section(".lfw.hiber")))
+system_npcx_hibernate_by_lfw_in_last_ram(const struct device *dev,
+ uint32_t pd_ram_mask)
+{
+ /* Modules used for hibernating */
+ struct twd_reg *const inst_twd = HAL_TWD_INST(dev);
+ struct mtc_reg *const inst_mtc = (struct mtc_reg *)(DT_REG_ADDR(
+ DT_INST(0, nuvoton_npcx_cros_mtc)));
+ struct pmc_reg *const inst_pmc = (struct pmc_reg *)(DT_REG_ADDR_BY_NAME(
+ DT_INST(0, nuvoton_npcx_pcc), pmc));
+ uint32_t reset_flags;
+ volatile uint8_t *saved_reset_flags =
+ (volatile uint8_t *)BBRAM_SAVED_RESET_FLAG_ADDR;
+
+ /* Turn off all blocks except last one for better power consumption */
+ inst_pmc->RAM_PD[0] = (uint8_t)pd_ram_mask;
+ inst_pmc->RAM_PD[1] = (uint8_t)(pd_ram_mask >> 8);
+
+ /* Set deep idle mode */
+ inst_pmc->PMCSR = BIT(NPCX_PMCSR_IDLE) | BIT(NPCX_PMCSR_DHF);
+
+ /* Enter system sleep mode */
+ __asm__ volatile("wfi");
+
+ /*
+ * Mark wake-up reason for hibernate. Do not call bbram utilities
+ * directly since the other ram blocks are power down.
+ */
+ if (IS_BIT_SET(inst_mtc->WTC, NPCX_WTC_PTO)) {
+ /* Save wake-up reason as RTC alarm. */
+ reset_flags = EC_RESET_FLAG_RTC_ALARM;
+ } else {
+ /* Otherwise, we treat it as GPIOs wake-up */
+ reset_flags = EC_RESET_FLAG_WAKE_PIN;
+ }
+
+ saved_reset_flags[0] |= reset_flags;
+ saved_reset_flags[1] |= reset_flags >> 8;
+ saved_reset_flags[2] |= reset_flags >> 16;
+ saved_reset_flags[3] |= reset_flags >> 24;
+
+ /*
+ * The trigger of a watchdog event by a "too early service" condition.
+ * When the watchdog is written more than once during three watchdog
+ * clock cycle.
+ */
+ inst_twd->WDSDM = 0x5C;
+ inst_twd->WDSDM = 0x5C;
+
+ /* Spin and wait for reboot; should never return */
+ while (1)
+ continue;
+}
+
+static inline int system_npcx_get_ram_blk_by_lfw_addr(char *address)
{
- __asm__("cpsid i");
+ return NPCX7_RAM_BLOCK_NUM -
+ ceiling_fraction((uint32_t)address -
+ CONFIG_CROS_EC_PROGRAM_MEMORY_BASE,
+ NPCX_RAM_BLOCK_SIZE);
+}
+
+static void system_npcx_hibernate_by_disable_ram(const struct device *dev,
+ uint32_t seconds,
+ uint32_t microseconds)
+{
+ /* Get 32kb ram block order of lfw function */
+ extern char __lfw_text_start[], __lfw_text_end[];
+ int lfw_block = system_npcx_get_ram_blk_by_lfw_addr(__lfw_text_start);
+ uint32_t pd_ram_mask = ~BIT(lfw_block) & NPCX7_RAM_BLOCK_PD_MASK;
+
+ if (lfw_block != system_npcx_get_ram_blk_by_lfw_addr(__lfw_text_end)) {
+ LOG_ERR("LFW cannot cross ram blocks!");
+ return;
+ }
+
+ /*
+ * Set status of pins which connect to flash to tri-state in case
+ * the leakage current.
+ */
+ system_npcx_set_flash_pins_tri_state(dev);
+
+ /* Initialize watchdog for reset after wake-up from hibernating */
+ system_npcx_init_watchdog_reset(dev);
+
+ /* Disable ADC and wait for 1000 us to make sure conversion is done */
+ if (IS_ENABLED(CONFIG_ADC))
+ system_npcx_turn_off_adc();
+
+ /* Disable kernel timer */
+ system_npcx_turn_off_kernel_timer();
+
+ /* Disable instant wake up mode for better power consumption */
+ system_npcx_disable_instant_wakeup();
+
+ /*
+ * Set wake-up input GPIOs and turn off the other sources for better
+ * power consumption before entering hibernate mode.
+ */
+ system_npcx_set_wakeup_gpios_before_hibernate();
+
+ /*
+ * Give the board a chance to do any late stage hibernation work. This
+ * is likely going to configure GPIOs for hibernation. On some boards,
+ * it's possible that this may not return at all. On those boards,
+ * power to the EC is likely being turn off entirely.
+ */
+ if (board_hibernate_late) {
+ board_hibernate_late();
+ }
+
+ /* Setup a RTC alarm if needed */
+ if (IS_ENABLED(CONFIG_RTC) && (seconds || microseconds)) {
+ system_set_rtc_alarm(seconds, microseconds);
+ }
+
+ /* Clear all pending IRQs in case wake-up immediately after sleeping */
+ for (int i = 0; i < CONFIG_NUM_IRQS; i++) {
+ NVIC_ClearPendingIRQ(i);
+ }
+
+ /* Execute hibernate by lfw which locates in last 32K block ram */
+ system_npcx_hibernate_by_lfw_in_last_ram(dev, pd_ram_mask);
}
static const char *cros_system_npcx_get_chip_vendor(const struct device *dev)
@@ -122,6 +400,37 @@ static const char *cros_system_npcx_get_chip_revision(const struct device *dev)
return rev;
}
+static void system_npcx_hibernate_by_psl(const struct device *dev,
+ uint32_t seconds,
+ uint32_t microseconds)
+{
+ ARG_UNUSED(dev);
+ /*
+ * TODO(b/178230662): RTC wake-up in PSL mode only support in npcx9
+ * series. Nuvoton will introduce CLs for it later.
+ */
+ ARG_UNUSED(seconds);
+ ARG_UNUSED(microseconds);
+
+ /*
+ * Configure PSL input pads from "psl-in-pads" property in device tree
+ * file.
+ */
+ npcx_pinctrl_psl_input_configure();
+
+ /*
+ * Give the board a chance to do any late stage hibernation work. This
+ * is likely going to configure GPIOs for hibernation. On some boards,
+ * it's possible that this may not return at all. On those boards,
+ * power to the EC is likely being turn off entirely.
+ */
+ if (board_hibernate_late)
+ board_hibernate_late();
+
+ /* Turn off VCC1 to enter ultra-low-power mode for hibernating */
+ npcx_pinctrl_psl_output_set_inactive();
+}
+
static int cros_system_npcx_get_reset_cause(const struct device *dev)
{
struct cros_system_npcx_data *data = DRV_DATA(dev);
@@ -164,21 +473,10 @@ static int cros_system_npcx_soc_reset(const struct device *dev)
*/
/* Stop the watchdog */
- if (IS_ENABLED(CONFIG_WATCHDOG)) {
- const struct device *wdt_dev = device_get_binding(
- DT_LABEL(DT_INST(0, nuvoton_npcx_watchdog)));
-
- if (!wdt_dev) {
- LOG_ERR("wdt_dev get binding failed");
- return -ENODEV;
- }
+ system_npcx_watchdog_stop();
- wdt_disable(wdt_dev);
- }
-
- /* Enable early touch */
- inst_twd->T0CSR &= ~BIT(NPCX_T0CSR_TESDIS);
- inst_twd->TWCFG |= BIT(NPCX_TWCFG_WDSDME);
+ /* Initialize watchdog for reset */
+ system_npcx_init_watchdog_reset(dev);
/*
* The trigger of a watchdog event by a "too early service" condition.
@@ -200,32 +498,20 @@ static int cros_system_npcx_soc_reset(const struct device *dev)
static int cros_system_npcx_hibernate(const struct device *dev,
uint32_t seconds, uint32_t microseconds)
{
- ARG_UNUSED(seconds);
- ARG_UNUSED(microseconds);
-
/* Disable interrupt first */
interrupt_disable_all();
- /*
- * TODO(b:178230662): RTC wake-up in PSL mode only support in npcx9
- * series. Nuvoton will introduce CLs for it later.
- */
-
- if (IS_ENABLED(CONFIG_PLATFORM_EC_HIBERNATE_PSL)) {
- /*
- * Configure PSL input pads from "psl-in-pads" property in
- * device tree file.
- */
- npcx_pinctrl_psl_input_configure();
+ /* Stop the watchdog */
+ system_npcx_watchdog_stop();
- /* Turn off VCC1 and enter ultra-low-power mode */
- npcx_pinctrl_psl_output_set_inactive();
+ /* Enter hibernate mode */
+ if (IS_ENABLED(CONFIG_PLATFORM_EC_SYSTEM_HIBERNATE_PSL)) {
+ system_npcx_hibernate_by_psl(dev, seconds, microseconds);
+ } else {
+ system_npcx_hibernate_by_disable_ram(dev, seconds,
+ microseconds);
}
- /*
- * TODO(b:183745774): implement Non-PSL hibernate mechanism if
- * CONFIG_PLATFORM_EC_HIBERNATE_PSL is not enabled.
- */
return 0;
}
diff --git a/zephyr/dts/bindings/gpio/gpio-enum-name.yaml b/zephyr/dts/bindings/gpio/gpio-enum-name.yaml
index d30984184d..b63020d4bc 100644
--- a/zephyr/dts/bindings/gpio/gpio-enum-name.yaml
+++ b/zephyr/dts/bindings/gpio/gpio-enum-name.yaml
@@ -40,6 +40,7 @@ properties:
- GPIO_EC_PCH_SYS_PWROK
- GPIO_EC_PCH_WAKE_ODL
- GPIO_EC_PROCHOT_IN_L
+ - GPIO_EC_RST_ODL
- GPIO_EC_WP_L
- GPIO_EN_A_RAILS
- GPIO_EN_PP3300_A
diff --git a/zephyr/dts/bindings/gpio/hibernate-wake-pins.yaml b/zephyr/dts/bindings/gpio/hibernate-wake-pins.yaml
new file mode 100644
index 0000000000..64435f7b3b
--- /dev/null
+++ b/zephyr/dts/bindings/gpio/hibernate-wake-pins.yaml
@@ -0,0 +1,13 @@
+# 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.
+
+description: Hibernate Wake-up Pins Configurations
+
+compatible: "cros-ec,hibernate-wake-pins"
+
+properties:
+ wakeup-pins:
+ type: phandles
+ required: true
+ description: GPIO list for hibernate wake-up
diff --git a/zephyr/linker/CMakeLists.txt b/zephyr/linker/CMakeLists.txt
index 170055dba7..27b028d22c 100644
--- a/zephyr/linker/CMakeLists.txt
+++ b/zephyr/linker/CMakeLists.txt
@@ -10,3 +10,7 @@ zephyr_linker_sources(RWDATA SORT_KEY 1 iram_text.ld)
# Compute the image size
zephyr_linker_sources(RAM_SECTIONS image_size.ld)
+
+# Little FW with specific purposes used by NPCX EC
+zephyr_linker_sources_ifdef(CONFIG_SOC_FAMILY_NPCX ROM_START SORT_KEY 1
+ npcx-lfw.ld)
diff --git a/zephyr/linker/npcx-lfw.ld b/zephyr/linker/npcx-lfw.ld
new file mode 100644
index 0000000000..bdab9c3546
--- /dev/null
+++ b/zephyr/linker/npcx-lfw.ld
@@ -0,0 +1,10 @@
+/* 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.
+ */
+
+. = ALIGN(4);
+__lfw_text_start = .;
+*(.lfw.*)
+. = ALIGN(4);
+__lfw_text_end = .;
diff --git a/zephyr/projects/trogdor/lazor/gpio.dts b/zephyr/projects/trogdor/lazor/gpio.dts
index c5f95a1fd1..5e91c23d57 100644
--- a/zephyr/projects/trogdor/lazor/gpio.dts
+++ b/zephyr/projects/trogdor/lazor/gpio.dts
@@ -131,6 +131,7 @@
};
ec_rst_odl {
gpios = <&gpio0 2 GPIO_INPUT>;
+ enum-name = "GPIO_EC_RST_ODL";
label = "EC_RST_ODL";
};
ec_entering_rw {
diff --git a/zephyr/shim/src/system.c b/zephyr/shim/src/system.c
index b1a443fbf4..c8b5c81341 100644
--- a/zephyr/shim/src/system.c
+++ b/zephyr/shim/src/system.c
@@ -176,8 +176,7 @@ static int check_reset_cause(void)
/*
* TODO(b/182876692): Implement CONFIG_POWER_BUTTON_INIT_IDLE &
- * CONFIG_BOARD_FORCE_RESET_PIN. Also, check the hibernate flow if PSL
- * merge into tot.
+ * CONFIG_BOARD_FORCE_RESET_PIN.
*/
switch (chip_reset_cause) {
@@ -234,10 +233,11 @@ static int check_reset_cause(void)
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.
+ * by system_reset or hibernate in order to distinguish reset
+ * cause is panic reason or not.
*/
- if (!(system_flags & (EC_RESET_FLAG_SOFT | EC_RESET_FLAG_HARD)))
+ if (!(system_flags & (EC_RESET_FLAG_SOFT | EC_RESET_FLAG_HARD |
+ EC_RESET_FLAG_HIBERNATE)))
system_flags |= EC_RESET_FLAG_WATCHDOG;
break;
}