diff options
Diffstat (limited to 'chip/max32660/hwtimer_chip.c')
-rw-r--r-- | chip/max32660/hwtimer_chip.c | 226 |
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"); |