diff options
Diffstat (limited to 'chip/it83xx/hwtimer.c')
-rw-r--r-- | chip/it83xx/hwtimer.c | 388 |
1 files changed, 234 insertions, 154 deletions
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; |