summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)