summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMulin Chao <mlchao@nuvoton.com>2017-04-26 14:49:26 +0800
committerchrome-bot <chrome-bot@chromium.org>2017-05-09 23:20:11 -0700
commit375b60776156036ef458069c408e5ed4b24692f0 (patch)
tree63b2b33aae9e74061f4a4db43adcc6426c8879f0
parent53b72194cdfdcdce50b5484ba0b06c2357fcc0b7 (diff)
downloadchrome-ec-375b60776156036ef458069c408e5ed4b24692f0.tar.gz
npcx: system: Add support for npcx7 series ec
This CL implements two methods for hibernating on npcx7 ec. One is using PSL (Power Switch Logic) circuit to cut off ec's VCC power rail. The other is turning off the power of all ram blocks except the last code ram block. In order to make sure hibernate utilities are located in the last code ram block and work properly, we introduce a new section called 'after_init' in ec.lds.S. We also moved the hibernate utilities, workarounds for sysjump and so on which are related to chip family into system-npcx5/7.c. It should be easier to maintain. It also includes: 1. Add CONFIG_HIBERNATE_PSL to select which method is used on npcx7 for hibernating. 2. Add new flag GPIO_HIB_WAKE_HIGH to configure the active priority of wake-up inputs during hibernating. 3. Add DEVICE_ID for npcx796f. BRANCH=none BUG=none TEST=No build errors for all boards using npcx5 series. Build poppy board and upload FW to platform. No issues found. Make sure AC_PRESENT and POWER_BUTTON_L can wake up system from hibernate. Passed hibernate tests no matter CONFIG_HIBERNATE_PSL is enabled or not on npcx796f evb. Change-Id: I4e045ebce4120b6fabaa582ed2ec31b5335dfdc3 Signed-off-by: Mulin Chao <mlchao@nuvoton.com> Reviewed-on: https://chromium-review.googlesource.com/493006 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--chip/npcx/build.mk1
-rw-r--r--chip/npcx/registers.h3
-rw-r--r--chip/npcx/system-npcx5.c277
-rw-r--r--chip/npcx/system-npcx7.c179
-rw-r--r--chip/npcx/system.c283
-rw-r--r--chip/npcx/system_chip.h40
-rw-r--r--core/cortex-m/ec.lds.S6
-rw-r--r--include/config.h7
-rw-r--r--include/gpio.h1
9 files changed, 542 insertions, 255 deletions
diff --git a/chip/npcx/build.mk b/chip/npcx/build.mk
index f733ab168c..fc6ea0e765 100644
--- a/chip/npcx/build.mk
+++ b/chip/npcx/build.mk
@@ -18,6 +18,7 @@ endif
# Required chip modules
chip-y=header.o clock.o gpio.o hwtimer.o jtag.o system.o uart.o
+chip-y+=system-$(CHIP_FAMILY).o
# Optional chip modules
chip-$(CONFIG_ADC)+=adc.o
diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h
index 15a630d7b2..7914caa5b7 100644
--- a/chip/npcx/registers.h
+++ b/chip/npcx/registers.h
@@ -803,6 +803,9 @@ enum {
#define NPCX_DISIDL_CTL REG8(NPCX_PMC_BASE_ADDR + 0x004)
#define NPCX_DISIDL_CTL1 REG8(NPCX_PMC_BASE_ADDR + 0x005)
#define NPCX_PWDWN_CTL(offset) REG8(NPCX_PMC_BASE_ADDR + 0x008 + offset)
+#if defined(CHIP_FAMILY_NPCX7)
+#define NPCX_RAM_PD(offset) REG8(NPCX_PMC_BASE_ADDR + 0x020 + offset)
+#endif
/* PMC register fields */
#define NPCX_PMCSR_DI_INSTW 0
diff --git a/chip/npcx/system-npcx5.c b/chip/npcx/system-npcx5.c
new file mode 100644
index 0000000000..abbc8b1e45
--- /dev/null
+++ b/chip/npcx/system-npcx5.c
@@ -0,0 +1,277 @@
+/* Copyright 2017 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.
+ */
+
+/* System module driver depends on chip series for Chrome EC */
+#include "common.h"
+#include "console.h"
+#include "cpu.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
+#include "gpio.h"
+#include "hwtimer_chip.h"
+#include "system_chip.h"
+#include "rom_chip.h"
+
+/* Begin address of Suspend RAM for hibernate utility */
+uintptr_t __lpram_fw_start = CONFIG_LPRAM_BASE;
+/* Offset of little FW in Suspend Ram for GDMA bypass */
+#define LFW_OFFSET 0x160
+/* Begin address of Suspend RAM for little FW (GDMA utilities). */
+uintptr_t __lpram_lfw_start = CONFIG_LPRAM_BASE + LFW_OFFSET;
+
+/*****************************************************************************/
+/* IC specific low-level driver depends on chip series */
+
+/**
+ * Configure address 0x40001600 (Low Power RAM) in the the MPU
+ * (Memory Protection Unit) as a "regular" memory
+ */
+void system_mpu_config(void)
+{
+ /* Enable MPU */
+ CPU_MPU_CTRL = 0x7;
+
+ /* Create a new MPU Region for low-power ram */
+ CPU_MPU_RNR = 0; /* Select region number 0 */
+ CPU_MPU_RASR = CPU_MPU_RASR & 0xFFFFFFFE; /* Disable region */
+ CPU_MPU_RBAR = CONFIG_LPRAM_BASE; /* Set region base address */
+ /*
+ * Set region size & attribute and enable region
+ * [31:29] - Reserved.
+ * [28] - XN (Execute Never) = 0
+ * [27] - Reserved.
+ * [26:24] - AP = 011 (Full access)
+ * [23:22] - Reserved.
+ * [21:19,18,17,16] - TEX,S,C,B = 001000 (Normal memory)
+ * [15:8] - SRD = 0 (Subregions enabled)
+ * [7:6] - Reserved.
+ * [5:1] - SIZE = 01001 (1K)
+ * [0] - ENABLE = 1 (enabled)
+ */
+ CPU_MPU_RASR = 0x03080013;
+
+ /* Create a new MPU Region for data ram */
+ CPU_MPU_RNR = 1; /* Select region number 1 */
+ CPU_MPU_RASR = CPU_MPU_RASR & 0xFFFFFFFE; /* Disable region */
+ CPU_MPU_RBAR = CONFIG_RAM_BASE; /* Set region base address */
+ /*
+ * Set region size & attribute and enable region
+ * [31:29] - Reserved.
+ * [28] - XN (Execute Never) = 1
+ * [27] - Reserved.
+ * [26:24] - AP = 011 (Full access)
+ * [23:22] - Reserved.
+ * [21:19,18,17,16] - TEX,S,C,B = 001000 (Normal memory)
+ * [15:8] - SRD = 0 (Subregions enabled)
+ * [7:6] - Reserved.
+ * [5:1] - SIZE = 01110 (32K)
+ * [0] - ENABLE = 1 (enabled)
+ */
+ CPU_MPU_RASR = 0x1308001D;
+}
+
+/**
+ * hibernate function in low power ram for npcx5 series.
+ */
+void __keep __attribute__ ((noreturn, section(".lowpower_ram")))
+__enter_hibernate_in_lpram(void)
+{
+ /*
+ * TODO (ML): Set stack pointer to upper 512B of Suspend RAM.
+ * Our bypass needs stack instructions but FW will turn off main ram
+ * later for better power consumption.
+ */
+ asm (
+ "ldr r0, =0x40001800\n"
+ "mov sp, r0\n"
+ );
+
+ /* Disable Code RAM first */
+ SET_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_5), NPCX_PWDWN_CTL5_MRFSH_DIS);
+ SET_BIT(NPCX_DISIDL_CTL, NPCX_DISIDL_CTL_RAM_DID);
+
+ /* Set deep idle mode*/
+ NPCX_PMCSR = 0x6;
+
+ /* Enter deep idle, wake-up by GPIOs or RTC */
+ /*
+ * TODO (ML): Although the probability is small, it still has chance
+ * to meet the same symptom that CPU's behavior is abnormal after
+ * wake-up from deep idle.
+ * Workaround: Apply the same bypass of idle but don't enable interrupt.
+ */
+ asm (
+ "push {r0-r5}\n" /* Save needed registers */
+ "ldr r0, =0x40001600\n" /* Set r0 to Suspend RAM addr */
+ "wfi\n" /* Wait for int to enter idle */
+ "ldm r0, {r0-r5}\n" /* Add a delay after WFI */
+ "pop {r0-r5}\n" /* Restore regs before enabling ints */
+ "isb\n" /* Flush the cpu pipeline */
+ );
+
+ /* RTC wake-up */
+ if (IS_BIT_SET(NPCX_WTC, NPCX_WTC_PTO))
+ /*
+ * Mark wake-up reason for hibernate
+ * Do not call bbram_data_write directly cause of
+ * executing in low-power ram
+ */
+ NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_MTC;
+ else
+ /* Otherwise, we treat it as GPIOs wake-up */
+ NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_PIN;
+
+ /* Start a watchdog reset */
+ NPCX_WDCNT = 0x01;
+ /* Reload and restart Timer 0 */
+ SET_BIT(NPCX_T0CSR, NPCX_T0CSR_RST);
+ /* Wait for timer is loaded and restart */
+ while (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_RST))
+ ;
+
+ /* Spin and wait for reboot; should never return */
+ while (1)
+ ;
+}
+
+/**
+ * Hibernate function for different Nuvoton chip series.
+ */
+void __hibernate_npcx_series(void)
+{
+ int i;
+ void (*__hibernate_in_lpram)(void) =
+ (void(*)(void))(__lpram_fw_start | 0x01);
+
+ /* Enable power for the Low Power RAM */
+ CLEAR_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6), 6);
+
+ /* Enable Low Power RAM */
+ NPCX_LPRAM_CTRL = 1;
+
+ /* Copy the __enter_hibernate_in_lpram instructions to LPRAM */
+ for (i = 0; i < &__flash_lpfw_end - &__flash_lpfw_start; i++)
+ *((uint32_t *)__lpram_fw_start + i) =
+ *(&__flash_lpfw_start + i);
+
+ /* execute hibernate func in LPRAM */
+ __hibernate_in_lpram();
+}
+
+#ifdef CONFIG_EXTERNAL_STORAGE
+/* Sysjump utilities in low power ram for npcx5 series. */
+void __keep __attribute__ ((noreturn, section(".lowpower_ram2")))
+__start_gdma(uint32_t exeAddr)
+{
+ /* Enable GDMA now */
+ SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAEN);
+
+ /* Start GDMA */
+ SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_SOFTREQ);
+
+ /* Wait for transfer to complete/fail */
+ while (!IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_TC) &&
+ !IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAERR))
+ ;
+
+ /* Disable GDMA now */
+ CLEAR_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAEN);
+
+ /*
+ * Failure occurs during GMDA transaction. Let watchdog issue and
+ * boot from RO region again.
+ */
+ if (IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAERR))
+ while (1)
+ ;
+
+ /*
+ * Jump to the exeAddr address if needed. Setting bit 0 of address to
+ * indicate it's a thumb branch for cortex-m series CPU.
+ */
+ ((void (*)(void))(exeAddr | 0x01))();
+
+ /* Should never get here */
+ while (1)
+ ;
+}
+
+/* Bypass for GMDA issue of ROM api utilities only on npcx5 series. */
+void system_download_from_flash(uint32_t srcAddr, uint32_t dstAddr,
+ uint32_t size, uint32_t exeAddr)
+{
+ int i;
+ uint8_t chunkSize = 16; /* 4 data burst mode. ie.16 bytes */
+ /*
+ * GDMA utility in Suspend RAM. Setting bit 0 of address to indicate
+ * it's a thumb branch for cortex-m series CPU.
+ */
+ void (*__start_gdma_in_lpram)(uint32_t) =
+ (void(*)(uint32_t))(__lpram_lfw_start | 0x01);
+
+ /*
+ * Before enabling burst mode for better performance of GDMA, it's
+ * important to make sure srcAddr, dstAddr and size of transactions
+ * are 16 bytes aligned in case failure occurs.
+ */
+ ASSERT((size % chunkSize) == 0 && (srcAddr % chunkSize) == 0 &&
+ (dstAddr % chunkSize) == 0);
+
+ /* Check valid address for jumpiing */
+ ASSERT(exeAddr != 0x0);
+
+ /* Enable power for the Low Power RAM */
+ CLEAR_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6), 6);
+
+ /* Enable Low Power RAM */
+ NPCX_LPRAM_CTRL = 1;
+
+ /*
+ * Initialize GDMA for flash reading.
+ * [31:21] - Reserved.
+ * [20] - GDMAERR = 0 (Indicate GMDA transfer error)
+ * [19] - Reserved.
+ * [18] - TC = 0 (Terminal Count. Indicate operation is end.)
+ * [17] - Reserved.
+ * [16] - SOFTREQ = 0 (Don't trigger here)
+ * [15] - DM = 0 (Set normal demand mode)
+ * [14] - Reserved.
+ * [13:12] - TWS. = 10 (One double-word for every GDMA transaction)
+ * [11:10] - Reserved.
+ * [9] - BME = 1 (4-data ie.16 bytes - Burst mode enable)
+ * [8] - SIEN = 0 (Stop interrupt disable)
+ * [7] - SAFIX = 0 (Fixed source address)
+ * [6] - Reserved.
+ * [5] - SADIR = 0 (Source address incremented)
+ * [4] - DADIR = 0 (Destination address incremented)
+ * [3:2] - GDMAMS = 00 (Software mode)
+ * [1] - Reserved.
+ * [0] - ENABLE = 0 (Don't enable yet)
+ */
+ NPCX_GDMA_CTL = 0x00002200;
+
+ /* Set source base address */
+ NPCX_GDMA_SRCB = CONFIG_MAPPED_STORAGE_BASE + srcAddr;
+
+ /* Set destination base address */
+ NPCX_GDMA_DSTB = dstAddr;
+
+ /* Set number of transfers */
+ NPCX_GDMA_TCNT = (size / chunkSize);
+
+ /* Clear Transfer Complete event */
+ SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_TC);
+
+ /* Copy the __start_gdma_in_lpram instructions to LPRAM */
+ for (i = 0; i < &__flash_lplfw_end - &__flash_lplfw_start; i++)
+ *((uint32_t *)__lpram_lfw_start + i) =
+ *(&__flash_lplfw_start + i);
+
+ /* Start GDMA in Suspend RAM */
+ __start_gdma_in_lpram(exeAddr);
+}
+#endif /* CONFIG_EXTERNAL_STORAGE */
diff --git a/chip/npcx/system-npcx7.c b/chip/npcx/system-npcx7.c
new file mode 100644
index 0000000000..e22a5cece0
--- /dev/null
+++ b/chip/npcx/system-npcx7.c
@@ -0,0 +1,179 @@
+/* Copyright 2017 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.
+ */
+
+/* System module driver depends on chip series for Chrome EC */
+#include "common.h"
+#include "console.h"
+#include "cpu.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
+#include "gpio.h"
+#include "hwtimer_chip.h"
+#include "system_chip.h"
+#include "rom_chip.h"
+
+/* Macros for last 32K ram block */
+#define LAST_RAM_BLK ((NPCX_RAM_SIZE / (32 * 1024)) - 1)
+#define RAM_PD_MASK (~(1 << LAST_RAM_BLK))
+
+/*****************************************************************************/
+/* IC specific low-level driver depends on chip series */
+
+void system_mpu_config(void)
+{
+ /* Enable MPU */
+ CPU_MPU_CTRL = 0x7;
+
+ /* Create a new MPU Region for data ram */
+ CPU_MPU_RNR = 0; /* Select region number 0 */
+ CPU_MPU_RASR = CPU_MPU_RASR & 0xFFFFFFFE; /* Disable region */
+ CPU_MPU_RBAR = CONFIG_RAM_BASE; /* Set region base address */
+ /*
+ * Set region size & attribute and enable region
+ * [31:29] - Reserved.
+ * [28] - XN (Execute Never) = 1
+ * [27] - Reserved.
+ * [26:24] - AP = 011 (Full access)
+ * [23:22] - Reserved.
+ * [21:19,18,17,16] - TEX,S,C,B = 001000 (Normal memory)
+ * [15:8] - SRD = 0 (Subregions enabled)
+ * [7:6] - Reserved.
+ * [5:1] - SIZE = 01111 (64KB in NPCX796F)
+ * [0] - ENABLE = 1 (enabled)
+ */
+ /* TODO: Add the configurations for the other npcx7 series. */
+ CPU_MPU_RASR = 0x1308001F;
+
+}
+
+#ifdef CONFIG_HIBERNATE_PSL
+#ifndef NPCX_PSL_MODE_SUPPORT
+#error "Do not enable CONFIG_HIBERNATE_PSL if npcx ec doesn't support PSL mode!"
+#endif
+
+/* Hibernate function implemented by PSL (Power Switch Logic) mode. */
+void __keep __attribute__ ((noreturn)) __enter_hibernate_in_psl(void)
+{
+ /* Configure pins from GPIOs to PSL which rely on VSBY power rail. */
+ gpio_config_module(MODULE_PMU, 1);
+
+ /*
+ * Only PSL_IN events can pull PSL_OUT to high and reboot ec.
+ * We should treat it as wake-up pin reset.
+ */
+ NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_PIN;
+
+ /*
+ * Pull PSL_OUT (GPIO85) to low to cut off ec's VCC power rail by
+ * setting bit 5 of PDOUT(8).
+ */
+ SET_BIT(NPCX_PDOUT(GPIO_PORT_8), 5);
+
+ /* Spin and wait for PSL cuts power; should never return */
+ while (1)
+ ;
+}
+
+static void system_psl_type_sel(int psl_no, uint32_t flags)
+{
+ /* Set PSL input events' type as level or edge trigger */
+ if ((flags & GPIO_INT_F_HIGH) || (flags & GPIO_INT_F_LOW))
+ CLEAR_BIT(NPCX_GLUE_PSL_CTS, psl_no + 4);
+ else if ((flags & GPIO_INT_F_RISING) || (flags & GPIO_INT_F_FALLING))
+ SET_BIT(NPCX_GLUE_PSL_CTS, psl_no + 4);
+
+ /*
+ * Set PSL input events' polarity is low (high-to-low) active or
+ * high (low-to-high) active
+ */
+ if (flags & GPIO_HIB_WAKE_HIGH)
+ SET_BIT(NPCX_DEVALT(ALT_GROUP_D), 2 * psl_no);
+ else
+ CLEAR_BIT(NPCX_DEVALT(ALT_GROUP_D), 2 * psl_no);
+}
+
+int system_config_psl_mode(enum gpio_signal signal)
+{
+ int psl_no;
+ const struct gpio_info *g = gpio_list + signal;
+
+ if (g->port == GPIO_PORT_D && g->mask == MASK_PIN2) /* GPIOD2 */
+ psl_no = 0;
+ else if (g->port == GPIO_PORT_0 && (g->mask & 0x07)) /* GPIO00/01/02 */
+ psl_no = GPIO_MASK_TO_NUM(g->mask) + 1;
+ else
+ return 0;
+
+ system_psl_type_sel(psl_no, g->flags);
+ return 1;
+}
+
+#else
+/**
+ * Hibernate function in last 32K ram block for npcx7 series.
+ * Do not use global variable since we also turn off data ram.
+ */
+void __keep __attribute__ ((noreturn, section(".after_init")))
+__enter_hibernate_in_last_block(void)
+{
+ /*
+ * The hibernate utility is located in the last block of RAM. The size
+ * of each RAM block is 32KB. We turn off all blocks except last one
+ * for better power consumption.
+ */
+ NPCX_RAM_PD(0) = RAM_PD_MASK & 0xFF;
+ NPCX_RAM_PD(1) = RAM_PD_MASK >> 8;
+
+ /* Set deep idle mode */
+ NPCX_PMCSR = 0x6;
+
+ /* Enter deep idle, wake-up by GPIOs or RTC */
+ asm volatile ("wfi");
+
+ /* RTC wake-up */
+ if (IS_BIT_SET(NPCX_WTC, NPCX_WTC_PTO))
+ /*
+ * Mark wake-up reason for hibernate
+ * Do not call bbram_data_write directly cause of
+ * no stack.
+ */
+ NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_MTC;
+ else
+ /* Otherwise, we treat it as GPIOs wake-up */
+ NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_PIN;
+
+ /* Start a watchdog reset */
+ NPCX_WDCNT = 0x01;
+ /* Reload and restart Timer 0 */
+ SET_BIT(NPCX_T0CSR, NPCX_T0CSR_RST);
+ /* Wait for timer is loaded and restart */
+ while (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_RST))
+ ;
+
+ /* Spin and wait for reboot; should never return */
+ while (1)
+ ;
+}
+#endif
+
+/**
+ * Hibernate function for different Nuvoton chip series.
+ */
+void __hibernate_npcx_series(void)
+{
+#ifdef CONFIG_HIBERNATE_PSL
+ __enter_hibernate_in_psl();
+#else
+ /* Make sure this is located in the last 32K code RAM block */
+ ASSERT((uint32_t)(&__after_init_end) - CONFIG_PROGRAM_MEMORY_BASE
+ < (32*1024));
+
+ /* Execute hibernate func in last 32K block */
+ __enter_hibernate_in_last_block();
+#endif
+}
+
diff --git a/chip/npcx/system.c b/chip/npcx/system.c
index be7f47135c..eda899d833 100644
--- a/chip/npcx/system.c
+++ b/chip/npcx/system.c
@@ -22,10 +22,6 @@
#include "timer.h"
#include "util.h"
-/* Flags for BBRM_DATA_INDEX_WAKE */
-#define HIBERNATE_WAKE_MTC (1 << 0) /* MTC alarm */
-#define HIBERNATE_WAKE_PIN (1 << 1) /* Wake pin */
-
/* Delay after writing TTC for value to latch */
#define MTC_TTC_LOAD_DELAY_US 250
#define MTC_ALARM_MASK ((1 << 25) - 1)
@@ -39,14 +35,6 @@
#define CPUTS(outstr) cputs(CC_SYSTEM, outstr)
#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
-/* Begin address of Suspend RAM for hibernate utility */
-uintptr_t __lpram_fw_start = CONFIG_LPRAM_BASE;
-
-/* Offset of little FW in Suspend Ram for GDMA bypass */
-#define LFW_OFFSET 0x160
-/* Begin address of Suspend RAM for little FW (GDMA utilities). */
-uintptr_t __lpram_lfw_start = CONFIG_LPRAM_BASE + LFW_OFFSET;
-
/*****************************************************************************/
/* Internal functions */
@@ -282,115 +270,6 @@ void system_check_reset_cause(void)
}
/**
- * Configure address 0x40001600 in the the MPU
- * (Memory Protection Unit) as a "regular" memory
- */
-void system_mpu_config(void)
-{
- /* Enable MPU */
- CPU_MPU_CTRL = 0x7;
-
- /* Create a new MPU Region for low-power ram */
- CPU_MPU_RNR = 0; /* Select region number 0 */
- CPU_MPU_RASR = CPU_MPU_RASR & 0xFFFFFFFE; /* Disable region */
- CPU_MPU_RBAR = CONFIG_LPRAM_BASE; /* Set region base address */
- /*
- * Set region size & attribute and enable region
- * [31:29] - Reserved.
- * [28] - XN (Execute Never) = 0
- * [27] - Reserved.
- * [26:24] - AP = 011 (Full access)
- * [23:22] - Reserved.
- * [21:19,18,17,16] - TEX,S,C,B = 001000 (Normal memory)
- * [15:8] - SRD = 0 (Subregions enabled)
- * [7:6] - Reserved.
- * [5:1] - SIZE = 01001 (1K)
- * [0] - ENABLE = 1 (enabled)
- */
- CPU_MPU_RASR = 0x03080013;
-
- /* Create a new MPU Region for data ram */
- CPU_MPU_RNR = 1; /* Select region number 1 */
- CPU_MPU_RASR = CPU_MPU_RASR & 0xFFFFFFFE; /* Disable region */
- CPU_MPU_RBAR = CONFIG_RAM_BASE; /* Set region base address */
- /*
- * Set region size & attribute and enable region
- * [31:29] - Reserved.
- * [28] - XN (Execute Never) = 1
- * [27] - Reserved.
- * [26:24] - AP = 011 (Full access)
- * [23:22] - Reserved.
- * [21:19,18,17,16] - TEX,S,C,B = 001000 (Normal memory)
- * [15:8] - SRD = 0 (Subregions enabled)
- * [7:6] - Reserved.
- * [5:1] - SIZE = 01110 (32K)
- * [0] - ENABLE = 1 (enabled)
- */
- CPU_MPU_RASR = 0x1308001D;
-}
-
-void __keep __attribute__ ((noreturn, section(".lowpower_ram")))
-__enter_hibernate_in_lpram(void)
-{
- /*
- * TODO (ML): Set stack pointer to upper 512B of Suspend RAM.
- * Our bypass needs stack instructions but FW will turn off main ram
- * later for better power consumption.
- */
- asm (
- "ldr r0, =0x40001800\n"
- "mov sp, r0\n"
- );
-
- /* Disable Code RAM first */
- SET_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_5), NPCX_PWDWN_CTL5_MRFSH_DIS);
- SET_BIT(NPCX_DISIDL_CTL, NPCX_DISIDL_CTL_RAM_DID);
-
- /* Set deep idle mode*/
- NPCX_PMCSR = 0x6;
-
- /* Enter deep idle, wake-up by GPIOxx or RTC */
- /*
- * TODO (ML): Although the probability is small, it still has chance
- * to meet the same symptom that CPU's behavior is abnormal after
- * wake-up from deep idle.
- * Workaround: Apply the same bypass of idle but don't enable interrupt.
- */
- asm (
- "push {r0-r5}\n" /* Save needed registers */
- "ldr r0, =0x40001600\n" /* Set r0 to Suspend RAM addr */
- "wfi\n" /* Wait for int to enter idle */
- "ldm r0, {r0-r5}\n" /* Add a delay after WFI */
- "pop {r0-r5}\n" /* Restore regs before enabling ints */
- "isb\n" /* Flush the cpu pipeline */
- );
-
- /* RTC wake-up */
- if (IS_BIT_SET(NPCX_WTC, NPCX_WTC_PTO))
- /*
- * Mark wake-up reason for hibernate
- * Do not call bbram_data_write directly cause of
- * executing in low-power ram
- */
- NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_MTC;
- else
- /* Otherwise, we treat it as GPIOs wake-up */
- NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_PIN;
-
- /* Start a watchdog reset */
- NPCX_WDCNT = 0x01;
- /* Reload and restart Timer 0*/
- SET_BIT(NPCX_T0CSR, NPCX_T0CSR_RST);
- /* Wait for timer is loaded and restart */
- while (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_RST))
- ;
-
- /* Spin and wait for reboot; should never return */
- while (1)
- ;
-}
-
-/**
* Chip-level function to set GPIOs and wake-up inputs for hibernate.
*/
void system_set_gpios_and_wakeup_inputs_hibernate(void)
@@ -412,16 +291,28 @@ void system_set_gpios_and_wakeup_inputs_hibernate(void)
}
}
+#if defined(CHIP_FAMILY_NPCX7)
+ /* Disable MIWU 2 group 6 inputs which used for the additional GPIOs */
+ NPCX_WKEN(MIWU_TABLE_2, MIWU_GROUP_6) = 0x00;
+ NPCX_WKPCL(MIWU_TABLE_2, MIWU_GROUP_6) = 0xFF;
+ NPCX_WKINEN(MIWU_TABLE_2, MIWU_GROUP_6) = 0x00;
+#endif
+
/* Enable wake-up inputs of hibernate_wake_pins array */
for (i = 0; i < hibernate_wake_pins_used; i++) {
gpio_reset(hibernate_wake_pins[i]);
/* Re-enable interrupt for wake-up inputs */
gpio_enable_interrupt(hibernate_wake_pins[i]);
+#if defined(CONFIG_HIBERNATE_PSL)
+ /* Config PSL pins setting for wake-up inputs */
+ if (!system_config_psl_mode(hibernate_wake_pins[i]))
+ ccprintf("Invalid PSL setting in wake-up pin %d\n", i);
+#endif
}
}
/**
- * Internal hibernate function.
+ * hibernate function for npcx ec.
*
* @param seconds Number of seconds to sleep before LCT alarm
* @param microseconds Number of microseconds to sleep before LCT alarm
@@ -429,11 +320,6 @@ void system_set_gpios_and_wakeup_inputs_hibernate(void)
void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
{
int i;
- void (*__hibernate_in_lpram)(void) =
- (void(*)(void))(__lpram_fw_start | 0x01);
-
- /* Enable power for the Low Power RAM */
- CLEAR_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6), 6);
/* Disable ADC */
NPCX_ADCCNF = 0;
@@ -445,6 +331,7 @@ void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
/* Disable instant wake up mode for better power consumption */
CLEAR_BIT(NPCX_ENIDL_CTL, NPCX_ENIDL_CTL_LP_WK_CTL);
+ /* Disable interrupt */
interrupt_disable();
/* ITIM event module disable */
@@ -459,20 +346,12 @@ void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
NPCX_WDSDM = 0x61;
NPCX_WDSDM = 0x63;
- /* Enable Low Power RAM */
- NPCX_LPRAM_CTRL = 1;
-
/* Initialize watchdog */
NPCX_TWCFG = 0; /* Select T0IN clock as watchdog prescaler clock */
SET_BIT(NPCX_TWCFG, NPCX_TWCFG_WDCT0I);
NPCX_TWCP = 0x00; /* Keep prescaler ratio timer0 clock to 1:1 */
NPCX_TWDT0 = 0x00; /* Set internal counter and prescaler */
- /* Copy the __enter_hibernate_in_lpram instructions to LPRAM */
- for (i = 0; i < &__flash_lpfw_end - &__flash_lpfw_start; i++)
- *((uint32_t *)__lpram_fw_start + i) =
- *(&__flash_lpfw_start + i);
-
/* Disable interrupt */
interrupt_disable();
@@ -500,8 +379,9 @@ void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
if (seconds || microseconds)
system_set_rtc_alarm(seconds, microseconds);
- /* execute hibernate func in LPRAM */
- __hibernate_in_lpram();
+
+ /* execute hibernate func depend on chip series */
+ __hibernate_npcx_series();
}
@@ -620,12 +500,21 @@ void system_pre_init(void)
/* Power-down the modules we don't need */
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_1) = 0xF9; /* Skip SDP_PD FIU_PD */
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_2) = 0xFF;
+#if defined(CHIP_FAMILY_NPCX5)
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_3) = 0x0F; /* Skip GDMA */
+#elif defined(CHIP_FAMILY_NPCX7)
+ NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_3) = 0x1F; /* Skip GDMA */
+#endif
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_4) = 0xF4; /* Skip ITIM2/1_PD */
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_5) = 0xF8;
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6) = 0xF5; /* Skip ITIM5_PD */
+#if defined(CHIP_FAMILY_NPCX7)
+ NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_7) = 0x07;
+#endif
- /* Power down the modules used internally */
+ /* Following modules can be powered down automatically in npcx7 */
+#if defined(CHIP_FAMILY_NPCX5)
+ /* Power down the modules of npcx5 used internally */
NPCX_INTERNAL_CTRL1 = 0x03;
NPCX_INTERNAL_CTRL2 = 0x03;
NPCX_INTERNAL_CTRL3 = 0x03;
@@ -633,6 +522,7 @@ void system_pre_init(void)
/* Enable low-power regulator */
CLEAR_BIT(NPCX_LFCGCALCNT, NPCX_LFCGCALCNT_LPREG_CTL_EN);
SET_BIT(NPCX_LFCGCALCNT, NPCX_LFCGCALCNT_LPREG_CTL_EN);
+#endif
/*
* Configure LPRAM in the MPU as a regular memory
@@ -702,6 +592,7 @@ const char *system_get_chip_name(void)
/* Read Chip ID in core register */
uint8_t chip_id = NPCX_DEVICE_ID_CR;
switch (chip_id) {
+#if defined(CHIP_FAMILY_NPCX5)
case 0x12:
return "NPCX585G";
case 0x13:
@@ -710,6 +601,10 @@ const char *system_get_chip_name(void)
return "NPCX586G";
case 0x17:
return "NPCX576G";
+#elif defined(CHIP_FAMILY_NPCX7)
+ case 0x21:
+ return "NPCX796F";
+#endif
default:
*p = system_to_hex((chip_id & 0xF0) >> 4);
*(p + 1) = system_to_hex(chip_id & 0x0F);
@@ -896,116 +791,6 @@ DECLARE_HOST_COMMAND(EC_CMD_RTC_GET_ALARM,
#endif /* CONFIG_HOSTCMD_RTC */
#ifdef CONFIG_EXTERNAL_STORAGE
-void __keep __attribute__ ((noreturn, section(".lowpower_ram2")))
-__start_gdma(uint32_t exeAddr)
-{
- /* Enable GDMA now */
- SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAEN);
-
- /* Start GDMA */
- SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_SOFTREQ);
-
- /* Wait for transfer to complete/fail */
- while (!IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_TC) &&
- !IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAERR))
- ;
-
- /* Disable GDMA now */
- CLEAR_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAEN);
-
- /*
- * Failure occurs during GMDA transaction. Let watchdog issue and
- * boot from RO region again.
- */
- if (IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAERR))
- while (1)
- ;
-
- /*
- * Jump to the exeAddr address if needed. Setting bit 0 of address to
- * indicate it's a thumb branch for cortex-m series CPU.
- */
- ((void (*)(void))(exeAddr | 0x01))();
-
- /* Should never get here */
- while (1)
- ;
-}
-
-static void system_download_from_flash(uint32_t srcAddr, uint32_t dstAddr,
- uint32_t size, uint32_t exeAddr)
-{
- int i;
- uint8_t chunkSize = 16; /* 4 data burst mode. ie.16 bytes */
- /*
- * GDMA utility in Suspend RAM. Setting bit 0 of address to indicate
- * it's a thumb branch for cortex-m series CPU.
- */
- void (*__start_gdma_in_lpram)(uint32_t) =
- (void(*)(uint32_t))(__lpram_lfw_start | 0x01);
-
- /*
- * Before enabling burst mode for better performance of GDMA, it's
- * important to make sure srcAddr, dstAddr and size of transactions
- * are 16 bytes aligned in case failure occurs.
- */
- ASSERT((size % chunkSize) == 0 && (srcAddr % chunkSize) == 0 &&
- (dstAddr % chunkSize) == 0);
-
- /* Check valid address for jumpiing */
- ASSERT(exeAddr != 0x0);
-
- /* Enable power for the Low Power RAM */
- CLEAR_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6), 6);
-
- /* Enable Low Power RAM */
- NPCX_LPRAM_CTRL = 1;
-
- /*
- * Initialize GDMA for flash reading.
- * [31:21] - Reserved.
- * [20] - GDMAERR = 0 (Indicate GMDA transfer error)
- * [19] - Reserved.
- * [18] - TC = 0 (Terminal Count. Indicate operation is end.)
- * [17] - Reserved.
- * [16] - SOFTREQ = 0 (Don't trigger here)
- * [15] - DM = 0 (Set normal demand mode)
- * [14] - Reserved.
- * [13:12] - TWS. = 10 (One double-word for every GDMA transaction)
- * [11:10] - Reserved.
- * [9] - BME = 1 (4-data ie.16 bytes - Burst mode enable)
- * [8] - SIEN = 0 (Stop interrupt disable)
- * [7] - SAFIX = 0 (Fixed source address)
- * [6] - Reserved.
- * [5] - SADIR = 0 (Source address incremented)
- * [4] - DADIR = 0 (Destination address incremented)
- * [3:2] - GDMAMS = 00 (Software mode)
- * [1] - Reserved.
- * [0] - ENABLE = 0 (Don't enable yet)
- */
- NPCX_GDMA_CTL = 0x00002200;
-
- /* Set source base address */
- NPCX_GDMA_SRCB = CONFIG_MAPPED_STORAGE_BASE + srcAddr;
-
- /* Set destination base address */
- NPCX_GDMA_DSTB = dstAddr;
-
- /* Set number of transfers */
- NPCX_GDMA_TCNT = (size / chunkSize);
-
- /* Clear Transfer Complete event */
- SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_TC);
-
- /* Copy the __start_gdma_in_lpram instructions to LPRAM */
- for (i = 0; i < &__flash_lplfw_end - &__flash_lplfw_start; i++)
- *((uint32_t *)__lpram_lfw_start + i) =
- *(&__flash_lplfw_start + i);
-
- /* Start GDMA in Suspend RAM */
- __start_gdma_in_lpram(exeAddr);
-}
-
void system_jump_to_booter(void)
{
enum API_RETURN_STATUS_T status __attribute__((unused));
@@ -1038,7 +823,7 @@ void system_jump_to_booter(void)
clock_turbo();
/* Bypass for GMDA issue of ROM api utilities */
-#if defined(CHIP_VARIANT_NPCX5M5G) || defined(CHIP_VARIANT_NPCX5M6G)
+#if defined(CHIP_FAMILY_NPCX5)
system_download_from_flash(
flash_offset, /* The offset of the data in spi flash */
CONFIG_PROGRAM_MEMORY_BASE, /* RAM Addr of downloaded data */
diff --git a/chip/npcx/system_chip.h b/chip/npcx/system_chip.h
index c32c163c6a..edec22e284 100644
--- a/chip/npcx/system_chip.h
+++ b/chip/npcx/system_chip.h
@@ -8,6 +8,10 @@
#ifndef __CROS_EC_SYSTEM_CHIP_H
#define __CROS_EC_SYSTEM_CHIP_H
+/* Flags for BBRM_DATA_INDEX_WAKE */
+#define HIBERNATE_WAKE_MTC (1 << 0) /* MTC alarm */
+#define HIBERNATE_WAKE_PIN (1 << 1) /* Wake pin */
+
/* Indices for battery-backed ram (BBRAM) data position */
enum bbram_data_index {
BBRM_DATA_INDEX_SCRATCHPAD = 0, /* General-purpose scratchpad */
@@ -21,17 +25,41 @@ enum bbram_data_index {
/* Issue a watchdog reset*/
void system_watchdog_reset(void);
-/* Check reset cause and return reset flags */
-void system_check_reset_cause(void);
-/* Begin flash address for hibernate utility; defined in linker script */
+/*
+ * Configure the specific memory addresses in the the MPU
+ * (Memory Protection Unit) for Nuvoton different chip series.
+ */
+void system_mpu_config(void);
+
+/* Hibernate function for different Nuvoton chip series. */
+void __hibernate_npcx_series(void);
+
+/* The utilities and variables depend on npcx chip family */
+#if defined(CHIP_FAMILY_NPCX5)
+/* Bypass for GMDA issue of ROM api utilities only on npcx5 series */
+void system_download_from_flash(uint32_t srcAddr, uint32_t dstAddr,
+ uint32_t size, uint32_t exeAddr);
+
+/* Begin address for hibernate utility; defined in linker script */
extern unsigned int __flash_lpfw_start;
-/* End flash address for hibernate utility; defined in linker script */
+
+/* End address for hibernate utility; defined in linker script */
extern unsigned int __flash_lpfw_end;
-/* Begin flash address for little FW; defined in linker script */
+/* Begin address for little FW; defined in linker script */
extern unsigned int __flash_lplfw_start;
-/* End flash address for little FW; defined in linker script */
+
+/* End address for little FW; defined in linker script */
extern unsigned int __flash_lplfw_end;
+#elif defined(CHIP_FAMILY_NPCX7)
+/* Configure PSL mode setting for the wake-up pins. */
+int system_config_psl_mode(enum gpio_signal signal);
+
+/* End address for hibernate utility; defined in linker script */
+extern unsigned int __after_init_end;
+
+#endif
+
#endif /* __CROS_EC_SYSTEM_CHIP_H */
diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S
index 47540ffb4a..3390a0a58f 100644
--- a/core/cortex-m/ec.lds.S
+++ b/core/cortex-m/ec.lds.S
@@ -87,6 +87,12 @@ SECTIONS
. = ALIGN(4);
STRINGIFY(OUTDIR/core/CORE/init.o) (.text)
+#if defined(CHIP_FAMILY_NPCX7) && !defined(CONFIG_HIBERNATE_PSL)
+ /* Keep hibernate utility in last code ram block */
+ . = ALIGN(4);
+ KEEP(*(.after_init))
+ __after_init_end = .;
+#endif
*(.text*)
#ifdef CONFIG_EXTERNAL_STORAGE
. = ALIGN(4);
diff --git a/include/config.h b/include/config.h
index ec962a3bdc..8ecabb0809 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1392,6 +1392,13 @@
/* For ECs with multiple wakeup pins, define enabled wakeup pins */
#undef CONFIG_HIBERNATE_WAKEUP_PINS
+/*
+ * Use PSL (Power Switch Logic) for hibernating. It turns off VCC power rail
+ * for ultra-low power consumption and uses PSL inputs rely on VSBY power rail
+ * to wake up ec and the whole system.
+ */
+#undef CONFIG_HIBERNATE_PSL
+
/* Use a hardware specific udelay(). */
#undef CONFIG_HW_SPECIFIC_UDELAY
diff --git a/include/gpio.h b/include/gpio.h
index 8e6fd19444..35f53801de 100644
--- a/include/gpio.h
+++ b/include/gpio.h
@@ -32,6 +32,7 @@
#define GPIO_SEL_1P8V (1 << 16) /* Support 1.8v */
#define GPIO_ALTERNATE (1 << 17) /* GPIO used for alternate function. */
#define GPIO_LOCKED (1 << 18) /* Lock GPIO output and configuration */
+#define GPIO_HIB_WAKE_HIGH (1 << 19) /* Hibernate wake on high level */
/* Common flag combinations */
#define GPIO_OUT_LOW (GPIO_OUTPUT | GPIO_LOW)