summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDino Li <dino.li@ite.com.tw>2015-10-20 16:55:43 +0800
committerchrome-bot <chrome-bot@chromium.org>2015-10-25 04:34:36 -0700
commit19c1e9905db46b0a669d0af8848be3f28dcedf41 (patch)
tree865aa1fbaf88bf4ca44d31c040c6f1484cbef526
parent9acd84460e953dd793c9594b141ca7e38568820d (diff)
downloadchrome-ec-19c1e9905db46b0a669d0af8848be3f28dcedf41.tar.gz
it8380dev: fix clock module
1. Implement deep doze mode for CONFIG_LOW_POWER_IDLE. Signed-off-by: Dino Li <dino.li@ite.com.tw> BRANCH=none BUG=none TEST=test the following items in deep doze mode. 1. WUI interrupts wake-up OK. (For example, power button, lid, uart rx, keyboard ksi, and so on) 2. LPC access interrupt wake-up OK. 3. Enabled Hook debug, no warning message received (48hrs). Change-Id: I8702a112632cb6c1c0fa75d682badf272130a7d4 Reviewed-on: https://chromium-review.googlesource.com/307060 Commit-Ready: Dino Li <dino.li@ite.com.tw> Tested-by: Dino Li <dino.li@ite.com.tw> Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/it8380dev/board.c8
-rw-r--r--board/it8380dev/board.h5
-rw-r--r--board/it8380dev/gpio.inc3
-rw-r--r--chip/it83xx/clock.c266
-rw-r--r--chip/it83xx/config_chip.h1
-rw-r--r--chip/it83xx/gpio.c12
-rw-r--r--chip/it83xx/hwtimer.c43
-rw-r--r--chip/it83xx/hwtimer_chip.h23
-rw-r--r--chip/it83xx/intc.h2
-rw-r--r--chip/it83xx/registers.h6
-rw-r--r--chip/it83xx/uart.c22
-rw-r--r--core/nds32/task.c45
12 files changed, 399 insertions, 37 deletions
diff --git a/board/it8380dev/board.c b/board/it8380dev/board.c
index 1223e01c65..0a746761c5 100644
--- a/board/it8380dev/board.c
+++ b/board/it8380dev/board.c
@@ -23,8 +23,10 @@
#include "registers.h"
#include "spi.h"
#include "switch.h"
+#include "system.h"
#include "task.h"
#include "timer.h"
+#include "uart.h"
#include "util.h"
#include "gpio_list.h"
@@ -173,7 +175,11 @@ BUILD_ASSERT(ARRAY_SIZE(pnpcfg_settings) == EC2I_SETTING_COUNT);
/* Initialize board. */
static void board_init(void)
{
-
+ /*
+ * Default no low power idle for EVB,
+ * use console command "sleepmask" to enable it if necessary.
+ */
+ disable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
diff --git a/board/it8380dev/board.h b/board/it8380dev/board.h
index d42e167f70..bb9d1dfad2 100644
--- a/board/it8380dev/board.h
+++ b/board/it8380dev/board.h
@@ -9,16 +9,17 @@
#define __CROS_EC_BOARD_H
/* Optional features */
-#undef CHIP_FAMILY_IT839X
+#define CHIP_FAMILY_IT839X
#define CONFIG_BATTERY_SMART
#define CONFIG_FANS 1
-#undef CONFIG_IT83XX_KEYBOARD_KSI_WUC_INT
+#define CONFIG_IT83XX_KEYBOARD_KSI_WUC_INT
#define CONFIG_IT83XX_LPC_ACCESS_INT
#define CONFIG_IT83XX_PECI_WITH_INTERRUPT
#define CONFIG_IT83XX_SMCLK2_ON_GPC7
#define CONFIG_KEYBOARD_BOARD_CONFIG
#define CONFIG_KEYBOARD_PROTOCOL_8042
+#define CONFIG_LOW_POWER_IDLE
#define CONFIG_PECI_TJMAX 100
#define CONFIG_POWER_BUTTON
/* Use CS0 of SSPI */
diff --git a/board/it8380dev/gpio.inc b/board/it8380dev/gpio.inc
index 282fa6fe52..49a5929f06 100644
--- a/board/it8380dev/gpio.inc
+++ b/board/it8380dev/gpio.inc
@@ -9,6 +9,9 @@ GPIO_INT(POWER_BUTTON_L, PIN(E, 4), GPIO_INT_BOTH | GPIO_PULL_UP, power_button
GPIO_INT(PCH_PLTRST_L, PIN(E, 3), GPIO_INT_BOTH | GPIO_PULL_UP, lpcrst_interrupt)
GPIO_INT(LID_OPEN, PIN(E, 2), GPIO_INT_BOTH | GPIO_PULL_DOWN, lid_interrupt)
GPIO_INT(WP_L, PIN(E, 1), GPIO_INT_BOTH, switch_interrupt) /* Write protect input */
+#ifdef CONFIG_LOW_POWER_IDLE
+GPIO_INT(UART1_RX, PIN(B, 0), GPIO_INT_FALLING, uart_deepsleep_interrupt) /* UART1 RX input */
+#endif
GPIO(CAPS_LED, PIN(H, 1), GPIO_OUT_LOW)
GPIO(SCRO_LED, PIN(H, 2), GPIO_OUT_LOW)
diff --git a/chip/it83xx/clock.c b/chip/it83xx/clock.c
index 4fe72245da..25c5dffd1b 100644
--- a/chip/it83xx/clock.c
+++ b/chip/it83xx/clock.c
@@ -8,15 +8,42 @@
#include "clock.h"
#include "common.h"
#include "console.h"
+#include "hwtimer.h"
+#include "hwtimer_chip.h"
#include "registers.h"
+#include "system.h"
#include "task.h"
#include "timer.h"
+#include "uart.h"
#include "util.h"
/* Console output macros. */
#define CPUTS(outstr) cputs(CC_CLOCK, outstr)
#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args)
+#ifdef CONFIG_LOW_POWER_IDLE
+#define SLEEP_SET_HTIMER_DELAY_USEC 250
+#define SLEEP_FTIMER_SKIP_USEC (HOOK_TICK_INTERVAL * 2)
+
+static timestamp_t sleep_mode_t0;
+static timestamp_t sleep_mode_t1;
+static int idle_doze_cnt;
+static int idle_sleep_cnt;
+static uint64_t total_idle_sleep_time_us;
+static int allow_sleep;
+/*
+ * Fixed amount of time to keep the console in use flag true after boot in
+ * order to give a permanent window in which the heavy sleep mode is not used.
+ */
+#define CONSOLE_IN_USE_ON_BOOT_TIME (15*SECOND)
+static int console_in_use_timeout_sec = 5;
+static timestamp_t console_expire_time;
+
+/* clock source is 32.768KHz */
+#define TIMER_32P768K_CNT_TO_US(cnt) ((cnt) * 32768 / 1000)
+#define TIMER_CNT_8M_32P768K(cnt) (((cnt) / 262) + 1)
+#endif /*CONFIG_LOW_POWER_IDLE */
+
static int freq;
struct clock_gate_ctrl {
@@ -49,6 +76,9 @@ void clock_init(void)
/* Turn off auto clock gating. */
IT83XX_ECPM_AUTOCG = 0x00;
+
+ /* Default doze mode */
+ IT83XX_ECPM_PLLCTRL = EC_PLL_DOZE;
}
int clock_get_freq(void)
@@ -100,3 +130,239 @@ void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode)
*reg |= reg_mask | tmp_mask;
}
+
+#ifdef CONFIG_LOW_POWER_IDLE
+void clock_refresh_console_in_use(void)
+{
+ /* Set console in use expire time. */
+ console_expire_time = get_time();
+ console_expire_time.val += console_in_use_timeout_sec * SECOND;
+}
+
+static void clock_event_timer_clock_change(enum ext_timer_clock_source clock,
+ uint32_t count)
+{
+ IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) &= ~(1 << 0);
+ IT83XX_ETWD_ETXPSR(EVENT_EXT_TIMER) = clock;
+ IT83XX_ETWD_ETXCNTLR(EVENT_EXT_TIMER) = count;
+ IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) |= 0x3;
+}
+
+static void clock_htimer_enable(void)
+{
+ uint32_t c;
+
+ /* disable free running interrupt */
+ task_disable_irq(et_ctrl_regs[FREE_EXT_TIMER_H].irq);
+ task_disable_irq(et_ctrl_regs[FREE_EXT_TIMER_L].irq);
+ /* change event timer clock source to 32.768 KHz */
+ c = TIMER_CNT_8M_32P768K(IT83XX_ETWD_ETXCNTOR(EVENT_EXT_TIMER));
+ clock_event_timer_clock_change(EXT_PSR_32P768K_HZ, c);
+}
+
+static int clock_allow_low_power_idle(void)
+{
+ if (!(IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) & (1 << 0)))
+ return 0;
+
+ if (*et_ctrl_regs[EVENT_EXT_TIMER].isr &
+ et_ctrl_regs[EVENT_EXT_TIMER].mask)
+ return 0;
+
+ if (EVENT_TIMER_COUNT_TO_US(IT83XX_ETWD_ETXCNTOR(EVENT_EXT_TIMER)) <
+ SLEEP_SET_HTIMER_DELAY_USEC)
+ return 0;
+
+ if (TIMER_L_COUNT_TO_US(IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_L)) <
+ SLEEP_SET_HTIMER_DELAY_USEC)
+ return 0;
+
+ sleep_mode_t0 = get_time();
+ if ((sleep_mode_t0.le.lo > (0xffffffff - SLEEP_FTIMER_SKIP_USEC)) ||
+ (sleep_mode_t0.le.lo < SLEEP_FTIMER_SKIP_USEC))
+ return 0;
+
+ if (sleep_mode_t0.val < console_expire_time.val)
+ return 0;
+
+ return 1;
+}
+
+static void clock_ec_pll_ctrl(enum ec_pll_ctrl mode)
+{
+ IT83XX_ECPM_PLLCTRL = mode;
+#ifdef CHIP_FAMILY_IT839X
+ /* for deep doze / sleep mode */
+ IT83XX_ECPM_PLLCTRL = mode;
+#endif
+ asm volatile ("dsb");
+}
+
+void clock_sleep_mode_wakeup_isr(void)
+{
+ uint32_t st_us, c;
+
+ if (IT83XX_ECPM_PLLCTRL != EC_PLL_DOZE) {
+ clock_ec_pll_ctrl(EC_PLL_DOZE);
+
+ /* update free running timer */
+ c = 0xffffffff - IT83XX_ETWD_ETXCNTOR(LOW_POWER_EXT_TIMER);
+ st_us = TIMER_32P768K_CNT_TO_US(c);
+ sleep_mode_t1.val = sleep_mode_t0.val + st_us;
+ /*
+ * When TIMER_L underflow, and because the observation value
+ * equals to counter setting register, we need a window of
+ * 64us (at minimum) to reset the value of TIMER_L back to
+ * 0xfffff8(TIMER_L_COUNT_TO_US(0xffffffff))
+ */
+ c = TIMER_L_US_TO_COUNT(0xffffffff - sleep_mode_t1.le.lo);
+ if (TIMER_L_COUNT_TO_US(c) < 64) {
+ sleep_mode_t1.le.lo |= 0x3F;
+ sleep_mode_t1.le.lo &= ~(1 << 6);
+ }
+ __hw_clock_source_set(sleep_mode_t1.le.lo);
+
+ /* reset event timer and clock source is 8 MHz */
+ clock_event_timer_clock_change(EXT_PSR_8M_HZ, 0xffffffff);
+ task_clear_pending_irq(et_ctrl_regs[EVENT_EXT_TIMER].irq);
+ process_timers(0);
+ /* disable uart wui */
+ uart_exit_dsleep();
+ /* Record time spent in sleep. */
+ total_idle_sleep_time_us += st_us;
+ }
+}
+
+/**
+ * Low power idle task. Executed when no tasks are ready to be scheduled.
+ */
+void __idle(void)
+{
+ console_expire_time.val = get_time().val + CONSOLE_IN_USE_ON_BOOT_TIME;
+ /* init hw timer and clock source is 32.768 KHz */
+ ext_timer_ms(LOW_POWER_EXT_TIMER, EXT_PSR_32P768K_HZ, 1, 0,
+ 0xffffffff, 1, 1);
+
+#if defined(CONFIG_LPC) && defined(CONFIG_IT83XX_LPC_ACCESS_INT)
+ IT83XX_WUC_WUESR4 = 0xff;
+ task_clear_pending_irq(IT83XX_IRQ_WKINTAD);
+ /* bit2, wake-up enable for LPC access */
+ IT83XX_WUC_WUENR4 |= (1 << 2);
+#endif
+ /*
+ * Print when the idle task starts. This is the lowest priority task,
+ * so this only starts once all other tasks have gotten a chance to do
+ * their task inits and have gone to sleep.
+ */
+ CPRINTS("low power idle task started");
+
+ while (1) {
+#if defined(CONFIG_LPC) && defined(CONFIG_IT83XX_LPC_ACCESS_INT)
+ BRAM_LPC_ACCESS = LPC_ACCESS_INT_BUSY;
+ /* LPC access interrupt pending. */
+ if (IT83XX_WUC_WUESR4 & (1 << 2)) {
+ task_enable_irq(IT83XX_IRQ_WKINTAD);
+ continue;
+ }
+ BRAM_LPC_ACCESS = 0;
+ task_enable_irq(IT83XX_IRQ_WKINTAD);
+#endif
+ allow_sleep = 0;
+ if (DEEP_SLEEP_ALLOWED)
+ allow_sleep = clock_allow_low_power_idle();
+
+ if (allow_sleep) {
+ interrupt_disable();
+ /* reset low power mode hw timer */
+ IT83XX_ETWD_ETXCTRL(LOW_POWER_EXT_TIMER) |= (1 << 1);
+ sleep_mode_t0 = get_time();
+ /* enable uart wui */
+ uart_enter_dsleep();
+ /* enable hw timer for deep doze / sleep mode wake-up */
+ clock_htimer_enable();
+ /* deep doze mode */
+ clock_ec_pll_ctrl(EC_PLL_DEEP_DOZE);
+ interrupt_enable();
+ /* standby instruction */
+ asm("standby wake_grant");
+ idle_sleep_cnt++;
+ } else {
+ /* doze mode */
+ clock_ec_pll_ctrl(EC_PLL_DOZE);
+ /* standby instruction */
+ asm("standby wake_grant");
+ idle_doze_cnt++;
+ }
+ }
+}
+#endif /* CONFIG_LOW_POWER_IDLE */
+
+#ifdef CONFIG_LOW_POWER_IDLE
+/**
+ * Print low power idle statistics
+ */
+static int command_idle_stats(int argc, char **argv)
+{
+ timestamp_t ts = get_time();
+
+ ccprintf("Num idle calls that doze: %d\n", idle_doze_cnt);
+ ccprintf("Num idle calls that sleep: %d\n", idle_sleep_cnt);
+
+ ccprintf("Total Time spent in sleep(sec): %.6ld(s)\n",
+ total_idle_sleep_time_us);
+ ccprintf("Total time on: %.6lds\n\n", ts.val);
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats,
+ "",
+ "Print last idle stats",
+ NULL);
+
+/**
+ * Configure deep sleep clock settings.
+ */
+static int command_dsleep(int argc, char **argv)
+{
+ int v;
+
+ if (argc > 1) {
+ if (parse_bool(argv[1], &v)) {
+ /*
+ * Force deep sleep not to use heavy sleep mode or
+ * allow it to use the heavy sleep mode.
+ */
+ if (v) /* 'on' */
+ disable_sleep(SLEEP_MASK_FORCE_NO_LOW_SPEED);
+ else /* 'off' */
+ enable_sleep(SLEEP_MASK_FORCE_NO_LOW_SPEED);
+ } else {
+ /* Set console in use timeout. */
+ char *e;
+
+ v = strtoi(argv[1], &e, 10);
+ if (*e)
+ return EC_ERROR_PARAM1;
+
+ console_in_use_timeout_sec = v;
+
+ /* Refresh console in use to use new timeout. */
+ clock_refresh_console_in_use();
+ }
+ }
+
+ ccprintf("Sleep mask: %08x\n", sleep_mask);
+ ccprintf("Console in use timeout: %d sec\n",
+ console_in_use_timeout_sec);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(dsleep, command_dsleep,
+ "[ on | off | <timeout> sec]",
+ "Deep sleep clock settings:\n"
+ "Use 'on' to force deep sleep NOT to enter heavysleep mode.\n"
+ "Use 'off' to allow deep sleep to use heavysleep whenever\n"
+ "conditions allow.\n"
+ "Give a timeout value for the console in use timeout.\n"
+ "See also 'sleepmask'.",
+ NULL);
+#endif /* CONFIG_LOW_POWER_IDLE */
diff --git a/chip/it83xx/config_chip.h b/chip/it83xx/config_chip.h
index 1e2d5c747a..4e43540a76 100644
--- a/chip/it83xx/config_chip.h
+++ b/chip/it83xx/config_chip.h
@@ -91,6 +91,7 @@
#define CONFIG_FW_RESET_VECTOR
/* Optional features present on this chip */
+#define CHIP_FAMILY_IT83XX
#define CONFIG_ADC
#define CONFIG_EC2I
#define CONFIG_I2C
diff --git a/chip/it83xx/gpio.c b/chip/it83xx/gpio.c
index b3d4d11945..063ed222fb 100644
--- a/chip/it83xx/gpio.c
+++ b/chip/it83xx/gpio.c
@@ -378,6 +378,18 @@ int gpio_disable_interrupt(enum gpio_signal signal)
return EC_SUCCESS;
}
+int gpio_clear_pending_interrupt(enum gpio_signal signal)
+{
+ int irq = gpio_to_irq(gpio_list[signal].port, gpio_list[signal].mask);
+
+ if (irq == -1)
+ return EC_ERROR_UNKNOWN;
+
+ *(wuesr(gpio_irqs[irq].wuc_group)) = gpio_irqs[irq].wuc_mask;
+ task_clear_pending_irq(irq);
+ return EC_SUCCESS;
+}
+
void gpio_pre_init(void)
{
const struct gpio_info *g = gpio_list;
diff --git a/chip/it83xx/hwtimer.c b/chip/it83xx/hwtimer.c
index 77354702b4..c0a3c29bba 100644
--- a/chip/it83xx/hwtimer.c
+++ b/chip/it83xx/hwtimer.c
@@ -56,52 +56,36 @@
* 8 MHz 32-bit timer to handle events.
*/
-#define TIMER_COUNT_1US_SHIFT 3
-
-/* Combinational mode, microseconds to timer counter setting register */
-#define TIMER_H_US_TO_COUNT(us) ((us) >> (24 - TIMER_COUNT_1US_SHIFT))
-#define TIMER_L_US_TO_COUNT(us) (((us) << TIMER_COUNT_1US_SHIFT) & 0x00ffffff)
-
-/* Free running timer counter observation value to microseconds */
-#define TIMER_H_COUNT_TO_US(cnt) ((~(cnt)) << (24 - TIMER_COUNT_1US_SHIFT))
-#define TIMER_L_COUNT_TO_US(cnt) (((cnt) & 0x00ffffff) >> TIMER_COUNT_1US_SHIFT)
-
-/* Microseconds to event timer counter setting register */
-#define EVENT_TIMER_US_TO_COUNT(us) ((us) << TIMER_COUNT_1US_SHIFT)
-/* Event timer counter observation value to microseconds */
-#define EVENT_TIMER_COUNT_TO_US(cnt) ((cnt) >> TIMER_COUNT_1US_SHIFT)
-
-#define TIMER_H_CNT_COMP TIMER_H_US_TO_COUNT(0xffffffff)
-#define TIMER_L_CNT_COMP TIMER_L_US_TO_COUNT(0xffffffff)
-
#define MS_TO_COUNT(hz, ms) ((hz) * (ms) / 1000)
const struct ext_timer_ctrl_t et_ctrl_regs[] = {
- {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x08,
+ {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x08,
IT83XX_IRQ_EXT_TIMER3},
- {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x10,
+ {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x10,
IT83XX_IRQ_EXT_TIMER4},
- {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x20,
+ {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x20,
IT83XX_IRQ_EXT_TIMER5},
- {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x40,
+ {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x40,
IT83XX_IRQ_EXT_TIMER6},
- {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x80,
+ {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x80,
IT83XX_IRQ_EXT_TIMER7},
- {&IT83XX_INTC_IELMR10, &IT83XX_INTC_IPOLR10, 0x01,
+ {&IT83XX_INTC_IELMR10, &IT83XX_INTC_IPOLR10, &IT83XX_INTC_ISR10, 0x01,
IT83XX_IRQ_EXT_TMR8},
};
BUILD_ASSERT(ARRAY_SIZE(et_ctrl_regs) == EXT_TIMER_COUNT);
static void free_run_timer_config_counter(uint32_t us)
{
+ /* bit0, timer stop */
+ IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) &= ~(1 << 0);
/*
* microseconds to timer counter,
* timer 3(TIMER_L) and 4(TIMER_H) combinational mode
*/
IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_H) = TIMER_H_US_TO_COUNT(us);
IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_L) = TIMER_L_US_TO_COUNT(us);
- /* bit1, timer re-start */
- IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) |= (1 << 1);
+ /* bit[0,1], timer start and reset */
+ IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) |= 3;
}
static void free_run_timer_clear_pending_isr(void)
@@ -174,9 +158,9 @@ void __hw_clock_source_set(uint32_t ts)
ext_timer_ms(FREE_EXT_TIMER_L, EXT_PSR_8M_HZ, 1, 1,
TIMER_L_US_TO_COUNT(start_us), 1, 1);
} else {
- free_run_timer_clear_pending_isr();
/* set timer counter only */
free_run_timer_config_counter(start_us);
+ free_run_timer_clear_pending_isr();
task_enable_irq(et_ctrl_regs[FREE_EXT_TIMER_H].irq);
task_enable_irq(et_ctrl_regs[FREE_EXT_TIMER_L].irq);
}
@@ -282,10 +266,13 @@ static void __hw_clock_source_irq(void)
* 0xfffff8(TIMER_L_COUNT_TO_US(0xffffffff)).
*/
if (IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_H)) {
+ /* bit0, timer stop */
+ IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) &= ~(1 << 0);
IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_L) =
TIMER_L_US_TO_COUNT(0xffffffff);
IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_H) -= 1;
- IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) |= (1 << 1);
+ /* bit[0,1], timer start and reset */
+ IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) |= 3;
update_exc_start_time();
} else {
free_run_timer_overflow();
diff --git a/chip/it83xx/hwtimer_chip.h b/chip/it83xx/hwtimer_chip.h
index 690505dcb1..18ee3f0f13 100644
--- a/chip/it83xx/hwtimer_chip.h
+++ b/chip/it83xx/hwtimer_chip.h
@@ -8,11 +8,30 @@
#ifndef __CROS_EC_HWTIMER_CHIP_H
#define __CROS_EC_HWTIMER_CHIP_H
+#define TIMER_COUNT_1US_SHIFT 3
+
+/* Combinational mode, microseconds to timer counter setting register */
+#define TIMER_H_US_TO_COUNT(us) ((us) >> (24 - TIMER_COUNT_1US_SHIFT))
+#define TIMER_L_US_TO_COUNT(us) (((us) << TIMER_COUNT_1US_SHIFT) & 0x00ffffff)
+
+/* Free running timer counter observation value to microseconds */
+#define TIMER_H_COUNT_TO_US(cnt) ((~(cnt)) << (24 - TIMER_COUNT_1US_SHIFT))
+#define TIMER_L_COUNT_TO_US(cnt) (((cnt) & 0x00ffffff) >> TIMER_COUNT_1US_SHIFT)
+
+/* Microseconds to event timer counter setting register */
+#define EVENT_TIMER_US_TO_COUNT(us) ((us) << TIMER_COUNT_1US_SHIFT)
+/* Event timer counter observation value to microseconds */
+#define EVENT_TIMER_COUNT_TO_US(cnt) ((cnt) >> TIMER_COUNT_1US_SHIFT)
+
+#define TIMER_H_CNT_COMP TIMER_H_US_TO_COUNT(0xffffffff)
+#define TIMER_L_CNT_COMP TIMER_L_US_TO_COUNT(0xffffffff)
+
#define FREE_EXT_TIMER_L EXT_TIMER_3
#define FREE_EXT_TIMER_H EXT_TIMER_4
#define FAN_CTRL_EXT_TIMER EXT_TIMER_5
#define EVENT_EXT_TIMER EXT_TIMER_6
#define WDT_EXT_TIMER EXT_TIMER_7
+#define LOW_POWER_EXT_TIMER EXT_TIMER_8
enum ext_timer_clock_source {
EXT_PSR_32P768K_HZ = 0,
@@ -35,7 +54,7 @@ enum ext_timer_sel {
EXT_TIMER_6,
/* For WDT capture important state information before being reset */
EXT_TIMER_7,
- /* reserved */
+ /* HW timer for low power mode */
EXT_TIMER_8,
EXT_TIMER_COUNT,
};
@@ -43,6 +62,7 @@ enum ext_timer_sel {
struct ext_timer_ctrl_t {
volatile uint8_t *mode;
volatile uint8_t *polarity;
+ volatile uint8_t *isr;
uint8_t mask;
uint8_t irq;
};
@@ -52,6 +72,7 @@ void ext_timer_start(enum ext_timer_sel ext_timer, int en_irq);
void ext_timer_stop(enum ext_timer_sel ext_timer, int dis_irq);
void fan_ext_timer_interrupt(void);
void update_exc_start_time(void);
+
/**
* Config a external timer.
*
diff --git a/chip/it83xx/intc.h b/chip/it83xx/intc.h
index 6734436fdc..39d8653819 100644
--- a/chip/it83xx/intc.h
+++ b/chip/it83xx/intc.h
@@ -18,5 +18,7 @@ void pm5_ibf_interrupt(void);
void lpcrst_interrupt(enum gpio_signal signal);
void peci_interrupt(void);
void i2c_interrupt(int port);
+int gpio_clear_pending_interrupt(enum gpio_signal signal);
+void clock_sleep_mode_wakeup_isr(void);
#endif /* __CROS_EC_INTC_H */
diff --git a/chip/it83xx/registers.h b/chip/it83xx/registers.h
index e8a5821ad5..df1339b241 100644
--- a/chip/it83xx/registers.h
+++ b/chip/it83xx/registers.h
@@ -581,6 +581,12 @@ enum {
#define IT83XX_ECPM_CGCTRL4R_OFF 0x09
#define IT83XX_ECPM_PLLCTRL REG8(IT83XX_ECPM_BASE+0x03)
+enum ec_pll_ctrl {
+ EC_PLL_DOZE = 0,
+ EC_PLL_SLEEP = 1,
+ EC_PLL_DEEP_DOZE = 3,
+};
+
#define IT83XX_ECPM_AUTOCG REG8(IT83XX_ECPM_BASE+0x04)
#define IT83XX_ECPM_PLLFREQR REG8(IT83XX_ECPM_BASE+0x06)
#define IT83XX_ECPM_PLLCSS REG8(IT83XX_ECPM_BASE+0x08)
diff --git a/chip/it83xx/uart.c b/chip/it83xx/uart.c
index a396e76316..4d8f9cbe98 100644
--- a/chip/it83xx/uart.c
+++ b/chip/it83xx/uart.c
@@ -9,6 +9,7 @@
#include "common.h"
#include "console.h"
#include "gpio.h"
+#include "intc.h"
#include "registers.h"
#include "system.h"
#include "task.h"
@@ -178,6 +179,27 @@ static void host_uart_config(void)
}
#endif
+#ifdef CONFIG_LOW_POWER_IDLE
+void uart_enter_dsleep(void)
+{
+ gpio_clear_pending_interrupt(GPIO_UART1_RX);
+ gpio_enable_interrupt(GPIO_UART1_RX);
+}
+
+void uart_exit_dsleep(void)
+{
+ gpio_disable_interrupt(GPIO_UART1_RX);
+ gpio_clear_pending_interrupt(GPIO_UART1_RX);
+}
+
+void uart_deepsleep_interrupt(enum gpio_signal signal)
+{
+ clock_refresh_console_in_use();
+ /* Disable interrupts on UART1 RX pin to avoid repeated interrupts. */
+ gpio_disable_interrupt(GPIO_UART1_RX);
+}
+#endif /* CONFIG_LOW_POWER_IDLE */
+
void uart_init(void)
{
/* reset uart before config it */
diff --git a/core/nds32/task.c b/core/nds32/task.c
index 7edad8b6e4..a7a3bc6688 100644
--- a/core/nds32/task.c
+++ b/core/nds32/task.c
@@ -9,6 +9,8 @@
#include "common.h"
#include "console.h"
#include "cpu.h"
+#include "hwtimer_chip.h"
+#include "intc.h"
#include "irq_chip.h"
#include "link_defs.h"
#include "registers.h"
@@ -58,6 +60,10 @@ static uint64_t exc_total_time; /* Total time in exceptions */
static uint32_t svc_calls; /* Number of service calls */
static uint32_t task_switches; /* Number of times active task changed */
static uint32_t irq_dist[CONFIG_IRQ_COUNT]; /* Distribution of IRQ calls */
+#if defined(CONFIG_LOW_POWER_IDLE) && defined(CHIP_FAMILY_IT83XX)
+static uint32_t exc_current_fth;
+static uint32_t exc_current_ftl;
+#endif
#endif
extern int __task_start(void);
@@ -92,8 +98,10 @@ void __idle(void)
task_enable_irq(IT83XX_IRQ_WKINTAD);
#endif
+#ifdef CHIP_FAMILY_IT83XX
/* doze mode */
- IT83XX_ECPM_PLLCTRL = 0x00;
+ IT83XX_ECPM_PLLCTRL = EC_PLL_DOZE;
+#endif
asm volatile ("dsb");
/*
* Wait for the next irq event. This stops the CPU clock
@@ -341,6 +349,10 @@ void update_exc_start_time(void)
{
#ifdef CONFIG_TASK_PROFILING
exc_start_time = get_time().val;
+#if defined(CONFIG_LOW_POWER_IDLE) && defined(CHIP_FAMILY_IT83XX)
+ exc_current_fth = IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_H);
+ exc_current_ftl = IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_L);
+#endif
#endif
}
@@ -348,15 +360,20 @@ void start_irq_handler(void)
{
#ifdef CONFIG_TASK_PROFILING
int irq;
-
+#endif
/* save r0, r1, and r2 for syscall */
asm volatile ("smw.adm $r0, [$sp], $r2, 0");
-
+#if defined(CONFIG_LOW_POWER_IDLE) && defined(CHIP_FAMILY_IT83XX)
+ clock_sleep_mode_wakeup_isr();
+#endif
+#ifdef CONFIG_TASK_PROFILING
update_exc_start_time();
irq = get_sw_int();
+#ifdef CHIP_FAMILY_IT83XX
if (!irq)
irq = IT83XX_INTC_AIVCT - 16;
+#endif
/*
* Track IRQ distribution. No need for atomic add, because an IRQ
@@ -364,10 +381,9 @@ void start_irq_handler(void)
*/
if ((irq > 0) && (irq < ARRAY_SIZE(irq_dist)))
irq_dist[irq]++;
-
+#endif
/* restore r0, r1, and r2 */
asm volatile ("lmw.bim $r0, [$sp], $r2, 0");
-#endif
}
void end_irq_handler(void)
@@ -375,6 +391,9 @@ void end_irq_handler(void)
#ifdef CONFIG_TASK_PROFILING
uint64_t t, p;
+#if defined(CONFIG_LOW_POWER_IDLE) && defined(CHIP_FAMILY_IT83XX)
+ uint32_t c;
+#endif
/*
* save r0 and fp (fp for restore r0-r5, r15, fp, lp and sp
* while interrupt exit.
@@ -382,6 +401,13 @@ void end_irq_handler(void)
asm volatile ("smw.adm $r0, [$sp], $r0, 8");
t = get_time().val;
+#if defined(CONFIG_LOW_POWER_IDLE) && defined(CHIP_FAMILY_IT83XX)
+ if (exc_current_fth != IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_H)) {
+ c = (IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_L) + exc_current_ftl) -
+ IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_L);
+ t = exc_start_time + (c >> TIMER_COUNT_1US_SHIFT);
+ }
+#endif
p = t - exc_start_time;
exc_total_time += p;
@@ -478,12 +504,20 @@ void task_enable_all_tasks(void)
void task_enable_irq(int irq)
{
+ uint32_t int_mask = get_int_mask();
+
+ interrupt_disable();
chip_enable_irq(irq);
+ set_int_mask(int_mask);
}
void task_disable_irq(int irq)
{
+ uint32_t int_mask = get_int_mask();
+
+ interrupt_disable();
chip_disable_irq(irq);
+ set_int_mask(int_mask);
}
void task_clear_pending_irq(int irq)
@@ -494,6 +528,7 @@ void task_clear_pending_irq(int irq)
void task_trigger_irq(int irq)
{
int cpu_int = chip_trigger_irq(irq);
+
if (cpu_int > 0) {
sw_int_num = irq;
__schedule(0, 0, cpu_int);