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