1 /* Copyright 2023 The ChromiumOS Authors
2 * SPDX-License-Identifier: Apache-2.0
3 */
4 #include <zephyr/spinlock.h>
5 #include <zephyr/init.h>
6 #include <zephyr/drivers/timer/system_timer.h>
7
8 #if defined(CONFIG_TEST)
9 const int32_t z_sys_timer_irq_for_test = DT_IRQN(DT_NODELABEL(ostimer0));
10 #endif
11
12 #define OSTIMER64_BASE DT_REG_ADDR(DT_NODELABEL(ostimer64))
13 #define OSTIMER_BASE DT_REG_ADDR(DT_NODELABEL(ostimer0))
14
15 /*
16 * This device has a LOT of timer hardware. There are SIX
17 * instantiated devices, with THREE different interfaces! Not
18 * including the three Xtensa CCOUNT timers!
19 *
20 * In practice only "ostimer0" is used as an interrupt source by the
21 * original SOF code, and the "ostimer64" and "platform" timers
22 * reflect the same underlying clock (though they're different
23 * counters with different values). There is also a "ptimer" device,
24 * which is unused by SOF and not exercised by this driver.
25 *
26 * The driver architecture itself is sort of a hybrid of what other
27 * Zephyr drivers use: there is no (or at least no documented)
28 * comparator facility. The "ostimer64" is used as the system clock,
29 * which is a 13 MHz 64 bit up-counter. But timeout interrupts are
30 * delivered by ostimers[0], which is a 32 bit (!) down-counter (!!)
31 * running at twice (!!!) the rate: 26MHz. Testing shows they're
32 * slaved the same underlying clock -- they don't skew relative to
33 * each other.
34 */
35 struct mtk_ostimer {
36 unsigned int con;
37 unsigned int rst;
38 unsigned int cur;
39 unsigned int irq_ack;
40 };
41
42 struct mtk_ostimer64 {
43 unsigned int con;
44 unsigned int init_l;
45 unsigned int init_h;
46 unsigned int cur_l;
47 unsigned int cur_h;
48 unsigned int tval_h;
49 unsigned int irq_ack;
50 };
51
52 #define OSTIMER64 (*(volatile struct mtk_ostimer64 *)OSTIMER64_BASE)
53
54 #define OSTIMERS ((volatile struct mtk_ostimer *)OSTIMER_BASE)
55
56 #define OSTIMER_CON_ENABLE BIT(0)
57 #define OSTIMER_CON_CLKSRC_MASK 0x30
58
59 #if defined(CONFIG_SOC_SERIES_MT818X)
60 #define OSTIMER_CON_CLKSRC_26M 0x00 /* 26 MHz */
61 #define OSTIMER_CON_CLKSRC_BCLK 0x10
62 #define OSTIMER_CON_CLKSRC_PCLK 0x20
63 #else
64 #define OSTIMER_CON_CLKSRC_32K 0x00 /* 32768 Hz */
65 #define OSTIMER_CON_CLKSRC_26M 0x10 /* 26 MHz */
66 #define OSTIMER_CON_CLKSRC_BCLK 0x20 /* CPU speed, 720 MHz */
67 #define OSTIMER_CON_CLKSRC_PCLK 0x30 /* ~312 MHz experimentally */
68 #endif
69
70 #define OSTIMER_IRQ_ACK_ENABLE BIT(0)
71 #define OSTIMER_IRQ_ACK_CLEAR BIT(5)
72
73 #define OST64_HZ 13000000U
74 #define OST_HZ 26000000U
75 #define OST64_PER_TICK (OST64_HZ / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
76 #define OST_PER_TICK (OST_HZ / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
77
78 #define MAX_TICKS ((0xffffffffU - OST_PER_TICK) / OST_PER_TICK)
79 #define CYC64_MAX (0xffffffff - OST64_PER_TICK)
80
81 static struct k_spinlock lock;
82
83 static uint64_t last_announce;
84
sys_clock_cycle_get_32(void)85 uint32_t sys_clock_cycle_get_32(void)
86 {
87 return OSTIMER64.cur_l;
88 }
89
sys_clock_cycle_get_64(void)90 uint64_t sys_clock_cycle_get_64(void)
91 {
92 uint32_t l, h0, h1;
93
94 do {
95 h0 = OSTIMER64.cur_h;
96 l = OSTIMER64.cur_l;
97 h1 = OSTIMER64.cur_h;
98 } while (h0 != h1);
99
100 return (((uint64_t)h0) << 32) | l;
101 }
102
sys_clock_set_timeout(int32_t ticks,bool idle)103 void sys_clock_set_timeout(int32_t ticks, bool idle)
104 {
105 /* Compute desired expiration time */
106 uint64_t now = sys_clock_cycle_get_64();
107 uint64_t end = now + CLAMP(ticks - 1, 0, MAX_TICKS) * OST64_PER_TICK;
108 uint32_t dt = (uint32_t)MIN(end - last_announce, CYC64_MAX);
109
110 /* Round up to tick boundary */
111 dt = ((dt + OST64_PER_TICK - 1) / OST64_PER_TICK) * OST64_PER_TICK;
112
113 /* Convert to "fast" OSTIMER[0] cycles! */
114 uint32_t cyc = 2 * (dt - (uint32_t)(now - last_announce));
115
116 /* Writes to RST need to be done when the device is disabled,
117 * and automatically reset CUR (which reads zero while disabled)
118 */
119 OSTIMERS[0].con &= ~OSTIMER_CON_ENABLE;
120 OSTIMERS[0].rst = cyc;
121 OSTIMERS[0].irq_ack |= OSTIMER_IRQ_ACK_CLEAR;
122 OSTIMERS[0].irq_ack |= OSTIMER_IRQ_ACK_ENABLE;
123 OSTIMERS[0].con |= OSTIMER_CON_ENABLE;
124 }
125
sys_clock_elapsed(void)126 uint32_t sys_clock_elapsed(void)
127 {
128 k_spinlock_key_t key = k_spin_lock(&lock);
129 uint32_t ret;
130
131 ret = (uint32_t)((sys_clock_cycle_get_64() - last_announce)
132 / OST64_PER_TICK);
133 k_spin_unlock(&lock, key);
134 return ret;
135 }
136
timer_isr(__maybe_unused void * arg)137 static void timer_isr(__maybe_unused void *arg)
138 {
139 /* Note: no locking. As it happens, on MT8195/8186/8188 all
140 * Zephyr-usable interrupts are delivered at the same level.
141 * So we can't be preempted and there's actually no need to
142 * take a spinlock here. But ideally we should verify/detect
143 * this instead of trusting blindly; this is fragile if future
144 * devices add nested interrupts.
145 */
146 uint64_t dcyc = sys_clock_cycle_get_64() - last_announce;
147 uint64_t ticks = dcyc / OST64_PER_TICK;
148
149 /* Leave the device disabled after clearing the interrupt,
150 * sys_clock_set_timeout() is responsible for turning it back
151 * on.
152 */
153 OSTIMERS[0].irq_ack |= OSTIMER_IRQ_ACK_CLEAR;
154 OSTIMERS[0].con &= ~OSTIMER_CON_ENABLE;
155 OSTIMERS[0].irq_ack &= ~OSTIMER_IRQ_ACK_ENABLE;
156
157 last_announce += ticks * OST64_PER_TICK;
158 sys_clock_announce(ticks);
159
160 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
161 sys_clock_set_timeout(1, false);
162 }
163 }
164
mtk_adsp_timer_init(void)165 static int mtk_adsp_timer_init(void)
166 {
167 IRQ_CONNECT(DT_IRQN(DT_NODELABEL(ostimer0)), 0, timer_isr, 0, 0);
168 irq_enable(DT_IRQN(DT_NODELABEL(ostimer0)));
169
170 /* Disable all timers */
171 for (int i = 0; i < 4; i++) {
172 OSTIMERS[i].con &= ~OSTIMER_CON_ENABLE;
173 OSTIMERS[i].irq_ack |= OSTIMER_IRQ_ACK_CLEAR;
174 OSTIMERS[i].irq_ack &= ~OSTIMER_IRQ_ACK_ENABLE;
175 }
176
177 /* Set them up to use the same clock. Note that OSTIMER64 has
178 * a built-in divide by two (or it's configurable and I don't
179 * know the register) and exposes a 13 MHz counter!
180 */
181 OSTIMERS[0].con = ((OSTIMERS[0].con & ~OSTIMER_CON_CLKSRC_MASK)
182 | OSTIMER_CON_CLKSRC_26M);
183 OSTIMERS[0].con |= OSTIMER_CON_ENABLE;
184
185 /* Clock is free running and survives reset, doesn't start at zero */
186 last_announce = sys_clock_cycle_get_64();
187
188 return 0;
189 }
190
191 SYS_INIT(mtk_adsp_timer_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
192