summaryrefslogtreecommitdiff
path: root/chip/it83xx/hwtimer.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/it83xx/hwtimer.c')
-rw-r--r--chip/it83xx/hwtimer.c388
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;