mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-30 18:41:11 +00:00
The jittery clock and trng security features require high permissions to be initialized. In the future these initializations and the permission level drop may be moved to RO. This change adds permission level checks before trying to access any registers that require high permission, so when we update RO to change the permission RW can still function fine. BUG=chrome-os-partner:59107 BRANCH=none TEST=Move the permission drop to the beginning of main and verify the system still boots. Change-Id: I5b7cb856decd0640288ad3476f875ec9edc42635 Signed-off-by: Mary Ruthven <mruthven@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/405840
159 lines
4.1 KiB
C
159 lines
4.1 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 "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;
|
|
}
|