347 lines
9.6 KiB
C
347 lines
9.6 KiB
C
|
/* 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;
|
||
|
}
|