summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMulin Chao <mlchao@nuvoton.com>2016-01-29 18:03:20 +0800
committerchrome-bot <chrome-bot@chromium.org>2016-02-06 01:57:58 -0800
commitf700e3bb0ea187bae315ff925f59c8eab05fb446 (patch)
tree1a0b6eba415c58d906299f554c9d6b0b1e415e79
parent4ec554dd8fbdd47871dcd04374fb04018b24b7b5 (diff)
downloadchrome-ec-f700e3bb0ea187bae315ff925f59c8eab05fb446.tar.gz
nuc: Add support for CONFIG_LOW_POWER_S0.
To get better power consumption in S0, we add FW support for CONFIG_LOW_POWER_S0. Before entering deep idle in S0, we must enable Host interrupt to wake up EC if it needs to service LPC bus. This version also add a new bit of sleep_mask (SLEEP_MASK_FAN) in system.h to prevent EC enter deep idle if fan's duty isn't zero. Normally, the freq of PWM fan is 25 kHz. It means we must select apb2 clock as the source clock of PWM fan. Or fan would stop when ec enters deep idle because of no PWM signal. In hwtimer.c, we reset the preload counter to maximum value in ITEI32's ISR since preload counter is changed by __hw_clock_source_set all the time. We also found there're no event set if it's deadline is over 32 bits but current source clock isn't. To prevent ec doesn't wake-up in deep-idle even if ITIM32 expires, FW set an event for ITIM32 after process_timers(). Modified sources: 1. wheatley/board.h: Add CONFIG_LOW_POWER_S0 definition. 2. clock.c: Enable Host interrupt for LPC. 3. clock.c: Disable LP_WK_CTL for better power consumption. 4. gpio.c: Add ISR for Host interrupt. 5. uart.c: Introduce bit 6 of USTAT to make sure transmitting is completed. 6. register.h: Add uart_clear_pending_wakeup function. 7. hwtimer.c: Fixed watchdog issue when ITIM32 is closed to overflow. 8. fan.c: Enable deep sleep if duty cycle is zero. 9. include/system.h: Add SLEEP_MASK_FAN for fan control loop. 10. core/cortex-m/task.c: Add "isb" to flash the garbage data in the instruction pipeline. BUG=chrome-os-partner:34346 TEST=make buildall -j; test nuvoton IC specific drivers BRANCH=none Change-Id: Ibe3630d0d68cf3f32206adb2afa1b5958916a2be Signed-off-by: Mulin Chao <mlchao@nuvoton.com> Reviewed-on: https://chromium-review.googlesource.com/324651 Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r--board/wheatley/board.h1
-rw-r--r--chip/npcx/clock.c44
-rw-r--r--chip/npcx/fan.c11
-rw-r--r--chip/npcx/gpio.c14
-rw-r--r--chip/npcx/hwtimer.c76
-rw-r--r--chip/npcx/hwtimer_chip.h4
-rw-r--r--chip/npcx/registers.h10
-rw-r--r--chip/npcx/uart.c6
-rw-r--r--core/cortex-m/task.c1
-rw-r--r--include/system.h1
10 files changed, 125 insertions, 43 deletions
diff --git a/board/wheatley/board.h b/board/wheatley/board.h
index 643a5c91f4..a98aae6b3c 100644
--- a/board/wheatley/board.h
+++ b/board/wheatley/board.h
@@ -51,6 +51,7 @@
#define CONFIG_LID_ANGLE_SENSOR_LID 2
#define CONFIG_LID_SWITCH
#define CONFIG_LOW_POWER_IDLE
+#define CONFIG_LOW_POWER_S0
#define CONFIG_LTO
#define CONFIG_POWER_BUTTON
#define CONFIG_POWER_BUTTON_X86
diff --git a/chip/npcx/clock.c b/chip/npcx/clock.c
index 9eeb06efc0..ae5080b6d4 100644
--- a/chip/npcx/clock.c
+++ b/chip/npcx/clock.c
@@ -205,10 +205,14 @@ void clock_uart2gpio(void)
{
/* Is pimux to UART? */
if (npcx_is_uart()) {
+ /* Flush tx before enter deep idle */
+ uart_tx_flush();
/* Change pinmux to GPIO and disable UART IRQ */
task_disable_irq(NPCX_IRQ_UART);
/* Set to GPIO */
npcx_uart2gpio();
+ /* Clear pending wakeup */
+ uart_clear_pending_wakeup();
/* Enable MIWU for GPIO (UARTRX) */
uart_enable_wakeup(1);
}
@@ -243,7 +247,8 @@ void __idle(void)
#else
timestamp_t t0, t1;
- uint32_t next_evt_us, evt_count;
+ uint32_t next_evt_us;
+ uint16_t evt_count;
/*
* Initialize console in use to true and specify the console expire
@@ -274,13 +279,14 @@ void __idle(void)
CLEAR_BIT(NPCX_PDOUT(0), 0);
#endif
idle_dsleep_cnt++;
- /* Set instant wake up mode */
- SET_BIT(NPCX_ENIDL_CTL, NPCX_ENIDL_CTL_LP_WK_CTL);
- /* Set deep idle - instant wake-up mode */
- NPCX_PMCSR = IDLE_PARAMS;
+ /* Enable Host access wakeup */
+ SET_BIT(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_5), 6);
+
/* UART-rx(console) become to GPIO (NONE INT mode) */
clock_uart2gpio();
+ /* Set deep idle - instant wake-up mode */
+ NPCX_PMCSR = IDLE_PARAMS;
/* Get current counter value of event timer */
evt_count = __hw_clock_event_count();
@@ -289,22 +295,40 @@ void __idle(void)
/* Get time delay cause of deep idle */
next_evt_us = __hw_clock_get_sleep_time(evt_count);
+ /*
+ * Clear PMCSR manually in case there's wake-up between
+ * setting it and wfi.
+ */
+ NPCX_PMCSR = 0;
/* GPIO back to UART-rx (console) */
clock_gpio2uart();
- /* Fast forward timer according to wake-up timer. */
- t1.val = t0.val + next_evt_us;
/* Record time spent in deep sleep. */
idle_dsleep_time_us += next_evt_us;
- force_time(t1);
+
+ /* Fast forward timer according to wake-up timer. */
+ t1.val = t0.val + next_evt_us;
+ /* Leave overflow situation for ITIM32 */
+ if (t1.le.hi == t0.le.hi)
+ force_time(t1);
} else {
#if DEBUG_CLK
/* Use GPIO to indicate NORMAL mode */
SET_BIT(NPCX_PDOUT(0), 0);
#endif
idle_sleep_cnt++;
- /* normal idle : wait for interrupt */
- asm("wfi");
+ /*
+ * Normal idle : wait for interrupt
+ * TODO (ML): Workaround method for wfi issue.
+ * Please see task.c for more detail
+ */
+ asm ("push {r0-r5}\n"
+ "ldr r0, =0x100A8000\n"
+ "wfi\n"
+ "ldm r0, {r0-r5}\n"
+ "pop {r0-r5}\n"
+ "isb\n"
+ );
}
/*
diff --git a/chip/npcx/fan.c b/chip/npcx/fan.c
index 07e4312d9a..9836ff60bd 100644
--- a/chip/npcx/fan.c
+++ b/chip/npcx/fan.c
@@ -19,6 +19,7 @@
#include "timer.h"
#include "task.h"
#include "hooks.h"
+#include "system.h"
#include "math_util.h"
#if !(DEBUG_FAN)
@@ -324,11 +325,19 @@ void fan_set_duty(int ch, int percent)
{
int pwm_id = mft_channels[ch].pwm_id;
- if (!percent)
+ /* duty is zero */
+ if (!percent) {
fan_status[ch].auto_status = FAN_STATUS_STOPPED;
+ enable_sleep(SLEEP_MASK_FAN);
+ } else
+ disable_sleep(SLEEP_MASK_FAN);
+
/* Set the duty cycle of PWM */
pwm_set_duty(pwm_id, percent);
}
+/* ensure that only one fan used since ec enables sleep bit if duty is zero */
+BUILD_ASSERT(CONFIG_FANS <= 1);
+
/**
* Get fan duty cycle.
diff --git a/chip/npcx/gpio.c b/chip/npcx/gpio.c
index 5574e1b0e0..a2178fcc35 100644
--- a/chip/npcx/gpio.c
+++ b/chip/npcx/gpio.c
@@ -656,9 +656,17 @@ void _irq_func(void) \
void __gpio_wk0efgh_interrupt(void)
{
#ifdef CONFIG_LPC
- if (IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_0 , MIWU_GROUP_5),7))
- lpc_lreset_pltrst_handler();
- else
+ /* Pending bit 7 or 6 ? */
+ if (NPCX_WKPND(MIWU_TABLE_0 , MIWU_GROUP_5) & 0xC0) {
+ if (IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_0 , MIWU_GROUP_5), 6)) {
+ /* Clear pending bit of WUI */
+ SET_BIT(NPCX_WKPCL(MIWU_TABLE_0 , MIWU_GROUP_5), 6);
+ /* Disable host wake-up */
+ CLEAR_BIT(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_5), 6);
+ }
+ if (IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_0 , MIWU_GROUP_5), 7))
+ lpc_lreset_pltrst_handler();
+ } else
#endif
gpio_interrupt(NPCX_IRQ_WKINTEFGH_0);
}
diff --git a/chip/npcx/hwtimer.c b/chip/npcx/hwtimer.c
index 88809c01a0..5a7fa8b38c 100644
--- a/chip/npcx/hwtimer.c
+++ b/chip/npcx/hwtimer.c
@@ -13,11 +13,14 @@
#include "hwtimer_chip.h"
#include "math_util.h"
#include "registers.h"
+#include "console.h"
#include "task.h"
#include "timer.h"
/* Use ITIM32 as main hardware timer */
#define TICK_ITIM32_MAX_CNT 0xFFFFFFFF
+/* Maximum deadline of event */
+#define EVT_MAX_EXPIRED_US 0xFFFFFFFF
/* Depth of event timer */
#define TICK_EVT_DEPTH 16 /* Depth of event timer Unit: bits */
@@ -105,21 +108,24 @@ void __hw_clock_event_set(uint32_t deadline)
/* Returns the time-stamp of the next programmed event */
uint32_t __hw_clock_event_get(void)
{
- return evt_expired_us;
+ if (evt_expired_us)
+ return evt_expired_us;
+ else /* No events. Give maximum deadline */
+ return EVT_MAX_EXPIRED_US;
}
/* Get current counter value of event timer */
-uint32_t __hw_clock_event_count(void)
+uint16_t __hw_clock_event_count(void)
{
return NPCX_ITCNT16(ITIM_EVENT_NO);
}
/* Returns time delay cause of deep idle */
-uint32_t __hw_clock_get_sleep_time(uint32_t pre_evt_cnt)
+uint32_t __hw_clock_get_sleep_time(uint16_t pre_evt_cnt)
{
fp_t evt_tick = FLOAT_TO_FP(SECOND/(float)INT_32K_CLOCK);
uint32_t sleep_time;
- uint32_t cnt = NPCX_ITCNT16(ITIM_EVENT_NO);
+ uint16_t cnt = NPCX_ITCNT16(ITIM_EVENT_NO);
/* Event has been triggered but timer ISR dosen't handle it */
if (IS_BIT_SET(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_TO_STS))
@@ -149,21 +155,33 @@ void __hw_clock_event_clear(void)
/* Irq for hwtimer event */
void __hw_clock_event_irq(void)
{
- /* Clear timeout status for event */
- SET_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_TO_STS);
-
/* ITIM event module disable */
CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_ITEN);
/* Disable interrupt of event */
task_disable_irq(ITIM16_INT(ITIM_EVENT_NO));
+ /* Clear timeout status for event */
+ SET_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_TO_STS);
+
/* Clear event parameters */
evt_expired_us = 0;
evt_cnt = 0;
/* handle upper driver */
process_timers(0);
+
+#ifdef CONFIG_LOW_POWER_IDLE
+ /*
+ * Set event for ITIM32 after process_timers() since no events set if
+ * event's deadline is over 32 bits but current source clock isn't.
+ * ITIM32 is based on apb2 and ec won't wake-up in deep-idle even if it
+ * expires.
+ */
+ if (evt_expired_us == 0)
+ __hw_clock_event_set(EVT_MAX_EXPIRED_US);
+#endif
+
}
DECLARE_IRQ(ITIM16_INT(ITIM_EVENT_NO) , __hw_clock_event_irq, 1);
@@ -171,6 +189,23 @@ DECLARE_IRQ(ITIM16_INT(ITIM_EVENT_NO) , __hw_clock_event_irq, 1);
/*****************************************************************************/
/* HWTimer tick handlers */
+/* Modify preload counter of source clock. */
+void hw_clock_source_set_preload(uint32_t ts, uint8_t clear)
+{
+ /* ITIM32 module disable */
+ CLEAR_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_ITEN);
+
+ CLEAR_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_CKSEL);
+
+ /* Set preload counter to current time */
+ NPCX_ITCNT32 = TICK_ITIM32_MAX_CNT - ts;
+ /* Clear timeout status or not */
+ if (clear)
+ SET_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_TO_STS);
+ /* ITIM32 module enable */
+ SET_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_ITEN);
+}
+
/* Returns the value of the free-running counter used as clock. */
uint32_t __hw_clock_source_read(void)
{
@@ -187,13 +222,7 @@ void __hw_clock_source_set(uint32_t ts)
#if DEBUG_TMR
cur_cnt_us_dbg = TICK_ITIM32_MAX_CNT - ts;
#endif
- /* ITIM32 module disable */
- CLEAR_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_ITEN);
- /* Set current time */
- NPCX_ITCNT32 = TICK_ITIM32_MAX_CNT - ts;
- /* ITIM32 module enable */
- SET_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_ITEN);
-
+ hw_clock_source_set_preload(ts, 0);
}
/* Irq for hwtimer tick */
@@ -201,16 +230,21 @@ void __hw_clock_source_irq(void)
{
/* Is timeout trigger trigger? */
if (IS_BIT_SET(NPCX_ITCTS(ITIM32), NPCX_ITCTS_TO_STS)) {
- /* Clear timeout status*/
- SET_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_TO_STS);
+ /* Restore ITIM32 preload counter value to maximum value */
+ hw_clock_source_set_preload(0, 1);
/* 32-bits timer count overflow */
process_timers(1);
} else { /* Handle soft trigger */
process_timers(0);
+#ifdef CONFIG_LOW_POWER_IDLE
+ /* Set event for ITIM32. Please see above for detail */
+ if (evt_expired_us == 0)
+ __hw_clock_event_set(EVT_MAX_EXPIRED_US);
+#endif
}
}
-DECLARE_IRQ(NPCX_IRQ_ITIM32, __hw_clock_source_irq, 1);
+DECLARE_IRQ(NPCX_IRQ_ITIM32, __hw_clock_source_irq, 0);
static void update_prescaler(void)
{
@@ -244,17 +278,11 @@ int __hw_clock_source_init(uint32_t start_t)
/* Set initial prescaler */
update_prescaler();
- /* ITIM count down : TICK_ITIM32_MAX_CNT us expired */
- NPCX_ITCNT32 = TICK_ITIM32_MAX_CNT;
-
/*
* Override the count with the start value now that counting has
* started.
*/
- __hw_clock_source_set(start_t);
-
- /* ITIM module enable */
- SET_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_ITEN);
+ hw_clock_source_set_preload(start_t, 1);
/* Enable interrupt of ITIM */
task_enable_irq(NPCX_IRQ_ITIM32);
diff --git a/chip/npcx/hwtimer_chip.h b/chip/npcx/hwtimer_chip.h
index ac657a2a4b..52b17c7dee 100644
--- a/chip/npcx/hwtimer_chip.h
+++ b/chip/npcx/hwtimer_chip.h
@@ -22,9 +22,9 @@ enum ITIM_SOURCE_CLOCK_T {
void init_hw_timer(int itim_no, enum ITIM_SOURCE_CLOCK_T source);
/* Returns the counter value of event timer */
-uint32_t __hw_clock_event_count(void);
+uint16_t __hw_clock_event_count(void);
/* Returns time delay because of deep idle */
-uint32_t __hw_clock_get_sleep_time(uint32_t pre_evt_cnt);
+uint32_t __hw_clock_get_sleep_time(uint16_t pre_evt_cnt);
#endif /* __CROS_EC_HWTIMER_CHIP_H */
diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h
index abb8936c04..d4d75b4e68 100644
--- a/chip/npcx/registers.h
+++ b/chip/npcx/registers.h
@@ -1353,6 +1353,16 @@ static inline int uart_is_enable_wakeup(void)
#endif
}
+/* This routine clears the pending wake-up from GPIO on UART rx pin */
+static inline void uart_clear_pending_wakeup(void)
+{
+#if NPCX_UART_MODULE2
+ SET_BIT(NPCX_WKPCL(1, 6), 4);
+#else
+ SET_BIT(NPCX_WKPCL(1, 1), 0);
+#endif
+}
+
/* This routine enables wake-up functionality from GPIO on UART rx pin */
static inline void uart_enable_wakeup(int enable)
{
diff --git a/chip/npcx/uart.c b/chip/npcx/uart.c
index 5b47110835..b9f06f42dc 100644
--- a/chip/npcx/uart.c
+++ b/chip/npcx/uart.c
@@ -66,6 +66,9 @@ void uart_tx_flush(void)
/* Wait for transmit FIFO empty */
while (!(NPCX_UICTRL & 0x01))
;
+ /* Wait for transmitting completed */
+ while (NPCX_USTAT & 0x40)
+ ;
}
int uart_tx_ready(void)
@@ -101,9 +104,6 @@ void uart_write_char(char c)
;
NPCX_UTBUF = c;
-#ifdef CONFIG_LOW_POWER_IDLE
- clock_refresh_console_in_use();
-#endif
}
int uart_read_char(void)
diff --git a/core/cortex-m/task.c b/core/cortex-m/task.c
index a4417f4fff..91b9e62caa 100644
--- a/core/cortex-m/task.c
+++ b/core/cortex-m/task.c
@@ -80,6 +80,7 @@ void __idle(void)
"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 */
"cpsie i\n" /* Enable interrupts */
);
#else
diff --git a/include/system.h b/include/system.h
index 9ae0da1bf0..d3c720a12c 100644
--- a/include/system.h
+++ b/include/system.h
@@ -309,6 +309,7 @@ enum {
SLEEP_MASK_USB_PD = (1 << 5), /* USB PD device connected */
SLEEP_MASK_SPI = (1 << 6), /* SPI communications ongoing */
SLEEP_MASK_I2C_SLAVE = (1 << 7), /* I2C slave communication ongoing */
+ SLEEP_MASK_FAN = (1 << 8), /* Fan control loop ongoing */
SLEEP_MASK_FORCE_NO_DSLEEP = (1 << 15), /* Force disable. */