summaryrefslogtreecommitdiff
path: root/chip/max32660/hwtimer_chip.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/max32660/hwtimer_chip.c')
-rw-r--r--chip/max32660/hwtimer_chip.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/chip/max32660/hwtimer_chip.c b/chip/max32660/hwtimer_chip.c
new file mode 100644
index 0000000000..b2f0643e0f
--- /dev/null
+++ b/chip/max32660/hwtimer_chip.c
@@ -0,0 +1,226 @@
+/* Copyright 2019 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.
+ */
+
+/* MAX32660 HW Timer module for Chrome EC */
+
+#include "clock.h"
+#include "console.h"
+#include "common.h"
+#include "hooks.h"
+#include "hwtimer.h"
+#include "task.h"
+#include "timer.h"
+#include "registers.h"
+#include "tmr_regs.h"
+#include "gcr_regs.h"
+
+/* Define the rollover timer */
+#define TMR_ROLLOVER MXC_TMR0
+#define TMR_ROLLOVER_IRQ EC_TMR0_IRQn
+
+/* Define the event timer */
+#define TMR_EVENT MXC_TMR1
+#define TMR_EVENT_IRQ EC_TMR1_IRQn
+
+#define ROLLOVER_EVENT 1
+#define NOT_ROLLOVER_EVENT 0
+
+#define TMR_PRESCALER MXC_V_TMR_CN_PRES_DIV8
+#define TMR_DIV (1 << TMR_PRESCALER)
+
+/* The frequency of timer using the prescaler */
+#define TIMER_FREQ_HZ (PeripheralClock / TMR_DIV)
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_SYSTEM, outstr)
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args)
+
+static uint32_t last_deadline;
+
+/* brief Timer prescaler values */
+enum tmr_pres {
+ TMR_PRES_1 = MXC_V_TMR_CN_PRES_DIV1, /// Divide input clock by 1
+ TMR_PRES_2 = MXC_V_TMR_CN_PRES_DIV2, /// Divide input clock by 2
+ TMR_PRES_4 = MXC_V_TMR_CN_PRES_DIV4, /// Divide input clock by 4
+ TMR_PRES_8 = MXC_V_TMR_CN_PRES_DIV8, /// Divide input clock by 8
+ TMR_PRES_16 = MXC_V_TMR_CN_PRES_DIV16, /// Divide input clock by 16
+ TMR_PRES_32 = MXC_V_TMR_CN_PRES_DIV32, /// Divide input clock by 32
+ TMR_PRES_64 = MXC_V_TMR_CN_PRES_DIV64, /// Divide input clock by 64
+ TMR_PRES_128 = MXC_V_TMR_CN_PRES_DIV128, /// Divide input clock by 128
+ TMR_PRES_256 =
+ (0x20 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock by 256
+ TMR_PRES_512 =
+ (0x21 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock by 512
+ TMR_PRES_1024 =
+ (0x22 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock by 1024
+ TMR_PRES_2048 =
+ (0x23 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock by 2048
+ TMR_PRES_4096 =
+ (0x24 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock by 4096
+};
+
+/* Timer modes */
+enum tmr_mode {
+ TMR_MODE_ONESHOT = MXC_V_TMR_CN_TMODE_ONESHOT, /// Timer Mode ONESHOT
+ TMR_MODE_CONTINUOUS =
+ MXC_V_TMR_CN_TMODE_CONTINUOUS, /// Timer Mode CONTINUOUS
+ TMR_MODE_COUNTER = MXC_V_TMR_CN_TMODE_COUNTER, /// Timer Mode COUNTER
+ TMR_MODE_PWM = MXC_V_TMR_CN_TMODE_PWM, /// Timer Mode PWM
+ TMR_MODE_CAPTURE = MXC_V_TMR_CN_TMODE_CAPTURE, /// Timer Mode CAPTURE
+ TMR_MODE_COMPARE = MXC_V_TMR_CN_TMODE_COMPARE, /// Timer Mode COMPARE
+ TMR_MODE_GATED = MXC_V_TMR_CN_TMODE_GATED, /// Timer Mode GATED
+ TMR_MODE_CAPTURE_COMPARE =
+ MXC_V_TMR_CN_TMODE_CAPTURECOMPARE /// Timer Mode CAPTURECOMPARE
+};
+
+/*
+ * Calculate the number of microseconds for a given timer tick
+ */
+static inline uint32_t ticks_to_usecs(uint32_t ticks)
+{
+ return (uint64_t)ticks * SECOND / TIMER_FREQ_HZ;
+}
+
+/*
+ * Calculate the number of timer ticks for a given microsecond value
+ */
+static inline uint32_t usecs_to_ticks(uint32_t usecs)
+{
+ return ((uint64_t)(usecs)*TIMER_FREQ_HZ / SECOND);
+}
+
+void __hw_clock_event_set(uint32_t deadline)
+{
+ uint32_t event_time_us;
+ uint32_t event_time_ticks;
+ uint32_t time_now;
+
+ last_deadline = deadline;
+ time_now = __hw_clock_source_read();
+
+ /* check if the deadline has rolled over */
+ if (deadline < time_now) {
+ event_time_us = (0xFFFFFFFF - time_now) + deadline;
+ } else {
+ /* How long from the current time to the deadline? */
+ event_time_us = (deadline - __hw_clock_source_read());
+ }
+
+ /* Convert event_time to ticks rounding up */
+ event_time_ticks = usecs_to_ticks(event_time_us) + 1;
+
+ /* set the event time into the timer compare */
+ TMR_EVENT->cmp = event_time_ticks;
+ /* zero out the timer */
+ TMR_EVENT->cnt = 0;
+ TMR_EVENT->cn |= MXC_F_TMR_CN_TEN;
+}
+
+uint32_t __hw_clock_event_get(void)
+{
+ return last_deadline;
+}
+
+void __hw_clock_event_clear(void)
+{
+ TMR_EVENT->cn &= ~(MXC_F_TMR_CN_TEN);
+}
+
+uint32_t __hw_clock_source_read(void)
+{
+ uint32_t timer_count_ticks;
+
+ /* Read the timer value and return the results in microseconds */
+ timer_count_ticks = TMR_ROLLOVER->cnt;
+ return ticks_to_usecs(timer_count_ticks);
+}
+
+void __hw_clock_source_set(uint32_t ts)
+{
+ uint32_t timer_count_ticks;
+ timer_count_ticks = usecs_to_ticks(ts);
+ TMR_ROLLOVER->cnt = timer_count_ticks;
+}
+
+/**
+ * Interrupt handler for Timer
+ */
+static void __timer_event_isr(void)
+{
+ /* Clear the event timer */
+ TMR_EVENT->intr = MXC_F_TMR_INTR_IRQ_CLR;
+ /* Process the timer, pass in that this was NOT a rollover event */
+ if (TMR_ROLLOVER->intr) {
+ TMR_ROLLOVER->intr = MXC_F_TMR_INTR_IRQ_CLR;
+ process_timers(ROLLOVER_EVENT);
+ } else {
+ process_timers(NOT_ROLLOVER_EVENT);
+ }
+}
+DECLARE_IRQ(EC_TMR1_IRQn, __timer_event_isr, 1);
+
+static void init_timer(mxc_tmr_regs_t *timer, enum tmr_pres prescaler,
+ enum tmr_mode mode, uint32_t count)
+{
+ /* Disable the Timer */
+ timer->cn &= ~(MXC_F_TMR_CN_TEN);
+
+ if (timer == MXC_TMR0) {
+ /* Enable Timer 0 Clock */
+ MXC_GCR->perckcn0 &= ~(MXC_F_GCR_PERCKCN0_T0D);
+ } else if (timer == MXC_TMR1) {
+ /* Enable Timer 1 Clock */
+ MXC_GCR->perckcn0 &= ~(MXC_F_GCR_PERCKCN0_T1D);
+ } else if (timer == MXC_TMR2) {
+ /* Enable Timer 2 Clock */
+ MXC_GCR->perckcn0 &= ~(MXC_F_GCR_PERCKCN0_T2D);
+ }
+
+ /* Disable timer and clear settings */
+ timer->cn = 0;
+
+ /* Clear interrupt flag */
+ timer->intr = MXC_F_TMR_INTR_IRQ_CLR;
+
+ /* Set the prescaler */
+ timer->cn = (prescaler << MXC_F_TMR_CN_PRES_POS);
+
+ /* Configure the timer */
+ timer->cn = (timer->cn & ~(MXC_F_TMR_CN_TMODE | MXC_F_TMR_CN_TPOL)) |
+ ((mode << MXC_F_TMR_CN_TMODE_POS) & MXC_F_TMR_CN_TMODE) |
+ ((0 << MXC_F_TMR_CN_TPOL_POS) & MXC_F_TMR_CN_TPOL);
+
+ timer->cnt = 0x1;
+ timer->cmp = count;
+}
+
+int __hw_clock_source_init(uint32_t start_t)
+{
+ /* Initialize two timers, one for the OS Rollover and one for the OS
+ * Events */
+ init_timer(TMR_ROLLOVER, TMR_PRESCALER, TMR_MODE_CONTINUOUS,
+ 0xffffffff);
+ init_timer(TMR_EVENT, TMR_PRESCALER, TMR_MODE_COMPARE, 0x0);
+ __hw_clock_source_set(start_t);
+
+ /* Enable the timers */
+ TMR_ROLLOVER->cn |= MXC_F_TMR_CN_TEN;
+ TMR_EVENT->cn |= MXC_F_TMR_CN_TEN;
+
+ /* Enable the IRQ */
+ task_enable_irq(TMR_EVENT_IRQ);
+
+ /* Return the Event timer IRQ number (NOT the Rollover IRQ) */
+ return TMR_EVENT_IRQ;
+}
+
+static int hwtimer_display(int argc, char **argv)
+{
+ CPRINTS(" TMR_EVENT count 0x%08x", TMR_EVENT->cnt);
+ CPRINTS(" TMR_ROLLOVER count 0x%08x", TMR_ROLLOVER->cnt);
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(hwtimer, hwtimer_display, "hwtimer",
+ "Display hwtimer counts");