1/* $OpenBSD: agtimer.c,v 1.28 2023/09/17 14:50:51 cheloha Exp $ */
2/*
3 * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
4 * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/systm.h>
21#include <sys/clockintr.h>
22#include <sys/queue.h>
23#include <sys/malloc.h>
24#include <sys/device.h>
25#include <sys/kernel.h>
26#include <sys/stdint.h>
27#include <sys/timetc.h>
28#include <sys/evcount.h>
29
30#include <machine/intr.h>
31#include <machine/bus.h>
32#include <machine/fdt.h>
33
34#include <dev/ofw/fdt.h>
35#include <dev/ofw/openfirm.h>
36
37/* registers */
38#define GTIMER_CNTV_CTL_ENABLE		(1 << 0)
39#define GTIMER_CNTV_CTL_IMASK		(1 << 1)
40#define GTIMER_CNTV_CTL_ISTATUS		(1 << 2)
41
42#define TIMER_FREQUENCY		24 * 1000 * 1000 /* ARM core clock */
43int32_t agtimer_frequency = TIMER_FREQUENCY;
44
45u_int agtimer_get_timecount_default(struct timecounter *);
46u_int agtimer_get_timecount_sun50i(struct timecounter *);
47
48static struct timecounter agtimer_timecounter = {
49	.tc_get_timecount = agtimer_get_timecount_default,
50	.tc_counter_mask = 0xffffffff,
51	.tc_frequency = 0,
52	.tc_name = "agtimer",
53	.tc_quality = 0,
54	.tc_priv = NULL,
55	.tc_user = TC_AGTIMER,
56};
57
58struct agtimer_softc {
59	struct device		sc_dev;
60	int			sc_node;
61
62	u_int32_t		sc_ticks_per_second;
63	uint64_t		sc_nsec_cycle_ratio;
64	uint64_t		sc_nsec_max;
65	void			*sc_ih;
66};
67
68int		agtimer_match(struct device *, void *, void *);
69void		agtimer_attach(struct device *, struct device *, void *);
70int		agtimer_intr(void *);
71void		agtimer_cpu_initclocks(void);
72void		agtimer_delay(u_int);
73void		agtimer_rearm(void *, uint64_t);
74void		agtimer_setstatclockrate(int stathz);
75void		agtimer_set_clockrate(int32_t new_frequency);
76void		agtimer_startclock(void);
77void		agtimer_trigger(void *);
78
79const struct cfattach agtimer_ca = {
80	sizeof (struct agtimer_softc), agtimer_match, agtimer_attach
81};
82
83struct cfdriver agtimer_cd = {
84	NULL, "agtimer", DV_DULL
85};
86
87struct intrclock agtimer_intrclock = {
88	.ic_rearm = agtimer_rearm,
89	.ic_trigger = agtimer_trigger
90};
91
92uint64_t
93agtimer_readcnt64_default(void)
94{
95	uint64_t val0, val1;
96
97	/*
98	 * Work around Cortex-A73 errata 858921, where there is a
99	 * one-cycle window where the read might return the old value
100	 * for the low 32 bits and the new value for the high 32 bits
101	 * upon roll-over of the low 32 bits.
102	 */
103	__asm volatile("isb" ::: "memory");
104	__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val0));
105	__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val1));
106	return ((val0 ^ val1) & 0x100000000ULL) ? val0 : val1;
107}
108
109uint64_t
110agtimer_readcnt64_sun50i(void)
111{
112	uint64_t val;
113	int retry;
114
115	__asm volatile("isb" ::: "memory");
116	for (retry = 0; retry < 150; retry++) {
117		__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val));
118
119		if (((val + 1) & 0x1ff) > 1)
120			break;
121	}
122	KASSERT(retry < 150);
123
124	return val;
125}
126
127uint64_t (*agtimer_readcnt64)(void) = agtimer_readcnt64_default;
128
129static inline uint64_t
130agtimer_get_freq(void)
131{
132	uint64_t val;
133
134	__asm volatile("mrs %x0, CNTFRQ_EL0" : "=r" (val));
135
136	return (val);
137}
138
139static inline int
140agtimer_get_ctrl(void)
141{
142	uint32_t val;
143
144	__asm volatile("mrs %x0, CNTV_CTL_EL0" : "=r" (val));
145
146	return (val);
147}
148
149static inline int
150agtimer_set_ctrl(uint32_t val)
151{
152	__asm volatile("msr CNTV_CTL_EL0, %x0" :: "r" (val));
153	__asm volatile("isb" ::: "memory");
154
155	return (0);
156}
157
158static inline int
159agtimer_set_tval(uint32_t val)
160{
161	__asm volatile("msr CNTV_TVAL_EL0, %x0" :: "r" (val));
162	__asm volatile("isb" ::: "memory");
163
164	return (0);
165}
166
167int
168agtimer_match(struct device *parent, void *cfdata, void *aux)
169{
170	struct fdt_attach_args *faa = (struct fdt_attach_args *)aux;
171
172	return (OF_is_compatible(faa->fa_node, "arm,armv7-timer") ||
173	    OF_is_compatible(faa->fa_node, "arm,armv8-timer"));
174}
175
176void
177agtimer_attach(struct device *parent, struct device *self, void *aux)
178{
179	struct agtimer_softc *sc = (struct agtimer_softc *)self;
180	struct fdt_attach_args *faa = aux;
181
182	sc->sc_node = faa->fa_node;
183
184	if (agtimer_get_freq() != 0)
185		agtimer_frequency = agtimer_get_freq();
186	agtimer_frequency =
187	    OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency);
188	sc->sc_ticks_per_second = agtimer_frequency;
189	sc->sc_nsec_cycle_ratio =
190	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
191	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
192
193	printf(": %u kHz\n", sc->sc_ticks_per_second / 1000);
194
195	/*
196	 * The Allwinner A64 has an erratum where the bottom 9 bits of
197	 * the counter register can't be trusted if any of the higher
198	 * bits are rolling over.
199	 */
200	if (OF_getpropbool(sc->sc_node, "allwinner,erratum-unknown1")) {
201		agtimer_readcnt64 = agtimer_readcnt64_sun50i;
202		agtimer_timecounter.tc_get_timecount =
203		    agtimer_get_timecount_sun50i;
204		agtimer_timecounter.tc_user = TC_AGTIMER_SUN50I;
205	}
206
207	/*
208	 * private timer and interrupts not enabled until
209	 * timer configures
210	 */
211
212	arm_clock_register(agtimer_cpu_initclocks, agtimer_delay,
213	    agtimer_setstatclockrate, agtimer_startclock);
214
215	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
216	agtimer_timecounter.tc_priv = sc;
217	tc_init(&agtimer_timecounter);
218
219	agtimer_intrclock.ic_cookie = sc;
220}
221
222u_int
223agtimer_get_timecount_default(struct timecounter *tc)
224{
225	uint64_t val;
226
227	/*
228	 * No need to work around Cortex-A73 errata 858921 since we
229	 * only look at the low 32 bits here.
230	 */
231	__asm volatile("isb" ::: "memory");
232	__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val));
233	return (val & 0xffffffff);
234}
235
236u_int
237agtimer_get_timecount_sun50i(struct timecounter *tc)
238{
239	return agtimer_readcnt64_sun50i();
240}
241
242void
243agtimer_rearm(void *cookie, uint64_t nsecs)
244{
245	struct agtimer_softc *sc = cookie;
246	uint32_t cycles;
247
248	if (nsecs > sc->sc_nsec_max)
249		nsecs = sc->sc_nsec_max;
250	cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32;
251	if (cycles > INT32_MAX)
252		cycles = INT32_MAX;
253	agtimer_set_tval(cycles);
254}
255
256void
257agtimer_trigger(void *unused)
258{
259	agtimer_set_tval(0);
260}
261
262int
263agtimer_intr(void *frame)
264{
265	return clockintr_dispatch(frame);
266}
267
268void
269agtimer_set_clockrate(int32_t new_frequency)
270{
271	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
272
273	agtimer_frequency = new_frequency;
274
275	if (sc == NULL)
276		return;
277
278	sc->sc_ticks_per_second = agtimer_frequency;
279	sc->sc_nsec_cycle_ratio =
280	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
281	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
282
283	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
284
285	printf("agtimer0: adjusting clock: new tick rate %u kHz\n",
286	    sc->sc_ticks_per_second / 1000);
287}
288
289void
290agtimer_cpu_initclocks(void)
291{
292	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
293
294	stathz = hz;
295	profhz = stathz * 10;
296	statclock_is_randomized = 1;
297
298	if (sc->sc_ticks_per_second != agtimer_frequency) {
299		agtimer_set_clockrate(agtimer_frequency);
300	}
301
302	/* configure virtual timer interrupt */
303	sc->sc_ih = arm_intr_establish_fdt_idx(sc->sc_node, 2,
304	    IPL_CLOCK|IPL_MPSAFE, agtimer_intr, NULL, "tick");
305}
306
307void
308agtimer_delay(u_int usecs)
309{
310	uint64_t cycles, start;
311
312	start = agtimer_readcnt64();
313	cycles = (uint64_t)usecs * agtimer_frequency / 1000000;
314	while (agtimer_readcnt64() - start < cycles)
315		CPU_BUSY_CYCLE();
316}
317
318void
319agtimer_setstatclockrate(int newhz)
320{
321}
322
323void
324agtimer_startclock(void)
325{
326	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
327	uint64_t kctl;
328	uint32_t reg;
329
330	if (!CPU_IS_PRIMARY(curcpu()))
331		arm_intr_route(sc->sc_ih, 1, curcpu());
332
333	clockintr_cpu_init(&agtimer_intrclock);
334
335	reg = agtimer_get_ctrl();
336	reg &= ~GTIMER_CNTV_CTL_IMASK;
337	reg |= GTIMER_CNTV_CTL_ENABLE;
338	agtimer_set_tval(INT32_MAX);
339	agtimer_set_ctrl(reg);
340
341	clockintr_trigger();
342
343	/* enable userland access to virtual counter */
344	kctl = READ_SPECIALREG(CNTKCTL_EL1);
345	WRITE_SPECIALREG(CNTKCTL_EL1, kctl | CNTKCTL_EL0VCTEN);
346}
347
348void
349agtimer_init(void)
350{
351	uint64_t cntfrq = 0;
352
353	/* XXX: Check for Generic Timer support. */
354	cntfrq = agtimer_get_freq();
355
356	if (cntfrq != 0) {
357		agtimer_frequency = cntfrq;
358		arm_clock_register(NULL, agtimer_delay, NULL, NULL);
359	}
360}
361