diff options
author | Tzung-Bi Shih <tzungbi@chromium.org> | 2021-06-24 15:05:32 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-06-25 03:42:23 +0000 |
commit | d18d88b3576bd159e26753321d8fb5e2e280d42a (patch) | |
tree | cc6d37c5a8b5eede4adbfda7bb9826a158eed88e /chip/mt_scp/mt8183/hrtimer.c | |
parent | fd85931c6db4d236917d6fadcff2ccfd48109ab0 (diff) | |
download | chrome-ec-d18d88b3576bd159e26753321d8fb5e2e280d42a.tar.gz |
chip/mt_scp: move mt8183 specific to sub-folder
BRANCH=none
BUG=b:191835814
TEST=make BOARD=kukui_scp
Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org>
Change-Id: Ic8387200a741a4e7ef99e13772231a0ec0bc1fc1
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2984255
Reviewed-by: Eric Yilun Lin <yllin@google.com>
Diffstat (limited to 'chip/mt_scp/mt8183/hrtimer.c')
-rw-r--r-- | chip/mt_scp/mt8183/hrtimer.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/chip/mt_scp/mt8183/hrtimer.c b/chip/mt_scp/mt8183/hrtimer.c new file mode 100644 index 0000000000..f970af6eb5 --- /dev/null +++ b/chip/mt_scp/mt8183/hrtimer.c @@ -0,0 +1,253 @@ +/* Copyright 2018 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. + */ + +/* + * High-res hardware timer + * + * SCP hardware 32bit count down timer can be configured to source clock from + * 32KHz, 26MHz, BCLK or PCLK. This implementation selects BCLK (ULPOSC1/8) as a + * source, countdown mode and converts to micro second value matching common + * timer. + */ + +#include "clock.h" +#include "clock_chip.h" +#include "common.h" +#include "console.h" +#include "hooks.h" +#include "hwtimer.h" +#include "panic.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "watchdog.h" + +#define IRQ_TIMER(n) CONCAT2(SCP_IRQ_TIMER, n) + +#define TIMER_SYSTEM 5 +#define TIMER_EVENT 3 + +/* ULPOSC1 should be a multiple of 8. */ +BUILD_ASSERT((ULPOSC1_CLOCK_MHZ % 8) == 0); +#define TIMER_CLOCK_MHZ (ULPOSC1_CLOCK_MHZ / 8) + +/* Common timer overflows at 0x100000000 micro seconds */ +#define OVERFLOW_TICKS (TIMER_CLOCK_MHZ * 0x100000000 - 1) + +static uint8_t sys_high; +static uint8_t event_high; + +/* Convert hardware countdown timer to 64bit countup ticks */ +static inline uint64_t timer_read_raw_system(void) +{ + uint32_t timer_ctrl = SCP_TIMER_IRQ_CTRL(TIMER_SYSTEM); + uint32_t sys_high_adj = sys_high; + + /* + * If an IRQ is pending, but has not been serviced yet, adjust the + * sys_high value. + */ + if (timer_ctrl & TIMER_IRQ_STATUS) + sys_high_adj = sys_high ? (sys_high - 1) : (TIMER_CLOCK_MHZ-1); + + return OVERFLOW_TICKS - (((uint64_t)sys_high_adj << 32) | + SCP_TIMER_VAL(TIMER_SYSTEM)); +} + +static inline uint64_t timer_read_raw_event(void) +{ + return OVERFLOW_TICKS - (((uint64_t)event_high << 32) | + SCP_TIMER_VAL(TIMER_EVENT)); +} + +static inline void timer_set_clock(int n, uint32_t clock_source) +{ + SCP_TIMER_EN(n) = (SCP_TIMER_EN(n) & ~TIMER_CLK_MASK) | + clock_source; +} + +static inline void timer_ack_irq(int n) +{ + SCP_TIMER_IRQ_CTRL(n) |= TIMER_IRQ_CLEAR; +} + +/* Set hardware countdown value */ +static inline void timer_set_reset_value(int n, uint32_t reset_value) +{ + SCP_TIMER_RESET_VAL(n) = reset_value; +} + +static void timer_reset(int n) +{ + __hw_timer_enable_clock(n, 0); + timer_ack_irq(n); + timer_set_reset_value(n, 0xffffffff); + timer_set_clock(n, TIMER_CLK_32K); +} + +/* Reload a new 32bit countdown value */ +static void timer_reload(int n, uint32_t value) +{ + __hw_timer_enable_clock(n, 0); + timer_set_reset_value(n, value); + __hw_timer_enable_clock(n, 1); +} + +static int timer_reload_event_high(void) +{ + if (event_high) { + if (SCP_TIMER_RESET_VAL(TIMER_EVENT) == 0xffffffff) + __hw_timer_enable_clock(TIMER_EVENT, 1); + else + timer_reload(TIMER_EVENT, 0xffffffff); + event_high--; + return 1; + } + + /* Disable event timer clock when done. */ + __hw_timer_enable_clock(TIMER_EVENT, 0); + return 0; +} + +void __hw_clock_event_clear(void) +{ + __hw_timer_enable_clock(TIMER_EVENT, 0); + timer_set_reset_value(TIMER_EVENT, 0x0000c1ea4); + event_high = 0; +} + +void __hw_clock_event_set(uint32_t deadline) +{ + uint64_t deadline_raw = (uint64_t)deadline * TIMER_CLOCK_MHZ; + uint64_t now_raw = timer_read_raw_system(); + uint32_t event_deadline; + + if (deadline_raw > now_raw) { + deadline_raw -= now_raw; + event_deadline = (uint32_t)deadline_raw; + event_high = deadline_raw >> 32; + } else { + event_deadline = 1; + event_high = 0; + } + + if (event_deadline) + timer_reload(TIMER_EVENT, event_deadline); + else + timer_reload_event_high(); +} + +void __hw_timer_enable_clock(int n, int enable) +{ + if (enable) { + SCP_TIMER_IRQ_CTRL(n) |= 1; + SCP_TIMER_EN(n) |= 1; + } else { + SCP_TIMER_EN(n) &= ~1; + SCP_TIMER_IRQ_CTRL(n) &= ~1; + } +} + +int __hw_clock_source_init(uint32_t start_t) +{ + int t; + + /* + * TODO(b/120169529): check clock tree to see if we need to turn on + * MCLK and BCLK gate. + */ + SCP_CLK_GATE |= (CG_TIMER_M | CG_TIMER_B); + + /* Reset all timer, select 32768Hz clock source */ + for (t = 0; t < NUM_TIMERS; t++) + timer_reset(t); + + /* Enable timer IRQ wake source */ + SCP_INTC_IRQ_WAKEUP |= (1 << IRQ_TIMER(0)) | (1 << IRQ_TIMER(1)) | + (1 << IRQ_TIMER(2)) | (1 << IRQ_TIMER(3)) | + (1 << IRQ_TIMER(4)) | (1 << IRQ_TIMER(5)); + /* + * Timer configuration: + * OS TIMER - count up @ 13MHz, 64bit value with latch. + * SYS TICK - count down @ 26MHz + * EVENT TICK - count down @ 26MHz + */ + + /* Turn on OS TIMER, tick at 13MHz */ + SCP_OSTIMER_CON |= 1; + + /* System timestamp timer from BCLK (sourced from ULPOSC) */ + SCP_CLK_BCLK = CLK_BCLK_SEL_ULPOSC1_DIV8; + + timer_set_clock(TIMER_SYSTEM, TIMER_CLK_BCLK); + sys_high = TIMER_CLOCK_MHZ-1; + timer_set_reset_value(TIMER_SYSTEM, 0xffffffff); + __hw_timer_enable_clock(TIMER_SYSTEM, 1); + task_enable_irq(IRQ_TIMER(TIMER_SYSTEM)); + /* Event tick timer */ + timer_set_clock(TIMER_EVENT, TIMER_CLK_BCLK); + task_enable_irq(IRQ_TIMER(TIMER_EVENT)); + + return IRQ_TIMER(TIMER_SYSTEM); +} + +uint32_t __hw_clock_source_read(void) +{ + return timer_read_raw_system() / TIMER_CLOCK_MHZ; +} + +uint32_t __hw_clock_event_get(void) +{ + return (timer_read_raw_event() + timer_read_raw_system()) + / TIMER_CLOCK_MHZ; +} + +static void __hw_clock_source_irq(int n) +{ + uint32_t timer_ctrl = SCP_TIMER_IRQ_CTRL(n); + + /* Ack if we're hardware interrupt */ + if (timer_ctrl & TIMER_IRQ_STATUS) + timer_ack_irq(n); + + switch (n) { + case TIMER_EVENT: + if (timer_ctrl & TIMER_IRQ_STATUS) { + if (timer_reload_event_high()) + return; + } + process_timers(0); + break; + case TIMER_SYSTEM: + /* If this is a hardware irq, check overflow */ + if (timer_ctrl & TIMER_IRQ_STATUS) { + if (sys_high) { + sys_high--; + process_timers(0); + } else { + /* Overflow, reload system timer */ + sys_high = TIMER_CLOCK_MHZ-1; + process_timers(1); + } + } else { + process_timers(0); + } + break; + default: + return; + } + +} + +#define DECLARE_TIMER_IRQ(n) \ + void __hw_clock_source_irq_##n(void) { __hw_clock_source_irq(n); } \ + DECLARE_IRQ(IRQ_TIMER(n), __hw_clock_source_irq_##n, 2) + +DECLARE_TIMER_IRQ(0); +DECLARE_TIMER_IRQ(1); +DECLARE_TIMER_IRQ(2); +DECLARE_TIMER_IRQ(3); +DECLARE_TIMER_IRQ(4); +DECLARE_TIMER_IRQ(5); |