/* Copyright (c) 2014 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. */ #include "common.h" #include "hooks.h" #include "hwtimer.h" #include "init_chip.h" #include "registers.h" #include "task.h" #include "util.h" /* The frequency of timerls is 256k so there are about 4usec/tick */ #define USEC_PER_TICK 4 /* * Scale the maximum number of ticks so that it will only count up to the * equivalent of 0xffffffff usecs. */ #define TIMELS_MAX (0xffffffff / USEC_PER_TICK) #define SOURCE(field) TIMER0_##field #define EVENT(field) TIMER1_##field static inline uint32_t ticks_to_usecs(uint32_t ticks) { return ticks * USEC_PER_TICK; } static inline uint32_t usec_to_ticks(uint32_t next_evt_us) { return next_evt_us / USEC_PER_TICK; } uint32_t __hw_clock_event_get(void) { /* At what time will the next event fire? */ return __hw_clock_source_read() + ticks_to_usecs(GREG32(TIMELS, EVENT(VALUE))); } void __hw_clock_event_clear(void) { /* one-shot, 32-bit, timer & interrupts disabled, 1:1 prescale */ GWRITE_FIELD(TIMELS, EVENT(CONTROL), ENABLE, 0); /* Disable interrupts */ GWRITE(TIMELS, EVENT(IER), 0); /* Clear any pending interrupts */ GWRITE(TIMELS, EVENT(WAKEUP_ACK), 1); GWRITE(TIMELS, EVENT(IAR), 1); } void __hw_clock_event_set(uint32_t deadline) { uint32_t event_time; __hw_clock_event_clear(); /* How long from the current time to the deadline? */ event_time = (deadline - __hw_clock_source_read()); /* Convert event_time to ticks rounding up */ GREG32(TIMELS, EVENT(LOAD)) = ((uint64_t)(event_time + USEC_PER_TICK - 1) / USEC_PER_TICK); /* Enable the timer & interrupts */ GWRITE(TIMELS, EVENT(IER), 1); GWRITE_FIELD(TIMELS, EVENT(CONTROL), ENABLE, 1); } /* * Handle event matches. It's priority matches the HW rollover irq to prevent * a race condition that could lead to a watchdog timeout if preempted after * the get_time() call in process_timers(). */ void __hw_clock_event_irq(void) { __hw_clock_event_clear(); process_timers(0); } DECLARE_IRQ(GC_IRQNUM_TIMELS0_TIMINT1, __hw_clock_event_irq, 1); uint32_t __hw_clock_source_read(void) { /* * Return the current time in usecs. Since the counter counts down, * we have to invert the value. */ return ticks_to_usecs(TIMELS_MAX - GREG32(TIMELS, SOURCE(VALUE))); } void __hw_clock_source_set(uint32_t ts) { GREG32(TIMELS, SOURCE(LOAD)) = (0xffffffff - ts) / USEC_PER_TICK; } /* This handles rollover in the HW timer */ void __hw_clock_source_irq(void) { /* Clear the interrupt */ GWRITE(TIMELS, SOURCE(WAKEUP_ACK), 1); GWRITE(TIMELS, SOURCE(IAR), 1); /* Reset the load value */ GREG32(TIMELS, SOURCE(LOAD)) = TIMELS_MAX; process_timers(1); } DECLARE_IRQ(GC_IRQNUM_TIMELS0_TIMINT0, __hw_clock_source_irq, 1); int __hw_clock_source_init(uint32_t start_t) { if (runlevel_is_high()) { /* Verify the contents of CC_TRIM are valid */ ASSERT(GR_FUSE(RC_RTC_OSC256K_CC_EN) == 0x5); /* Initialize RTC to 256kHz */ GWRITE_FIELD(RTC, CTRL, X_RTC_RC_CTRL, GR_FUSE(RC_RTC_OSC256K_CC_TRIM)); } /* Configure timer1 */ GREG32(TIMELS, EVENT(LOAD)) = TIMELS_MAX; GREG32(TIMELS, EVENT(RELOADVAL)) = TIMELS_MAX; GWRITE_FIELD(TIMELS, EVENT(CONTROL), WRAP, 1); GWRITE_FIELD(TIMELS, EVENT(CONTROL), RELOAD, 0); GWRITE_FIELD(TIMELS, EVENT(CONTROL), ENABLE, 0); /* Configure timer0 */ GREG32(TIMELS, SOURCE(RELOADVAL)) = TIMELS_MAX; GWRITE_FIELD(TIMELS, SOURCE(CONTROL), WRAP, 1); GWRITE_FIELD(TIMELS, SOURCE(CONTROL), RELOAD, 1); /* Event timer disabled */ __hw_clock_event_clear(); /* Clear any pending interrupts */ GWRITE(TIMELS, SOURCE(WAKEUP_ACK), 1); /* Force the time to whatever we're told it is */ __hw_clock_source_set(start_t); /* HW Timer enabled, periodic, interrupt enabled, 32-bit, wrapping */ GWRITE_FIELD(TIMELS, SOURCE(CONTROL), ENABLE, 1); /* Enable source timer interrupts */ GWRITE(TIMELS, SOURCE(IER), 1); /* Here we go... */ task_enable_irq(GC_IRQNUM_TIMELS0_TIMINT0); task_enable_irq(GC_IRQNUM_TIMELS0_TIMINT1); /* Return the Event timer IRQ number (NOT the HW timer IRQ) */ return GC_IRQNUM_TIMELS0_TIMINT1; }