mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-30 10:31:02 +00:00
We were losing timer events because usecs_to_ticks(deadline) was clipping the value when deadline was larger than 0x08888888 (deadline is a timestamp rather than a delay). The computation of the timer deadline has been modified to avoid the clipping issue. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=chrome-os-partner:34347 TEST=run on Cr50 with the watchdog activated and no longer see watchdog warnings. Run the "timer_calib" test and see that the 1s sleep is 1000038 us : make BOARD=cr50 tests fhl ../build/cr50/timer_calib/timer_calib.RO.hex Change-Id: Id2200a89eb1b72099e536291af321609b24b4777 Reviewed-on: https://chromium-review.googlesource.com/233531 Reviewed-by: Bill Richardson <wfrichar@chromium.org> Trybot-Ready: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
167 lines
4.7 KiB
C
167 lines
4.7 KiB
C
/* 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 "registers.h"
|
|
#include "task.h"
|
|
#include "util.h"
|
|
|
|
/*
|
|
* Other chips can interrupt at arbitrary match points. We can only interrupt
|
|
* at zero, so we'll have to use a separate timer for events. We'll use module
|
|
* 0, timer 1 for the current time and module 0, timer 2 for event timers.
|
|
*
|
|
* Oh, and we can't control the rate at which the timers tick. We expect to
|
|
* have all counter values in microseconds, but instead they'll be some factor
|
|
* faster than that. 1 usec / tick == 1 MHz, so if PCLK is 30 MHz, we'll have
|
|
* to divide the hardware counter by 30 to get the values expected outside of
|
|
* this file.
|
|
*/
|
|
static uint32_t clock_mul_factor;
|
|
static uint32_t clock_div_factor;
|
|
static uint32_t hw_rollover_count;
|
|
|
|
static inline uint32_t ticks_to_usecs(uint32_t ticks)
|
|
{
|
|
return hw_rollover_count * clock_div_factor + ticks / clock_mul_factor;
|
|
}
|
|
|
|
static inline uint32_t usecs_to_ticks(uint32_t usecs)
|
|
{
|
|
/* Really large counts will just be scheduled more than once */
|
|
if (usecs >= clock_div_factor)
|
|
return 0xffffffff;
|
|
|
|
return usecs * clock_mul_factor;
|
|
}
|
|
|
|
static void update_prescaler(void)
|
|
{
|
|
/*
|
|
* We want the timer to tick every microsecond, but we can only divide
|
|
* PCLK by 1, 16, or 256. We're targeting 30MHz, so we'll just let it
|
|
* run at 1:1.
|
|
*/
|
|
REG_WRITE_MLV(GR_TIMEHS_CONTROL(0, 1),
|
|
GC_TIMEHS_TIMER1CONTROL_PRE_MASK,
|
|
GC_TIMEHS_TIMER1CONTROL_PRE_LSB, 0);
|
|
REG_WRITE_MLV(GR_TIMEHS_CONTROL(0, 2),
|
|
GC_TIMEHS_TIMER1CONTROL_PRE_MASK,
|
|
GC_TIMEHS_TIMER1CONTROL_PRE_LSB, 0);
|
|
|
|
/*
|
|
* We're not yet doing anything to detect the current frequency, we're
|
|
* just hard-coding it. We're also assuming the clock rate is an
|
|
* integer multiple of MHz.
|
|
*/
|
|
clock_mul_factor = 30; /* NOTE: prototype board */
|
|
clock_div_factor = 0xffffffff / clock_mul_factor;
|
|
}
|
|
DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT);
|
|
|
|
uint32_t __hw_clock_event_get(void)
|
|
{
|
|
/* At what time will the next event fire? */
|
|
uint32_t time_now_in_ticks;
|
|
time_now_in_ticks = (0xffffffff - GR_TIMEHS_VALUE(0, 1));
|
|
return ticks_to_usecs(time_now_in_ticks + GR_TIMEHS_VALUE(0, 2));
|
|
}
|
|
|
|
void __hw_clock_event_clear(void)
|
|
{
|
|
/* one-shot, 32-bit, timer & interrupts disabled, 1:1 prescale */
|
|
GR_TIMEHS_CONTROL(0, 2) = 0x3;
|
|
/* Clear any pending interrupts */
|
|
GR_TIMEHS_INTCLR(0, 2) = 0x1;
|
|
}
|
|
|
|
void __hw_clock_event_set(uint32_t deadline)
|
|
{
|
|
uint32_t time_now_in_ticks;
|
|
|
|
__hw_clock_event_clear();
|
|
|
|
/* How long from the current time to the deadline? */
|
|
time_now_in_ticks = (0xffffffff - GR_TIMEHS_VALUE(0, 1));
|
|
GR_TIMEHS_LOAD(0, 2) = (deadline - time_now_in_ticks / clock_mul_factor)
|
|
* clock_mul_factor;
|
|
|
|
/* timer & interrupts enabled */
|
|
GR_TIMEHS_CONTROL(0, 2) = 0xa3;
|
|
}
|
|
|
|
/*
|
|
* Handle event matches. It's lower priority than the HW rollover irq, so it
|
|
* will always be either before or after a rollover exception.
|
|
*/
|
|
void __hw_clock_event_irq(void)
|
|
{
|
|
__hw_clock_event_clear();
|
|
process_timers(0);
|
|
}
|
|
DECLARE_IRQ(GC_IRQNUM_TIMEHS0_TIMINT2, __hw_clock_event_irq, 2);
|
|
|
|
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(0xffffffff - GR_TIMEHS_VALUE(0, 1));
|
|
}
|
|
|
|
void __hw_clock_source_set(uint32_t ts)
|
|
{
|
|
GR_TIMEHS_LOAD(0, 1) = 0xffffffff - usecs_to_ticks(ts);
|
|
}
|
|
|
|
/* This handles rollover in the HW timer */
|
|
void __hw_clock_source_irq(void)
|
|
{
|
|
/* Clear the interrupt */
|
|
GR_TIMEHS_INTCLR(0, 1) = 0x1;
|
|
|
|
/* The one-tick-per-clock HW counter has rolled over. */
|
|
hw_rollover_count++;
|
|
/* Has the system's usec counter rolled over? */
|
|
if (hw_rollover_count >= clock_mul_factor) {
|
|
hw_rollover_count = 0;
|
|
process_timers(1);
|
|
} else {
|
|
process_timers(0);
|
|
}
|
|
}
|
|
DECLARE_IRQ(GC_IRQNUM_TIMEHS0_TIMINT1, __hw_clock_source_irq, 1);
|
|
|
|
int __hw_clock_source_init(uint32_t start_t)
|
|
{
|
|
/* Set the reload and current value. */
|
|
GR_TIMEHS_BGLOAD(0, 1) = 0xffffffff;
|
|
GR_TIMEHS_LOAD(0, 1) = 0xffffffff;
|
|
|
|
/* HW Timer enabled, periodic, interrupt enabled, 32-bit, wrapping */
|
|
GR_TIMEHS_CONTROL(0, 1) = 0xe2;
|
|
/* Event timer disabled */
|
|
__hw_clock_event_clear();
|
|
|
|
/* Account for the clock speed. */
|
|
update_prescaler();
|
|
|
|
/* Clear any pending interrupts */
|
|
GR_TIMEHS_INTCLR(0, 1) = 0x1;
|
|
|
|
/* Force the time to whatever we're told it is */
|
|
__hw_clock_source_set(start_t);
|
|
|
|
/* Here we go... */
|
|
task_enable_irq(GC_IRQNUM_TIMEHS0_TIMINT1);
|
|
task_enable_irq(GC_IRQNUM_TIMEHS0_TIMINT2);
|
|
|
|
/* Return the Event timer IRQ number (NOT the HW timer IRQ) */
|
|
return GC_IRQNUM_TIMEHS0_TIMINT2;
|
|
}
|