summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2014-11-13 14:02:30 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-11-14 03:03:32 +0000
commit9157dd93daf0500c4f8f0768b00dafb324d633ec (patch)
tree5cf26a3379168fbf9f8a0e7f4ff294104f73d548
parent3ddd906b92deff11b29934bd0e5fdc49b3fd3921 (diff)
downloadchrome-ec-9157dd93daf0500c4f8f0768b00dafb324d633ec.tar.gz
cr50: Add support for hwtimer
Implement the API expected by common/timer.c BUG=chrome-os-partner:33699 BRANCH=none TEST=manual Run the "gettime" and "timerinfo" and "taskinfo" and "waitms" commands. Compare the elapsed time with the real world. They seem to match. Change-Id: Ie5acae76780ee09e7dfb6cc0282de25f8063e96f Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/229642 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--chip/g/hwtimer.c145
-rw-r--r--chip/g/registers.h45
2 files changed, 186 insertions, 4 deletions
diff --git a/chip/g/hwtimer.c b/chip/g/hwtimer.c
index ec9750db0a..f6bd9f67d5 100644
--- a/chip/g/hwtimer.c
+++ b/chip/g/hwtimer.c
@@ -4,21 +4,158 @@
*/
#include "common.h"
+#include "hooks.h"
#include "hwtimer.h"
+#include "registers.h"
+#include "task.h"
+#include "util.h"
-/* TODO(crosbug.com/p/33432): Implement these functions */
+/*
+ * 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_MASK(G_TIMEHS_CONTROL(0, 1), 0x0c, 0x00, 2);
+ REG_WRITE_MASK(G_TIMEHS_CONTROL(0, 2), 0x0c, 0x00, 2);
+
+ /*
+ * 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 = 26; /* 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)
{
- return 0;
+ /* At what time will the next event fire? */
+ uint32_t time_now_in_ticks;
+ time_now_in_ticks = (0xffffffff - G_TIMEHS_VALUE(0, 1));
+ return ticks_to_usecs(time_now_in_ticks + G_TIMEHS_VALUE(0, 2));
+}
+
+void __hw_clock_event_clear(void)
+{
+ /* one-shot, 32-bit, timer & interrupts disabled, 1:1 prescale */
+ G_TIMEHS_CONTROL(0, 2) = 0x3;
+ /* Clear any pending interrupts */
+ G_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 - G_TIMEHS_VALUE(0, 1));
+ G_TIMEHS_LOAD(0, 2) = usecs_to_ticks(deadline) - time_now_in_ticks;
+
+ /* timer & interrupts enabled */
+ G_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(G_IRQNUM_TIMEHS0_TIMINT2, __hw_clock_event_irq, 2);
uint32_t __hw_clock_source_read(void)
{
- return 0;
+ /*
+ * Return the current time in usecs. Since the counter counts down,
+ * we have to invert the value.
+ */
+ return ticks_to_usecs(0xffffffff - G_TIMEHS_VALUE(0, 1));
}
+void __hw_clock_source_set(uint32_t ts)
+{
+ G_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 */
+ G_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(G_IRQNUM_TIMEHS0_TIMINT1, __hw_clock_source_irq, 1);
+
int __hw_clock_source_init(uint32_t start_t)
{
- return 0;
+ /* Set the reload and current value. */
+ G_TIMEHS_BGLOAD(0, 1) = 0xffffffff;
+ G_TIMEHS_LOAD(0, 1) = 0xffffffff;
+
+ /* HW Timer enabled, periodic, interrupt enabled, 32-bit, wrapping */
+ G_TIMEHS_CONTROL(0, 1) = 0xe2;
+ /* Event timer disabled */
+ __hw_clock_event_clear();
+
+ /* Account for the clock speed. */
+ update_prescaler();
+
+ /* Clear any pending interrupts */
+ G_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(G_IRQNUM_TIMEHS0_TIMINT1);
+ task_enable_irq(G_IRQNUM_TIMEHS0_TIMINT2);
+
+ /* Return the Event timer IRQ number (NOT the HW timer IRQ) */
+ return G_IRQNUM_TIMEHS0_TIMINT2;
}
diff --git a/chip/g/registers.h b/chip/g/registers.h
index a45d94914d..9a705f99b1 100644
--- a/chip/g/registers.h
+++ b/chip/g/registers.h
@@ -80,6 +80,51 @@ static inline int g_uart_addr(int ch, int offset)
#define G_UART_FIFO(ch) G_UARTREG(ch, 0x0024)
#define G_UART_RFIFO(ch) G_UARTREG(ch, 0x0028)
+
+/*
+ * High-speed timers. Two modules with two timers each; four timers total.
+ */
+#define G_TIMEHS0_BASE_ADDR 0x40570000
+#define G_TIMEHS1_BASE_ADDR 0x40580000
+#define G_TIMEHS_BASE_ADDR_SEP 0x00010000
+#define G_TIMEHSX_TIMER1_OFS 0x00
+#define G_TIMEHSX_TIMER2_OFS 0x20
+#define G_TIMEHSX_TIMER_OFS_SEP 0x20
+/* NOTE: module is 0-1, timer is 1-2 */
+static inline int g_timehs_addr(unsigned int module, unsigned int timer,
+ int offset)
+{
+ return G_TIMEHS0_BASE_ADDR + G_TIMEHS_BASE_ADDR_SEP * module
+ + G_TIMEHSX_TIMER1_OFS + G_TIMEHSX_TIMER_OFS_SEP * (timer - 1)
+ + offset;
+}
+/* Per-timer registers */
+#define G_TIMEHSREG(m, t, ofs) REG32(g_timehs_addr(m, t, ofs))
+#define G_TIMEHS_LOAD(m, t) G_TIMEHSREG(m, t, 0x0000)
+#define G_TIMEHS_VALUE(m, t) G_TIMEHSREG(m, t, 0x0004)
+#define G_TIMEHS_CONTROL(m, t) G_TIMEHSREG(m, t, 0x0008)
+#define G_TIMEHS_INTCLR(m, t) G_TIMEHSREG(m, t, 0x000c)
+#define G_TIMEHS_RIS(m, t) G_TIMEHSREG(m, t, 0x0010)
+#define G_TIMEHS_MIS(m, t) G_TIMEHSREG(m, t, 0x0014)
+#define G_TIMEHS_BGLOAD(m, t) G_TIMEHSREG(m, t, 0x0018)
+/* These are only per-module */
+#define G_TIMEHS_ITCR(m) G_TIMEHSREG(m, 1, 0x0f00)
+#define G_TIMEHS_ITOP(m) G_TIMEHSREG(m, 1, 0x0f04)
+#define G_TIMEHS_PERIPHID4(m) G_TIMEHSREG(m, 1, 0x0fd0)
+#define G_TIMEHS_PERIPHID5(m) G_TIMEHSREG(m, 1, 0x0fd4)
+#define G_TIMEHS_PERIPHID6(m) G_TIMEHSREG(m, 1, 0x0fd8)
+#define G_TIMEHS_PERIPHID7(m) G_TIMEHSREG(m, 1, 0x0fdc)
+#define G_TIMEHS_PERIPHID0(m) G_TIMEHSREG(m, 1, 0x0fe0)
+#define G_TIMEHS_PERIPHID1(m) G_TIMEHSREG(m, 1, 0x0fe4)
+#define G_TIMEHS_PERIPHID2(m) G_TIMEHSREG(m, 1, 0x0fe8)
+#define G_TIMEHS_PERIPHID3(m) G_TIMEHSREG(m, 1, 0x0fec)
+#define G_TIMEHS_PCELLID0(m) G_TIMEHSREG(m, 1, 0x0ff0)
+#define G_TIMEHS_PCELLID1(m) G_TIMEHSREG(m, 1, 0x0ff4)
+#define G_TIMEHS_PCELLID2(m) G_TIMEHSREG(m, 1, 0x0ff8)
+#define G_TIMEHS_PCELLID3(m) G_TIMEHSREG(m, 1, 0x0ffc)
+
+
+/* Oscillator */
#define G_XO0_BASE_ADDR 0x40420000
#define G_XO_OSC_RC_CAL_RSTB REG32(G_XO0_BASE_ADDR + 0x0014)
#define G_XO_OSC_RC_CAL_LOAD REG32(G_XO0_BASE_ADDR + 0x0018)