diff options
author | Dino Li <dino.li@ite.com.tw> | 2015-10-20 16:55:43 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2015-10-25 04:34:36 -0700 |
commit | 19c1e9905db46b0a669d0af8848be3f28dcedf41 (patch) | |
tree | 865aa1fbaf88bf4ca44d31c040c6f1484cbef526 | |
parent | 9acd84460e953dd793c9594b141ca7e38568820d (diff) | |
download | chrome-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.c | 8 | ||||
-rw-r--r-- | board/it8380dev/board.h | 5 | ||||
-rw-r--r-- | board/it8380dev/gpio.inc | 3 | ||||
-rw-r--r-- | chip/it83xx/clock.c | 266 | ||||
-rw-r--r-- | chip/it83xx/config_chip.h | 1 | ||||
-rw-r--r-- | chip/it83xx/gpio.c | 12 | ||||
-rw-r--r-- | chip/it83xx/hwtimer.c | 43 | ||||
-rw-r--r-- | chip/it83xx/hwtimer_chip.h | 23 | ||||
-rw-r--r-- | chip/it83xx/intc.h | 2 | ||||
-rw-r--r-- | chip/it83xx/registers.h | 6 | ||||
-rw-r--r-- | chip/it83xx/uart.c | 22 | ||||
-rw-r--r-- | core/nds32/task.c | 45 |
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); |