summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDino Li <dino.li@ite.com.tw>2015-10-01 11:46:00 +0800
committerchrome-bot <chrome-bot@chromium.org>2015-10-01 01:55:26 -0700
commit692428e1a6676efa7e952b747aac1cee6c503922 (patch)
treebb4423190b979695733fa41c681945fa1c489fd9
parent04bb47c32a22bc6b01dc1d03a423a945a74b1c10 (diff)
downloadchrome-ec-692428e1a6676efa7e952b747aac1cee6c503922.tar.gz
it8380dev: fix hw timer and related function.
[chip config] 1. No hardware specific udelay(). 2. Enable watchdog. [watchdog] 3. Watchdog period is "CONFIG_WATCHDOG_PERIOD_MS" of config.h. 4. Watchdog auxiliary timer period is "CONFIG_AUX_TIMER_PERIOD_MS". [task and irq] 5. Write 1 to clear interrupt pending status, no |. 6. A global variable for store interrupt number of software interrupt. [uart] 7. Always reset UART module before config it. [hwtimer] 8. Use more external timers for HW timer module. [task] 9. Fix task profiling. Signed-off-by: Dino Li <dino.li@ite.com.tw> BRANCH=none BUG=none TEST=[watchdog] 1. console "waitms 1100", only pre-watchdog warning message. 2. console "waitms 1600", warning message and watchdog reset. [hwtimer] 3. console commands "gettime", "timerinfo", and "forcetime". 4. enable hook debug and there is no delayed by more than 10% warning message over 48 hours. 5. There is no watchdog reset too. [task] 6. console 'taskinfo' Task Ready Name Events Time (s) StkUsed 0 R << idle >> 00000000 32.927724 308/512 1 HOOKS 00000000 0.034267 372/768 2 R CONSOLE 00000000 0.116763 468/768 3 HOSTCMD 00000000 0.000641 372/512 4 KEYPROTO 00000000 0.000042 212/512 5 KEYSCAN 00000000 0.000908 356/512 IRQ counts by type: 38 2932 155 1 158 261 160 67 Service calls: 87 Total exceptions: 3348 Task switches: 167 Task switching started: 0.001999 s Time in tasks: 33.282819 s Time in exceptions: 0.164717 s Change-Id: I234085cec231cd855d2a5e639ea1b0966c61d796 Reviewed-on: https://chromium-review.googlesource.com/296939 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.h2
-rw-r--r--chip/it83xx/config_chip.h5
-rw-r--r--chip/it83xx/fan.c2
-rw-r--r--chip/it83xx/hwtimer.c388
-rw-r--r--chip/it83xx/hwtimer_chip.h24
-rw-r--r--chip/it83xx/irq.c3
-rw-r--r--chip/it83xx/registers.h13
-rw-r--r--chip/it83xx/uart.c3
-rw-r--r--chip/it83xx/watchdog.c40
-rw-r--r--core/nds32/init.S4
-rw-r--r--core/nds32/irq_chip.h5
-rw-r--r--core/nds32/switch.S2
-rw-r--r--core/nds32/task.c132
13 files changed, 427 insertions, 196 deletions
diff --git a/board/it8380dev/board.h b/board/it8380dev/board.h
index 7eaa27a9aa..1f7c55c469 100644
--- a/board/it8380dev/board.h
+++ b/board/it8380dev/board.h
@@ -29,6 +29,8 @@
#define CONFIG_CMD_STACKOVERFLOW
/* Debug */
+#undef CONFIG_CMD_FORCETIME
+#undef CONFIG_HOOK_DEBUG
#undef CONFIG_KEYBOARD_DEBUG
#undef CONFIG_UART_TX_BUF_SIZE
#define CONFIG_UART_TX_BUF_SIZE 4096
diff --git a/chip/it83xx/config_chip.h b/chip/it83xx/config_chip.h
index b05fc1d338..1e2d5c747a 100644
--- a/chip/it83xx/config_chip.h
+++ b/chip/it83xx/config_chip.h
@@ -10,7 +10,7 @@
#include "core/nds32/config_core.h"
/* Number of IRQ vectors on the IVIC */
-#define CONFIG_IRQ_COUNT 16
+#define CONFIG_IRQ_COUNT IT83XX_IRQ_COUNT
/* Interval between HOOK_TICK notifications */
#define HOOK_TICK_INTERVAL_MS 500
@@ -88,8 +88,6 @@
/****************************************************************************/
/* Customize the build */
-/* Use hardware specific udelay() for this chip */
-#define CONFIG_HW_SPECIFIC_UDELAY
#define CONFIG_FW_RESET_VECTOR
/* Optional features present on this chip */
@@ -100,7 +98,6 @@
#define CONFIG_PECI
#define CONFIG_PWM
#define CONFIG_SPI
-#undef CONFIG_WATCHDOG
#define GPIO_PIN(port, index) GPIO_##port, (1 << index)
#define GPIO_PIN_MASK(port, mask) GPIO_##port, (mask)
diff --git a/chip/it83xx/fan.c b/chip/it83xx/fan.c
index 4e0e7ab6bd..8f11b29e23 100644
--- a/chip/it83xx/fan.c
+++ b/chip/it83xx/fan.c
@@ -470,6 +470,6 @@ static void fan_init(void)
/* init external timer for fan control */
ext_timer_ms(FAN_CTRL_EXT_TIMER, EXT_PSR_32P768K_HZ, 0, 0,
- FAN_CTRL_BASED_MS, 1);
+ FAN_CTRL_BASED_MS, 1, 0);
}
DECLARE_HOOK(HOOK_INIT, fan_init, HOOK_PRIO_INIT_PWM);
diff --git a/chip/it83xx/hwtimer.c b/chip/it83xx/hwtimer.c
index 998125a5a2..77354702b4 100644
--- a/chip/it83xx/hwtimer.c
+++ b/chip/it83xx/hwtimer.c
@@ -9,36 +9,72 @@
#include "common.h"
#include "hooks.h"
#include "hwtimer.h"
+#include "hwtimer_chip.h"
+#include "irq_chip.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "watchdog.h"
-#include "hwtimer_chip.h"
-/* 128us (2^7 us) between 2 ticks */
-#define TICK_INTERVAL_LOG2 7
+/*
+ * The IT839X series support combinational mode for combining specific pairs of
+ * timers: 3(24-bit) and 4(32-bit) / timer 5(24-bit) and 6(32-bit) /
+ * timer 7(24-bit) and 8(32-bit).
+ * That means we will have a 56-bit timer if timer 3(TIMER_L) and
+ * timer 4(TIMER_H) is combined (bit3 @ IT83XX_ETWD_ETXCTRL).
+ * For a 32-bit MHz free-running counter, we select 8MHz clock source
+ * for timer 3(TIMER_L) and 4(TIMER_H).
+ * Counter setting value register(IT83XX_ETWD_ETXCNTLR) and counter observation
+ * value register(IT83XX_ETWD_ETXCNTOR) of timer 3 and 4 need to be shifted by
+ * 3 (2^3). So that each count will be equal to 0.125us.
+ *
+ * For example, if
+ * __hw_clock_source_set() set 0 us, the counter setting register are
+ * timer 3(TIMER_L) ((0xffffffff << 3) & 0xffffff) = 0xfffff8
+ * timer 4(TIMER_H) (0xffffffff >> (24-3)) = 0x000007ff
+ * The 56-bit 8MHz timer = 0x000007fffffff8
+ * 0x000007fffffff8 / (2^3) = 0xffffffff(32-bit MHz free-running counter)
+ *
+ * Note:
+ * In combinational mode, the counter observation value of
+ * timer 4(TIMER_H), 6, 8 will in incrementing order.
+ * For the above example, the counter observation value registers will be
+ * timer 3(TIMER_L) 0xfffff8
+ * timer 4(TIMER_H) ~0x000007ff = 0xfffff800
+ *
+ * The following will describe timer 3 and 4's operation in combinational mode:
+ * 1. When timer 3(TIMER_L) observation value counting down to 0,
+ timer 4(TIMER_H) observation value++.
+ * 2. Timer 3(TIMER_L) observation value = counter setting register.
+ * 3. Timer 3(TIMER_L) interrupt occurs if interrupt is enabled.
+ * 4. When timer 4(TIMER_H) observation value overflows.
+ * 5. Timer 4(TIMER_H) observation value = ~counter setting register.
+ * 6. Timer 4(TIMER_H) interrupt occurs.
+ *
+ * IT839X only supports terminal count interrupt. We need a separate
+ * 8 MHz 32-bit timer to handle events.
+ */
-#define TICK_INTERVAL (1 << TICK_INTERVAL_LOG2)
-#define TICK_INTERVAL_MASK (TICK_INTERVAL - 1)
+#define TIMER_COUNT_1US_SHIFT 3
-#define MS_TO_COUNT(hz, ms) ((hz) * (ms) / 1000)
+/* 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)
-/*
- * Tick interval must fit in one byte, and must be greater than two
- * so that the duty cycle does not equal the cycle time (IT83XX_TMR_DCR_B0 must
- * be less than IT83XX_TMR_CTR_B0).
- */
-BUILD_ASSERT(TICK_INTERVAL < 256 && TICK_INTERVAL > 2);
+/* 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)
-static volatile uint32_t time_us;
+/* 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)
-/*
- * Next event time of 0 represents "no event set". But, when we actually want
- * to trigger when the event time is 0, it is handled implicitly by calling
- * process_timers(1) when the timer value rolls over.
- */
-static uint32_t next_event_time;
+#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,
@@ -56,44 +92,154 @@ const struct ext_timer_ctrl_t et_ctrl_regs[] = {
};
BUILD_ASSERT(ARRAY_SIZE(et_ctrl_regs) == EXT_TIMER_COUNT);
-void __hw_clock_event_set(uint32_t deadline)
+static void free_run_timer_config_counter(uint32_t us)
{
- next_event_time = deadline;
+ /*
+ * 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);
}
-uint32_t __hw_clock_event_get(void)
+static void free_run_timer_clear_pending_isr(void)
{
- return next_event_time;
+ /* w/c interrupt status */
+ task_clear_pending_irq(et_ctrl_regs[FREE_EXT_TIMER_L].irq);
+ task_clear_pending_irq(et_ctrl_regs[FREE_EXT_TIMER_H].irq);
}
-void __hw_clock_event_clear(void)
+static void free_run_timer_overflow(void)
+{
+ /*
+ * If timer counter 4(TIMER_H) + timer counter 3(TIMER_L)
+ * != 0x000007fffffff8.
+ * This usually happens once after sysjump, force time, and etc.
+ * (when __hw_clock_source_set is called and param 'ts' != 0)
+ */
+ if ((IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_H) != TIMER_H_CNT_COMP) ||
+ (IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_L) != TIMER_L_CNT_COMP))
+ free_run_timer_config_counter(0xffffffff);
+
+ /* w/c interrupt status */
+ free_run_timer_clear_pending_isr();
+ /* timer overflow */
+ process_timers(1);
+ update_exc_start_time();
+}
+
+static void event_timer_clear_pending_isr(void)
{
- next_event_time = 0;
+ /* w/c interrupt status */
+ task_clear_pending_irq(et_ctrl_regs[EVENT_EXT_TIMER].irq);
}
uint32_t __hw_clock_source_read(void)
{
- return time_us;
+ uint32_t l_cnt, h_cnt;
+
+ /*
+ * get timer counter observation value, timer 3(TIMER_L) and 4(TIMER_H)
+ * combinational mode.
+ */
+ h_cnt = IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_H);
+ l_cnt = IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_L);
+ /* timer 3(TIMER_L) overflow, get counter observation value again */
+ if (h_cnt != IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_H)) {
+ h_cnt = IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_H);
+ l_cnt = IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_L);
+ }
+
+ /* timer counter observation value to microseconds */
+ return 0xffffffff - (TIMER_L_COUNT_TO_US(l_cnt) |
+ TIMER_H_COUNT_TO_US(h_cnt));
}
void __hw_clock_source_set(uint32_t ts)
{
- time_us = ts & TICK_INTERVAL_MASK;
+ uint32_t start_us;
+
+ /* counting down timer */
+ start_us = 0xffffffff - ts;
+
+ /* timer 3(TIMER_L) and timer 4(TIMER_H) are not enabled */
+ if ((IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) & 0x09) != 0x09) {
+ /* bit3, timer 3 and timer 4 combinational mode */
+ IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) |= (1 << 3);
+ /* microseconds to timer counter, clock source is 8mhz */
+ ext_timer_ms(FREE_EXT_TIMER_H, EXT_PSR_8M_HZ, 0, 1,
+ TIMER_H_US_TO_COUNT(start_us), 1, 1);
+ 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);
+ task_enable_irq(et_ctrl_regs[FREE_EXT_TIMER_H].irq);
+ task_enable_irq(et_ctrl_regs[FREE_EXT_TIMER_L].irq);
+ }
+}
+
+void __hw_clock_event_set(uint32_t deadline)
+{
+ uint32_t wait;
+ /* bit0, disable event timer */
+ IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) &= ~(1 << 0);
+ /* w/c interrupt status */
+ event_timer_clear_pending_isr();
+ /* microseconds to timer counter */
+ wait = deadline - __hw_clock_source_read();
+ IT83XX_ETWD_ETXCNTLR(EVENT_EXT_TIMER) =
+ wait < EVENT_TIMER_COUNT_TO_US(0xffffffff) ?
+ EVENT_TIMER_US_TO_COUNT(wait) : 0xffffffff;
+ /* enable and re-start timer */
+ IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) |= 0x03;
+ task_enable_irq(et_ctrl_regs[EVENT_EXT_TIMER].irq);
}
+uint32_t __hw_clock_event_get(void)
+{
+ uint32_t next_event_us = __hw_clock_source_read();
+
+ /* bit0, event timer is enabled */
+ if (IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) & (1 << 0)) {
+ /* timer counter observation value to microseconds */
+ next_event_us += EVENT_TIMER_COUNT_TO_US(
+ IT83XX_ETWD_ETXCNTOR(EVENT_EXT_TIMER));
+ }
+ return next_event_us;
+}
+
+void __hw_clock_event_clear(void)
+{
+ /* stop event timer */
+ ext_timer_stop(EVENT_EXT_TIMER, 1);
+ event_timer_clear_pending_isr();
+}
+
+int __hw_clock_source_init(uint32_t start_t)
+{
+ /* enable free running timer */
+ __hw_clock_source_set(start_t);
+ /* init event timer */
+ ext_timer_ms(EVENT_EXT_TIMER, EXT_PSR_8M_HZ, 0, 0, 0xffffffff, 1, 1);
+ /* returns the IRQ number of event timer */
+ return et_ctrl_regs[EVENT_EXT_TIMER].irq;
+}
static void __hw_clock_source_irq(void)
{
-#if defined(CONFIG_WATCHDOG) || defined(CONFIG_FANS)
/* Determine interrupt number. */
int irq = IT83XX_INTC_IVCT3 - 16;
-#endif
- /*
- * If this is a SW interrupt, then process the timers, but don't
- * increment the time_us.
- */
- if (get_itype() & 8) {
+ /* SW/HW interrupt of event timer. */
+ if ((get_sw_int() == et_ctrl_regs[EVENT_EXT_TIMER].irq) ||
+ (irq == et_ctrl_regs[EVENT_EXT_TIMER].irq)) {
+ IT83XX_ETWD_ETXCNTLR(EVENT_EXT_TIMER) = 0xffffffff;
+ IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) |= (1 << 1);
+ event_timer_clear_pending_isr();
process_timers(0);
return;
}
@@ -104,7 +250,7 @@ static void __hw_clock_source_irq(void)
* go through this irq. So, if this interrupt was caused by watchdog
* warning timer, then call that function.
*/
- if (irq == IT83XX_IRQ_EXT_TIMER3) {
+ if (irq == et_ctrl_regs[WDT_EXT_TIMER].irq) {
watchdog_warning_irq();
return;
}
@@ -117,109 +263,48 @@ static void __hw_clock_source_irq(void)
}
#endif
- /*
- * If we're still here, this is actually a hardware interrupt for the
- * clock source. Clear its interrupt status and update time_us.
- */
- task_clear_pending_irq(IT83XX_IRQ_TMR_B0);
-
- time_us += TICK_INTERVAL;
-
- /*
- * Find expired timers and set the new timer deadline; check the IRQ
- * status to determine if the free-running counter overflowed. Note
- * since each tick is greater than 1us and events can be set in
- * increments of 1us, in order to find expired timers we have to
- * check two conditions: the current time is exactly the next event
- * time, or this tick just caused us to pass the next event time.
- */
- if (time_us == 0)
- process_timers(1);
- else if (time_us == next_event_time ||
- (time_us-TICK_INTERVAL) ==
- (next_event_time & ~TICK_INTERVAL_MASK))
- process_timers(0);
-}
-DECLARE_IRQ(IT83XX_IRQ_TMR_B0, __hw_clock_source_irq, 1);
-
-static void setup_gpio(void)
-{
- /* TMB0 enabled */
- IT83XX_GPIO_GRC2 |= 0x04;
-
- /* Pin muxing (TMB0) */
- IT83XX_GPIO_GPCRF0 = 0x00;
-}
-
-static void hw_timer_enable_int(void)
-{
- /* clear interrupt status */
- task_clear_pending_irq(IT83XX_IRQ_TMR_B0);
-
- /* enable interrupt B0 */
- task_enable_irq(IT83XX_IRQ_TMR_B0);
-}
-
-int __hw_clock_source_init(uint32_t start_t)
-{
- __hw_clock_source_set(start_t);
-
- /* GPIO module should do this. */
- setup_gpio();
-
-#if PLL_CLOCK == 48000000
- /* Set prescaler divider value (PRSC0 = /8). */
- IT83XX_TMR_PRSC = 0x04;
-
- /* Tim B: 8 bit pulse mode, 8MHz clock. */
- IT83XX_TMR_GCSMS = 0x01;
-#else
-#error "Support only for PLL clock speed of 48MHz."
-#endif
-
- /* Set timer B to use PRSC0. */
- IT83XX_TMR_CCGSR = 0x00;
-
- /*
- * Set the 8-bit cycle time, duty time for timer B. Note 0 < DCR < CTR
- * in order for timer interrupt to properly fire when cycle time is
- * reached.
- */
- IT83XX_TMR_CTR_B0 = TICK_INTERVAL - 1;
- IT83XX_TMR_DCR_B0 = 0x01;
-
- /* Enable the cycle time interrupt for timer B0. */
- IT83XX_TMR_TMRIE |= 0x10;
-
- hw_timer_enable_int();
-
- /* Enable TMR clock counter. */
- IT83XX_TMR_TMRCE |= 0x02;
-
- return IT83XX_IRQ_TMR_B0;
-}
+ /* Interrupt of free running timer TIMER_L. */
+ if (irq == et_ctrl_regs[FREE_EXT_TIMER_L].irq) {
+ /* w/c interrupt status */
+ task_clear_pending_irq(et_ctrl_regs[FREE_EXT_TIMER_L].irq);
+ /* disable timer 3(TIMER_L) interrupt */
+ task_disable_irq(et_ctrl_regs[FREE_EXT_TIMER_L].irq);
+ /* No need to set timer counter */
+ if (IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_L) == TIMER_L_CNT_COMP)
+ return;
+ /*
+ * If timer counter 3(TIMER_L) != 0xfffff8.
+ * This usually happens once after sysjump, force time, and etc.
+ * (when __hw_clock_source_set is called and param 'ts' != 0)
+ *
+ * The interrupt is used to make sure the counter of
+ * timer 3(TIMER_L) is
+ * 0xfffff8(TIMER_L_COUNT_TO_US(0xffffffff)).
+ */
+ if (IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_H)) {
+ 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);
+ update_exc_start_time();
+ } else {
+ free_run_timer_overflow();
+ }
+ return;
+ }
-void udelay(unsigned us)
-{
- /*
- * When WNCKR register is set, the CPU pauses until a low to
- * high transition on an internal 65kHz clock (~15.25us). We need to
- * make sure though that we don't ever delay less than the requested
- * amount, so we always have to add an extra wait.
- *
- * TODO: This code has a few limitations, the math isn't exact so
- * the larger the delay the farther off it will be, it uses a divide,
- * and the resolution is only about 15us.
- */
- int waits = us*4/61 + 1;
- while (waits-- >= 0)
- IT83XX_GCTRL_WNCKR = 0;
+ /* Interrupt of free running timer TIMER_H. */
+ if (irq == et_ctrl_regs[FREE_EXT_TIMER_H].irq) {
+ free_run_timer_overflow();
+ return;
+ }
}
+DECLARE_IRQ(CPU_INT_GROUP_3, __hw_clock_source_irq, 1);
void ext_timer_start(enum ext_timer_sel ext_timer, int en_irq)
{
/* enable external timer n */
- IT83XX_ETWD_ETXCTRL(ext_timer) |= 0x01;
+ IT83XX_ETWD_ETXCTRL(ext_timer) |= 0x03;
if (en_irq) {
task_clear_pending_irq(et_ctrl_regs[ext_timer].irq);
@@ -256,9 +341,7 @@ static void ext_timer_ctrl(enum ext_timer_sel ext_timer,
IT83XX_ETWD_ETXPSR(ext_timer) = ext_timer_clock;
/* The count number of external timer n. */
- IT83XX_ETWD_ETXCNTLH2R(ext_timer) = (count >> 16) & 0xFF;
- IT83XX_ETWD_ETXCNTLHR(ext_timer) = (count >> 8) & 0xFF;
- IT83XX_ETWD_ETXCNTLLR(ext_timer) = count & 0xFF;
+ IT83XX_ETWD_ETXCNTLR(ext_timer) = count;
ext_timer_stop(ext_timer, 0);
if (start)
@@ -275,28 +358,25 @@ int ext_timer_ms(enum ext_timer_sel ext_timer,
int start,
int with_int,
int32_t ms,
- int first_time_enable)
+ int first_time_enable,
+ int raw)
{
uint32_t count;
- if (ext_timer_clock == EXT_PSR_32P768K_HZ)
- count = MS_TO_COUNT(32768, ms);
- else if (ext_timer_clock == EXT_PSR_1P024K_HZ)
- count = MS_TO_COUNT(1024, ms);
- else if (ext_timer_clock == EXT_PSR_32_HZ)
- count = MS_TO_COUNT(32, ms);
- else if (ext_timer_clock == EXT_PSR_8M_HZ)
- count = 8000 * ms;
- else
- return -1;
-
- /*
- * IT838X support 24-bits external timer only,
- * IT839X support three(4, 6, and 8) 32-bit external timers,
- * implemented later.
- */
- if (count >> 24)
- return -2;
+ if (raw) {
+ count = ms;
+ } else {
+ if (ext_timer_clock == EXT_PSR_32P768K_HZ)
+ count = MS_TO_COUNT(32768, ms);
+ else if (ext_timer_clock == EXT_PSR_1P024K_HZ)
+ count = MS_TO_COUNT(1024, ms);
+ else if (ext_timer_clock == EXT_PSR_32_HZ)
+ count = MS_TO_COUNT(32, ms);
+ else if (ext_timer_clock == EXT_PSR_8M_HZ)
+ count = 8000 * ms;
+ else
+ return -1;
+ }
if (count == 0)
return -3;
diff --git a/chip/it83xx/hwtimer_chip.h b/chip/it83xx/hwtimer_chip.h
index 9217545c5b..690505dcb1 100644
--- a/chip/it83xx/hwtimer_chip.h
+++ b/chip/it83xx/hwtimer_chip.h
@@ -8,7 +8,11 @@
#ifndef __CROS_EC_HWTIMER_CHIP_H
#define __CROS_EC_HWTIMER_CHIP_H
+#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
enum ext_timer_clock_source {
EXT_PSR_32P768K_HZ = 0,
@@ -17,16 +21,19 @@ enum ext_timer_clock_source {
EXT_PSR_8M_HZ = 3
};
+/*
+ * 24-bit timers: external timer 3, 5, and 7
+ * 32-bit timers: external timer 4, 6, and 8
+ */
enum ext_timer_sel {
- /* For WDT capture important state information before being reset */
+ /* timer 3 and 4 combine mode for free running timer */
EXT_TIMER_3 = 0,
- /* reserved */
EXT_TIMER_4,
/* For fan control */
EXT_TIMER_5,
- /* reserved */
+ /* timer 6 for event timer */
EXT_TIMER_6,
- /* reserved */
+ /* For WDT capture important state information before being reset */
EXT_TIMER_7,
/* reserved */
EXT_TIMER_8,
@@ -44,11 +51,18 @@ extern const struct ext_timer_ctrl_t et_ctrl_regs[];
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.
+ *
+ * @param raw (!=0) timer count equal to param "ms" no conversion.
+ */
int ext_timer_ms(enum ext_timer_sel ext_timer,
enum ext_timer_clock_source ext_timer_clock,
int start,
int et_int,
int32_t ms,
- int first_time_enable);
+ int first_time_enable,
+ int raw);
#endif /* __CROS_EC_HWTIMER_CHIP_H */
diff --git a/chip/it83xx/irq.c b/chip/it83xx/irq.c
index 1c8de0983e..f980147df4 100644
--- a/chip/it83xx/irq.c
+++ b/chip/it83xx/irq.c
@@ -69,7 +69,8 @@ int chip_clear_pending_irq(int irq)
int group = irq / 8;
int bit = irq % 8;
- IT83XX_INTC_REG(irq_groups[group].isr_off) |= 1 << bit;
+ /* always write 1 clear, no | */
+ IT83XX_INTC_REG(irq_groups[group].isr_off) = 1 << bit;
return -1; /* everything has been done */
}
diff --git a/chip/it83xx/registers.h b/chip/it83xx/registers.h
index fa31c11d83..53e1ed728e 100644
--- a/chip/it83xx/registers.h
+++ b/chip/it83xx/registers.h
@@ -305,6 +305,9 @@
#define CPU_INT_GROUP_12 253
#define IT83XX_CPU_INT_IRQ_253 12
+#define CPU_INT_GROUP_3 251
+#define IT83XX_CPU_INT_IRQ_251 3
+
#define CPU_INT(irq) CONCAT2(IT83XX_CPU_INT_IRQ_, irq)
/* --- INTC --- */
@@ -629,16 +632,10 @@ enum clock_gate_offsets {
#define IT83XX_ETWD_EWDCNTLLR REG8(IT83XX_ETWD_BASE+0x06)
#define IT83XX_ETWD_EWDKEYR REG8(IT83XX_ETWD_BASE+0x07)
#define IT83XX_ETWD_EWDCNTLHR REG8(IT83XX_ETWD_BASE+0x09)
-#define IT83XX_ETWD_ET3CTRL REG8(IT83XX_ETWD_BASE+0x10)
-#define IT83XX_ETWD_ET3PSR REG8(IT83XX_ETWD_BASE+0x11)
-#define IT83XX_ETWD_ET3CNTLLR REG8(IT83XX_ETWD_BASE+0x14)
-#define IT83XX_ETWD_ET3CNTLHR REG8(IT83XX_ETWD_BASE+0x15)
-#define IT83XX_ETWD_ET3CNTLH2R REG8(IT83XX_ETWD_BASE+0x16)
#define IT83XX_ETWD_ETXCTRL(n) REG8(IT83XX_ETWD_BASE + 0x10 + (n << 3))
#define IT83XX_ETWD_ETXPSR(n) REG8(IT83XX_ETWD_BASE + 0x11 + (n << 3))
-#define IT83XX_ETWD_ETXCNTLLR(n) REG8(IT83XX_ETWD_BASE + 0x14 + (n << 3))
-#define IT83XX_ETWD_ETXCNTLHR(n) REG8(IT83XX_ETWD_BASE + 0x15 + (n << 3))
-#define IT83XX_ETWD_ETXCNTLH2R(n) REG8(IT83XX_ETWD_BASE + 0x16 + (n << 3))
+#define IT83XX_ETWD_ETXCNTLR(n) REG32(IT83XX_ETWD_BASE + 0x14 + (n << 3))
+#define IT83XX_ETWD_ETXCNTOR(n) REG32(IT83XX_ETWD_BASE + 0x48 + (n << 2))
/* --- General Control (GCTRL) --- */
#define IT83XX_GCTRL_BASE 0x00F02000
diff --git a/chip/it83xx/uart.c b/chip/it83xx/uart.c
index 550672804a..a396e76316 100644
--- a/chip/it83xx/uart.c
+++ b/chip/it83xx/uart.c
@@ -180,6 +180,9 @@ static void host_uart_config(void)
void uart_init(void)
{
+ /* reset uart before config it */
+ IT83XX_GCTRL_RSTC4 |= (1 << 1);
+
/* Waiting for when we can use the GPIO module to set pin muxing */
gpio_config_module(MODULE_UART, 1);
diff --git a/chip/it83xx/watchdog.c b/chip/it83xx/watchdog.c
index d22a3da538..e6941b1de5 100644
--- a/chip/it83xx/watchdog.c
+++ b/chip/it83xx/watchdog.c
@@ -8,13 +8,14 @@
#include "common.h"
#include "cpu.h"
#include "hooks.h"
+#include "hwtimer_chip.h"
#include "panic.h"
#include "registers.h"
#include "task.h"
#include "watchdog.h"
/*
- * We use timer3 to trigger an interrupt just before the watchdog timer
+ * We use WDT_EXT_TIMER to trigger an interrupt just before the watchdog timer
* will fire so that we can capture important state information before
* being reset.
*/
@@ -25,18 +26,18 @@
void watchdog_warning_irq(void)
{
/* clear interrupt status */
- task_clear_pending_irq(IT83XX_IRQ_EXT_TIMER3);
+ task_clear_pending_irq(et_ctrl_regs[WDT_EXT_TIMER].irq);
- /* Reset warning timer (timer 3). */
- IT83XX_ETWD_ET3CTRL = 0x03;
+ /* Reset warning timer. */
+ IT83XX_ETWD_ETXCTRL(WDT_EXT_TIMER) = 0x03;
panic_printf("Pre-watchdog warning! IPC: %08x\n", get_ipc());
}
void watchdog_reload(void)
{
- /* Reset warning timer (timer 3). */
- IT83XX_ETWD_ET3CTRL = 0x03;
+ /* Reset warning timer. */
+ IT83XX_ETWD_ETXCTRL(WDT_EXT_TIMER) = 0x03;
/* Restart (tickle) watchdog timer. */
IT83XX_ETWD_EWDKEYR = ITE83XX_WATCHDOG_MAGIC_WORD;
@@ -45,11 +46,12 @@ DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT);
int watchdog_init(void)
{
+ uint16_t wdt_count = CONFIG_WATCHDOG_PERIOD_MS * 1024 / 1000;
+
/* Unlock access to watchdog registers. */
IT83XX_ETWD_ETWCFG = 0x00;
- /* Set timer 3 and WD timer to use 1.024kHz clock. */
- IT83XX_ETWD_ET3PSR = 0x01;
+ /* Set WD timer to use 1.024kHz clock. */
IT83XX_ETWD_ET1PSR = 0x01;
/* Set WDT key match enabled and WDT clock to use ET1PSR. */
@@ -58,23 +60,19 @@ int watchdog_init(void)
/* Specify that watchdog cannot be stopped. */
IT83XX_ETWD_ETWCTRL = 0x00;
- /* Set timer 3 load value to 1024 (~1.05 seconds). */
- IT83XX_ETWD_ET3CNTLH2R = 0x00;
- IT83XX_ETWD_ET3CNTLHR = 0x04;
- IT83XX_ETWD_ET3CNTLLR = 0x00;
-
- /* Enable interrupt on timer 3 expiration. */
- task_enable_irq(IT83XX_IRQ_EXT_TIMER3);
-
- /* Start timer 3. */
- IT83XX_ETWD_ET3CTRL = 0x03;
+ /* Start WDT_EXT_TIMER (CONFIG_AUX_TIMER_PERIOD_MS ms). */
+ ext_timer_ms(WDT_EXT_TIMER, EXT_PSR_32P768K_HZ, 1, 1,
+ CONFIG_AUX_TIMER_PERIOD_MS, 1, 0);
/* Start timer 1 (must be started for watchdog timer to run). */
IT83XX_ETWD_ET1CNTLLR = 0x00;
- /* Set watchdog timer to ~1.3 seconds. Writing CNTLL starts timer. */
- IT83XX_ETWD_EWDCNTLHR = 0x05;
- IT83XX_ETWD_EWDCNTLLR = 0x00;
+ /*
+ * Set watchdog timer to CONFIG_WATCHDOG_PERIOD_MS ms.
+ * Writing CNTLL starts timer.
+ */
+ IT83XX_ETWD_EWDCNTLHR = (wdt_count >> 8) & 0xff;
+ IT83XX_ETWD_EWDCNTLLR = wdt_count & 0xff;
/* Lock access to watchdog registers. */
IT83XX_ETWD_ETWCFG = 0x3f;
diff --git a/core/nds32/init.S b/core/nds32/init.S
index a27fcd8d0e..d5d93d299b 100644
--- a/core/nds32/init.S
+++ b/core/nds32/init.S
@@ -20,6 +20,8 @@ __entry_\()\name:
smw.adm $r15, [$sp], $r15, 0xb
/* r0-r5 are caller saved */
smw.adm $r0, [$sp], $r5, 0
+ /* isr entry */
+ jal start_irq_handler
/* switch to system stack if we are called from process stack */
la $r3, stack_end
mov55 $fp, $sp
@@ -30,6 +32,8 @@ __entry_\()\name:
/* check whether we need to change the scheduled task */
lwi.gp $r2, [ + need_resched]
bnez $r2, __switch_task
+ /* isr exit */
+ jal end_irq_handler
/* restore r0-r5 */
lmw.bim $r0, [$fp], $r5, 0
/* restore r15, fp, lp and sp */
diff --git a/core/nds32/irq_chip.h b/core/nds32/irq_chip.h
index e7b67317a8..dfbd75ea6f 100644
--- a/core/nds32/irq_chip.h
+++ b/core/nds32/irq_chip.h
@@ -51,4 +51,9 @@ int chip_trigger_irq(int irq);
*/
void chip_init_irqs(void);
+/**
+ * Return interrupt number of software interrupt.
+ */
+int get_sw_int(void);
+
#endif /* __CROS_EC_IRQ_CHIP_H */
diff --git a/core/nds32/switch.S b/core/nds32/switch.S
index 2c4ce0c273..81c0770dea 100644
--- a/core/nds32/switch.S
+++ b/core/nds32/switch.S
@@ -63,6 +63,8 @@ __switch_task:
/* exception frame pointer for the new task */
mov55 $fp, $r3
1: /* un-pile the interruption entry context */
+ /* isr exit */
+ jal end_irq_handler
/* restore r0-r5 */
lmw.bim $r0, [$fp], $r5, 0
/* restore r15, fp, lp and sp */
diff --git a/core/nds32/task.c b/core/nds32/task.c
index 3c7358abe6..df7743ed82 100644
--- a/core/nds32/task.c
+++ b/core/nds32/task.c
@@ -48,6 +48,18 @@ static const char * const task_names[] = {
};
#undef TASK
+#ifdef CONFIG_TASK_PROFILING
+static int task_will_switch;
+static uint64_t exc_sub_time;
+static uint64_t task_start_time; /* Time task scheduling started */
+static uint64_t exc_start_time; /* Time of task->exception transition */
+static uint64_t exc_end_time; /* Time of exception->task transition */
+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 */
+#endif
+
extern int __task_start(void);
#ifndef CONFIG_LOW_POWER_IDLE
@@ -168,6 +180,9 @@ static uint32_t tasks_ready = (1 << TASK_ID_HOOKS);
static int start_called; /* Has task swapping started */
+/* interrupt number of sw interrupt */
+static int sw_int_num;
+
static inline task_ *__task_id_to_ptr(task_id_t id)
{
return tasks + id;
@@ -237,6 +252,14 @@ int task_start_called(void)
return start_called;
}
+int get_sw_int(void)
+{
+ /* If this is a SW interrupt */
+ if (get_itype() & 8)
+ return sw_int_num;
+ return 0;
+}
+
/**
* Scheduling system call
*
@@ -251,6 +274,7 @@ void syscall_handler(int desched, task_id_t resched, int swirq)
set_ipc(get_ipc() + 4);
/* call the regular IRQ handler */
handler();
+ sw_int_num = 0;
return;
}
@@ -266,13 +290,27 @@ void syscall_handler(int desched, task_id_t resched, int swirq)
/* trigger a re-scheduling on exit */
need_resched = 1;
+#ifdef CONFIG_TASK_PROFILING
+ svc_calls++;
+#endif
+
/* adjust IPC to return *after* the syscall instruction */
set_ipc(get_ipc() + 4);
}
task_ *next_sched_task(void)
{
- return __task_id_to_ptr(__fls(tasks_ready));
+ task_ *new_task = __task_id_to_ptr(__fls(tasks_ready));
+
+#ifdef CONFIG_TASK_PROFILING
+ if (current_task != new_task) {
+ current_task->runtime +=
+ (exc_start_time - exc_end_time - exc_sub_time);
+ task_will_switch = 1;
+ }
+#endif
+
+ return new_task;
}
static inline void __schedule(int desched, int resched, int swirq)
@@ -284,6 +322,67 @@ static inline void __schedule(int desched, int resched, int swirq)
asm("syscall 0" : : "r"(p0), "r"(p1), "r"(p2));
}
+void update_exc_start_time(void)
+{
+#ifdef CONFIG_TASK_PROFILING
+ exc_start_time = get_time().val;
+#endif
+}
+
+void start_irq_handler(void)
+{
+#ifdef CONFIG_TASK_PROFILING
+ int irq;
+
+ /* save r0, r1, and r2 for syscall */
+ asm volatile ("smw.adm $r0, [$sp], $r2, 0");
+
+ update_exc_start_time();
+
+ irq = get_sw_int();
+ if (!irq)
+ irq = IT83XX_INTC_AIVCT - 16;
+
+ /*
+ * Track IRQ distribution. No need for atomic add, because an IRQ
+ * can't pre-empt itself.
+ */
+ if ((irq > 0) && (irq < ARRAY_SIZE(irq_dist)))
+ irq_dist[irq]++;
+
+ /* restore r0, r1, and r2 */
+ asm volatile ("lmw.bim $r0, [$sp], $r2, 0");
+#endif
+}
+
+void end_irq_handler(void)
+{
+#ifdef CONFIG_TASK_PROFILING
+ uint64_t t, p;
+
+ /*
+ * save r0 and fp (fp for restore r0-r5, r15, fp, lp and sp
+ * while interrupt exit.
+ */
+ asm volatile ("smw.adm $r0, [$sp], $r0, 8");
+
+ t = get_time().val;
+ p = t - exc_start_time;
+
+ exc_total_time += p;
+ exc_sub_time += p;
+ if (task_will_switch) {
+ task_will_switch = 0;
+ exc_sub_time = 0;
+ exc_end_time = t;
+ task_switches++;
+ }
+
+ /* restore r0 and fp */
+ asm volatile ("lmw.bim $r0, [$sp], $r0, 8");
+#endif
+}
+
static uint32_t __wait_evt(int timeout_us, task_id_t resched)
{
task_ *tsk = current_task;
@@ -380,8 +479,10 @@ void task_clear_pending_irq(int irq)
void task_trigger_irq(int irq)
{
int cpu_int = chip_trigger_irq(irq);
- if (cpu_int > 0)
+ if (cpu_int > 0) {
+ sw_int_num = irq;
__schedule(0, 0, cpu_int);
+ }
}
/*
@@ -490,8 +591,32 @@ void task_print_list(void)
int command_task_info(int argc, char **argv)
{
+#ifdef CONFIG_TASK_PROFILING
+ int total = 0;
+ int i;
+#endif
+
task_print_list();
+#ifdef CONFIG_TASK_PROFILING
+ ccputs("IRQ counts by type:\n");
+ cflush();
+ for (i = 0; i < ARRAY_SIZE(irq_dist); i++) {
+ if (irq_dist[i]) {
+ ccprintf("%4d %8d\n", i, irq_dist[i]);
+ total += irq_dist[i];
+ }
+ }
+
+ ccprintf("Service calls: %11d\n", svc_calls);
+ ccprintf("Total exceptions: %11d\n", total + svc_calls);
+ ccprintf("Task switches: %11d\n", task_switches);
+ ccprintf("Task switching started: %11.6ld s\n", task_start_time);
+ ccprintf("Time in tasks: %11.6ld s\n",
+ get_time().val - task_start_time);
+ ccprintf("Time in exceptions: %11.6ld s\n", exc_total_time);
+#endif
+
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(taskinfo, command_task_info,
@@ -564,6 +689,9 @@ void task_pre_init(void)
int task_start(void)
{
+#ifdef CONFIG_TASK_PROFILING
+ task_start_time = exc_end_time = get_time().val;
+#endif
start_called = 1;
return __task_start();