/* Copyright 2013 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* Hardware timers driver */ #include "cpu.h" #include "common.h" #include "hooks.h" #include "hwtimer.h" #include "hwtimer_chip.h" #include "intc.h" #include "irq_chip.h" #include "registers.h" #include "task.h" #include "timer.h" #include "util.h" #include "watchdog.h" /* * 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). * * 32-bit MHz free-running counter: We combine (bit3@IT83XX_ETWD_ETXCTRL) * timer 3(TIMER_L) and 4(TIMER_H) and set clock source register to 8MHz. * In combinational mode, the counter register(IT83XX_ETWD_ETXCNTLR) of timer 3 * is a fixed value = 7, and observation register(IT83XX_ETWD_ETXCNTOR) * of timer 4 will increase one per-us. * * For example, if * __hw_clock_source_set() set 0 us, the counter setting registers are * timer 3(TIMER_L) = 0x000007 (fixed, will not change) * timer 4(TIMER_H) = 0xffffffff * * 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) 0x0000007 * timer 4(TIMER_H) ~0xffffffff = 0x00000000 * * The following will describe timer 3 and 4's operation in combinational mode: * 1. When timer 3(TIMER_L) has completed each counting (per-us), timer 4(TIMER_H) observation value++. * 2. When timer 4(TIMER_H) observation value overflows: * timer 4(TIMER_H) observation value = ~counter setting register. * 3. 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 MS_TO_COUNT(hz, ms) ((hz) * (ms) / 1000) const struct ext_timer_ctrl_t et_ctrl_regs[] = { {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x08, IT83XX_IRQ_EXT_TIMER3}, {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x10, IT83XX_IRQ_EXT_TIMER4}, {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x20, IT83XX_IRQ_EXT_TIMER5}, {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x40, IT83XX_IRQ_EXT_TIMER6}, {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, &IT83XX_INTC_ISR19, 0x80, IT83XX_IRQ_EXT_TIMER7}, {&IT83XX_INTC_IELMR10, &IT83XX_INTC_IPOLR10, &IT83XX_INTC_ISR10, 0x01, IT83XX_IRQ_EXT_TMR8}, }; BUILD_ASSERT(ARRAY_SIZE(et_ctrl_regs) == EXT_TIMER_COUNT); static void free_run_timer_overflow(void) { /* * If timer 4 (TIMER_H) counter register != 0xffffffff. * 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) != 0xffffffff) { /* set timer counter register */ IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_H) = 0xffffffff; /* bit[1], timer reset */ IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) |= BIT(1); } /* w/c interrupt status */ task_clear_pending_irq(et_ctrl_regs[FREE_EXT_TIMER_H].irq); /* timer overflow */ process_timers(1); update_exc_start_time(); } static void event_timer_clear_pending_isr(void) { /* w/c interrupt status */ task_clear_pending_irq(et_ctrl_regs[EVENT_EXT_TIMER].irq); } uint32_t __ram_code __hw_clock_source_read(void) { #ifdef IT83XX_EXT_OBSERVATION_REG_READ_TWO_TIMES /* * In combinational mode, the counter observation register of * timer 4(TIMER_H) will increment. */ return ext_observation_reg_read(FREE_EXT_TIMER_H); #else return IT83XX_ETWD_ETXCNTOR(FREE_EXT_TIMER_H); #endif } void __hw_clock_source_set(uint32_t ts) { /* counting down timer, microseconds to timer counter register */ IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_H) = 0xffffffff - ts; /* bit[1], timer reset */ IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) |= BIT(1); } void __hw_clock_event_set(uint32_t deadline) { uint32_t wait; /* bit0, disable event timer */ IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) &= ~BIT(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) & BIT(0)) { /* timer counter observation value to microseconds */ next_event_us += EVENT_TIMER_COUNT_TO_US( #ifdef IT83XX_EXT_OBSERVATION_REG_READ_TWO_TIMES ext_observation_reg_read(EVENT_EXT_TIMER)); #else IT83XX_ETWD_ETXCNTOR(EVENT_EXT_TIMER)); #endif } 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) { /* bit3, timer 3 and timer 4 combinational mode */ IT83XX_ETWD_ETXCTRL(FREE_EXT_TIMER_L) |= BIT(3); /* init free running timer (timer 4, TIMER_H), clock source is 8mhz */ ext_timer_ms(FREE_EXT_TIMER_H, EXT_PSR_8M_HZ, 0, 1, 0xffffffff, 1, 1); /* 1us counter setting (timer 3, TIMER_L) */ ext_timer_ms(FREE_EXT_TIMER_L, EXT_PSR_8M_HZ, 1, 0, 7, 1, 1); __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) { /* Determine interrupt number. */ int irq = intc_get_ec_int(); /* SW/HW interrupt of event timer. */ if (irq == et_ctrl_regs[EVENT_EXT_TIMER].irq) { IT83XX_ETWD_ETXCNTLR(EVENT_EXT_TIMER) = 0xffffffff; IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) |= BIT(1); event_timer_clear_pending_isr(); process_timers(0); return; } #ifdef CONFIG_WATCHDOG /* * Both the external timer for the watchdog warning and the HW timer * go through this irq. So, if this interrupt was caused by watchdog * warning timer, then call that function. */ if (irq == et_ctrl_regs[WDT_EXT_TIMER].irq) { watchdog_warning_irq(); return; } #endif #ifdef CONFIG_FANS if (irq == et_ctrl_regs[FAN_CTRL_EXT_TIMER].irq) { fan_ext_timer_interrupt(); return; } #endif /* Interrupt of free running timer TIMER_H. */ if (irq == et_ctrl_regs[FREE_EXT_TIMER_H].irq) { free_run_timer_overflow(); return; } /* * This interrupt is used to wakeup EC from sleep mode * to complete PLL frequency change. */ if (irq == et_ctrl_regs[LOW_POWER_EXT_TIMER].irq) { ext_timer_stop(LOW_POWER_EXT_TIMER, 1); return; } } DECLARE_IRQ(CPU_INT_GROUP_3, __hw_clock_source_irq, 1); #ifdef IT83XX_EXT_OBSERVATION_REG_READ_TWO_TIMES /* Number of CPU cycles in 125 us */ #define CYCLES_125NS (125*(PLL_CLOCK/SECOND) / 1000) uint32_t __ram_code ext_observation_reg_read(enum ext_timer_sel ext_timer) { uint32_t prev_mask = get_int_mask(); uint32_t val; interrupt_disable(); asm volatile( /* read observation register for the first time */ "lwi %0,[%1]\n\t" /* * the delay time between reading the first and second * observation registers need to be greater than 0.125us and * smaller than 0.250us. */ ".rept %2\n\t" "nop\n\t" ".endr\n\t" /* read for the second time */ "lwi %0,[%1]\n\t" : "=&r"(val) : "r"((uintptr_t) &IT83XX_ETWD_ETXCNTOR(ext_timer)), "i"(CYCLES_125NS)); /* restore interrupts */ set_int_mask(prev_mask); return val; } #endif void ext_timer_start(enum ext_timer_sel ext_timer, int en_irq) { /* enable external timer n */ IT83XX_ETWD_ETXCTRL(ext_timer) |= 0x03; if (en_irq) { task_clear_pending_irq(et_ctrl_regs[ext_timer].irq); task_enable_irq(et_ctrl_regs[ext_timer].irq); } } void ext_timer_stop(enum ext_timer_sel ext_timer, int dis_irq) { /* disable external timer n */ IT83XX_ETWD_ETXCTRL(ext_timer) &= ~0x01; if (dis_irq) task_disable_irq(et_ctrl_regs[ext_timer].irq); } static void ext_timer_ctrl(enum ext_timer_sel ext_timer, enum ext_timer_clock_source ext_timer_clock, int start, int with_int, int32_t count) { uint8_t intc_mask; /* rising-edge-triggered */ intc_mask = et_ctrl_regs[ext_timer].mask; *et_ctrl_regs[ext_timer].mode |= intc_mask; *et_ctrl_regs[ext_timer].polarity &= ~intc_mask; /* clear interrupt status */ task_clear_pending_irq(et_ctrl_regs[ext_timer].irq); /* These bits control the clock input source to the exttimer 3 - 8 */ IT83XX_ETWD_ETXPSR(ext_timer) = ext_timer_clock; /* The count number of external timer n. */ IT83XX_ETWD_ETXCNTLR(ext_timer) = count; ext_timer_stop(ext_timer, 0); if (start) ext_timer_start(ext_timer, 0); if (with_int) task_enable_irq(et_ctrl_regs[ext_timer].irq); else task_disable_irq(et_ctrl_regs[ext_timer].irq); } int ext_timer_ms(enum ext_timer_sel ext_timer, enum ext_timer_clock_source ext_timer_clock, int start, int with_int, int32_t ms, int first_time_enable, int raw) { uint32_t count; 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; if (first_time_enable) { ext_timer_start(ext_timer, 0); ext_timer_stop(ext_timer, 0); } ext_timer_ctrl(ext_timer, ext_timer_clock, start, with_int, count); return 0; }