summaryrefslogtreecommitdiff
path: root/chip/mt_scp/mt8183/hrtimer.c
diff options
context:
space:
mode:
authorTzung-Bi Shih <tzungbi@chromium.org>2021-06-24 15:05:32 +0800
committerCommit Bot <commit-bot@chromium.org>2021-06-25 03:42:23 +0000
commitd18d88b3576bd159e26753321d8fb5e2e280d42a (patch)
treecc6d37c5a8b5eede4adbfda7bb9826a158eed88e /chip/mt_scp/mt8183/hrtimer.c
parentfd85931c6db4d236917d6fadcff2ccfd48109ab0 (diff)
downloadchrome-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.c253
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);