diff options
-rw-r--r-- | board/it8380dev/board.h | 2 | ||||
-rw-r--r-- | chip/it83xx/config_chip.h | 5 | ||||
-rw-r--r-- | chip/it83xx/fan.c | 2 | ||||
-rw-r--r-- | chip/it83xx/hwtimer.c | 388 | ||||
-rw-r--r-- | chip/it83xx/hwtimer_chip.h | 24 | ||||
-rw-r--r-- | chip/it83xx/irq.c | 3 | ||||
-rw-r--r-- | chip/it83xx/registers.h | 13 | ||||
-rw-r--r-- | chip/it83xx/uart.c | 3 | ||||
-rw-r--r-- | chip/it83xx/watchdog.c | 40 | ||||
-rw-r--r-- | core/nds32/init.S | 4 | ||||
-rw-r--r-- | core/nds32/irq_chip.h | 5 | ||||
-rw-r--r-- | core/nds32/switch.S | 2 | ||||
-rw-r--r-- | core/nds32/task.c | 132 |
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(); |