1*0ed1bf01Scheloha /* $OpenBSD: agtimer.c,v 1.21 2023/09/17 14:50:51 cheloha Exp $ */
2466195a2Spatrick /*
3466195a2Spatrick * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
4466195a2Spatrick * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
5466195a2Spatrick *
6466195a2Spatrick * Permission to use, copy, modify, and distribute this software for any
7466195a2Spatrick * purpose with or without fee is hereby granted, provided that the above
8466195a2Spatrick * copyright notice and this permission notice appear in all copies.
9466195a2Spatrick *
10466195a2Spatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11466195a2Spatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12466195a2Spatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13466195a2Spatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14466195a2Spatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15466195a2Spatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16466195a2Spatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17466195a2Spatrick */
18466195a2Spatrick
19466195a2Spatrick #include <sys/param.h>
20466195a2Spatrick #include <sys/systm.h>
2118cf9b3aScheloha #include <sys/clockintr.h>
22466195a2Spatrick #include <sys/device.h>
23466195a2Spatrick #include <sys/kernel.h>
2418cf9b3aScheloha #include <sys/stdint.h>
25466195a2Spatrick #include <sys/timetc.h>
26466195a2Spatrick
27466195a2Spatrick #include <machine/intr.h>
2824e5cb75Skettenis #include <machine/bus.h>
2924e5cb75Skettenis #include <machine/fdt.h>
3024e5cb75Skettenis
3124e5cb75Skettenis #include <arm/cpufunc.h>
32466195a2Spatrick
3324e5cb75Skettenis #include <dev/ofw/fdt.h>
3424e5cb75Skettenis #include <dev/ofw/openfirm.h>
3524e5cb75Skettenis
36466195a2Spatrick /* registers */
37466195a2Spatrick #define GTIMER_CNTP_CTL_ENABLE (1 << 0)
38466195a2Spatrick #define GTIMER_CNTP_CTL_IMASK (1 << 1)
39466195a2Spatrick #define GTIMER_CNTP_CTL_ISTATUS (1 << 2)
40466195a2Spatrick
41466195a2Spatrick #define TIMER_FREQUENCY 24 * 1000 * 1000 /* ARM core clock */
42466195a2Spatrick int32_t agtimer_frequency = TIMER_FREQUENCY;
43466195a2Spatrick
44466195a2Spatrick u_int agtimer_get_timecount(struct timecounter *);
45466195a2Spatrick
46466195a2Spatrick static struct timecounter agtimer_timecounter = {
478611d3cdScheloha .tc_get_timecount = agtimer_get_timecount,
488611d3cdScheloha .tc_counter_mask = 0xffffffff,
498611d3cdScheloha .tc_frequency = 0,
508611d3cdScheloha .tc_name = "agtimer",
518611d3cdScheloha .tc_quality = 0,
528611d3cdScheloha .tc_priv = NULL,
53466195a2Spatrick };
54466195a2Spatrick
55466195a2Spatrick struct agtimer_softc {
56466195a2Spatrick struct device sc_dev;
5724e5cb75Skettenis int sc_node;
58466195a2Spatrick u_int32_t sc_ticks_per_second;
5918cf9b3aScheloha uint64_t sc_nsec_cycle_ratio;
6018cf9b3aScheloha uint64_t sc_nsec_max;
61466195a2Spatrick };
62466195a2Spatrick
63466195a2Spatrick int agtimer_match(struct device *, void *, void *);
64466195a2Spatrick void agtimer_attach(struct device *, struct device *, void *);
6524e5cb75Skettenis uint64_t agtimer_readcnt64(void);
66466195a2Spatrick int agtimer_intr(void *);
67466195a2Spatrick void agtimer_cpu_initclocks(void);
68466195a2Spatrick void agtimer_delay(u_int);
69466195a2Spatrick void agtimer_setstatclockrate(int stathz);
70466195a2Spatrick void agtimer_set_clockrate(int32_t new_frequency);
71466195a2Spatrick void agtimer_startclock(void);
72466195a2Spatrick
73e3ee5e84Smpi const struct cfattach agtimer_ca = {
74466195a2Spatrick sizeof (struct agtimer_softc), agtimer_match, agtimer_attach
75466195a2Spatrick };
76466195a2Spatrick
77466195a2Spatrick struct cfdriver agtimer_cd = {
78466195a2Spatrick NULL, "agtimer", DV_DULL
79466195a2Spatrick };
80466195a2Spatrick
8118cf9b3aScheloha void agtimer_rearm(void *, uint64_t);
8218cf9b3aScheloha void agtimer_trigger(void *);
8318cf9b3aScheloha
8418cf9b3aScheloha struct intrclock agtimer_intrclock = {
8518cf9b3aScheloha .ic_rearm = agtimer_rearm,
8618cf9b3aScheloha .ic_trigger = agtimer_trigger
8718cf9b3aScheloha };
8818cf9b3aScheloha
89466195a2Spatrick uint64_t
agtimer_readcnt64(void)9024e5cb75Skettenis agtimer_readcnt64(void)
91466195a2Spatrick {
92466195a2Spatrick uint64_t val;
93466195a2Spatrick
94466195a2Spatrick __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val));
95466195a2Spatrick
96466195a2Spatrick return (val);
97466195a2Spatrick }
98466195a2Spatrick
99466195a2Spatrick static inline int
agtimer_get_ctrl(void)100466195a2Spatrick agtimer_get_ctrl(void)
101466195a2Spatrick {
102466195a2Spatrick uint32_t val;
103466195a2Spatrick
104466195a2Spatrick __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
105466195a2Spatrick
106466195a2Spatrick return (val);
107466195a2Spatrick }
108466195a2Spatrick
109466195a2Spatrick static inline int
agtimer_set_ctrl(uint32_t val)110466195a2Spatrick agtimer_set_ctrl(uint32_t val)
111466195a2Spatrick {
112466195a2Spatrick __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : :
113466195a2Spatrick [val] "r" (val));
114466195a2Spatrick
115466195a2Spatrick cpu_drain_writebuf();
116466195a2Spatrick //isb();
117466195a2Spatrick
118466195a2Spatrick return (0);
119466195a2Spatrick }
120466195a2Spatrick
121466195a2Spatrick static inline int
agtimer_set_tval(uint32_t val)122466195a2Spatrick agtimer_set_tval(uint32_t val)
123466195a2Spatrick {
124466195a2Spatrick __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : :
125466195a2Spatrick [val] "r" (val));
126466195a2Spatrick cpu_drain_writebuf();
127466195a2Spatrick //isb();
128466195a2Spatrick
129466195a2Spatrick return (0);
130466195a2Spatrick }
131466195a2Spatrick
132466195a2Spatrick int
agtimer_match(struct device * parent,void * cfdata,void * aux)133466195a2Spatrick agtimer_match(struct device *parent, void *cfdata, void *aux)
134466195a2Spatrick {
13524e5cb75Skettenis struct fdt_attach_args *faa = (struct fdt_attach_args *)aux;
136466195a2Spatrick
13724e5cb75Skettenis return OF_is_compatible(faa->fa_node, "arm,armv7-timer");
138466195a2Spatrick }
139466195a2Spatrick
140466195a2Spatrick void
agtimer_attach(struct device * parent,struct device * self,void * aux)14124e5cb75Skettenis agtimer_attach(struct device *parent, struct device *self, void *aux)
142466195a2Spatrick {
143466195a2Spatrick struct agtimer_softc *sc = (struct agtimer_softc *)self;
14424e5cb75Skettenis struct fdt_attach_args *faa = aux;
145466195a2Spatrick
14624e5cb75Skettenis sc->sc_node = faa->fa_node;
14724e5cb75Skettenis
14824e5cb75Skettenis agtimer_frequency =
14924e5cb75Skettenis OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency);
150466195a2Spatrick sc->sc_ticks_per_second = agtimer_frequency;
15118cf9b3aScheloha sc->sc_nsec_cycle_ratio =
15218cf9b3aScheloha sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
15318cf9b3aScheloha sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
1542252d02cSkettenis printf(": %d kHz\n", sc->sc_ticks_per_second / 1000);
155466195a2Spatrick
156466195a2Spatrick /* XXX: disable user access */
157466195a2Spatrick
158466195a2Spatrick /*
159466195a2Spatrick * private timer and interrupts not enabled until
160466195a2Spatrick * timer configures
161466195a2Spatrick */
162466195a2Spatrick
163466195a2Spatrick arm_clock_register(agtimer_cpu_initclocks, agtimer_delay,
164466195a2Spatrick agtimer_setstatclockrate, agtimer_startclock);
165466195a2Spatrick
166466195a2Spatrick agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
167466195a2Spatrick agtimer_timecounter.tc_priv = sc;
168466195a2Spatrick tc_init(&agtimer_timecounter);
16918cf9b3aScheloha
17018cf9b3aScheloha agtimer_intrclock.ic_cookie = sc;
171466195a2Spatrick }
172466195a2Spatrick
173466195a2Spatrick u_int
agtimer_get_timecount(struct timecounter * tc)174466195a2Spatrick agtimer_get_timecount(struct timecounter *tc)
175466195a2Spatrick {
17624e5cb75Skettenis return agtimer_readcnt64();
177466195a2Spatrick }
178466195a2Spatrick
17918cf9b3aScheloha void
agtimer_rearm(void * cookie,uint64_t nsecs)18018cf9b3aScheloha agtimer_rearm(void *cookie, uint64_t nsecs)
18118cf9b3aScheloha {
18218cf9b3aScheloha struct agtimer_softc *sc = cookie;
18318cf9b3aScheloha uint32_t cycles;
18418cf9b3aScheloha
18518cf9b3aScheloha if (nsecs > sc->sc_nsec_max)
18618cf9b3aScheloha nsecs = sc->sc_nsec_max;
18718cf9b3aScheloha cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32;
18818cf9b3aScheloha if (cycles > INT32_MAX)
18918cf9b3aScheloha cycles = INT32_MAX;
19018cf9b3aScheloha agtimer_set_tval(cycles);
19118cf9b3aScheloha }
19218cf9b3aScheloha
19318cf9b3aScheloha void
agtimer_trigger(void * unused)19418cf9b3aScheloha agtimer_trigger(void *unused)
19518cf9b3aScheloha {
19618cf9b3aScheloha agtimer_set_tval(0);
19718cf9b3aScheloha }
19818cf9b3aScheloha
199466195a2Spatrick int
agtimer_intr(void * frame)200466195a2Spatrick agtimer_intr(void *frame)
201466195a2Spatrick {
20218cf9b3aScheloha return clockintr_dispatch(frame);
203466195a2Spatrick }
204466195a2Spatrick
205466195a2Spatrick void
agtimer_set_clockrate(int32_t new_frequency)206466195a2Spatrick agtimer_set_clockrate(int32_t new_frequency)
207466195a2Spatrick {
208466195a2Spatrick struct agtimer_softc *sc = agtimer_cd.cd_devs[0];
209466195a2Spatrick
210466195a2Spatrick agtimer_frequency = new_frequency;
211466195a2Spatrick
212466195a2Spatrick if (sc == NULL)
213466195a2Spatrick return;
214466195a2Spatrick
215466195a2Spatrick sc->sc_ticks_per_second = agtimer_frequency;
21618cf9b3aScheloha sc->sc_nsec_cycle_ratio =
21718cf9b3aScheloha sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
21818cf9b3aScheloha sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
21918cf9b3aScheloha
220466195a2Spatrick agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
22118cf9b3aScheloha
2222252d02cSkettenis printf("agtimer0: adjusting clock: new rate %d kHz\n",
223466195a2Spatrick sc->sc_ticks_per_second / 1000);
224466195a2Spatrick }
225466195a2Spatrick
226466195a2Spatrick void
agtimer_cpu_initclocks(void)22737e35e27Sjsg agtimer_cpu_initclocks(void)
228466195a2Spatrick {
229466195a2Spatrick struct agtimer_softc *sc = agtimer_cd.cd_devs[0];
230466195a2Spatrick
231466195a2Spatrick stathz = hz;
23218cf9b3aScheloha profhz = stathz * 10;
233b3ef18bdScheloha statclock_is_randomized = 1;
234466195a2Spatrick
235466195a2Spatrick if (sc->sc_ticks_per_second != agtimer_frequency) {
236466195a2Spatrick agtimer_set_clockrate(agtimer_frequency);
237466195a2Spatrick }
238466195a2Spatrick
23924e5cb75Skettenis /* Setup secure and non-secure timer IRQs. */
24024e5cb75Skettenis arm_intr_establish_fdt_idx(sc->sc_node, 0, IPL_CLOCK,
24124e5cb75Skettenis agtimer_intr, NULL, "tick");
24224e5cb75Skettenis arm_intr_establish_fdt_idx(sc->sc_node, 1, IPL_CLOCK,
24324e5cb75Skettenis agtimer_intr, NULL, "tick");
244466195a2Spatrick }
245466195a2Spatrick
246466195a2Spatrick void
agtimer_delay(u_int usecs)247466195a2Spatrick agtimer_delay(u_int usecs)
248466195a2Spatrick {
249466195a2Spatrick u_int32_t clock, oclock, delta, delaycnt;
250466195a2Spatrick volatile int j;
251466195a2Spatrick int csec, usec;
252466195a2Spatrick
25324e5cb75Skettenis if (usecs > (0x80000000 / agtimer_frequency)) {
254466195a2Spatrick csec = usecs / 10000;
255466195a2Spatrick usec = usecs % 10000;
256466195a2Spatrick
25724e5cb75Skettenis delaycnt = (agtimer_frequency / 100) * csec +
25824e5cb75Skettenis (agtimer_frequency / 100) * usec / 10000;
259466195a2Spatrick } else {
26024e5cb75Skettenis delaycnt = agtimer_frequency * usecs / 1000000;
261466195a2Spatrick }
262466195a2Spatrick if (delaycnt <= 1)
263466195a2Spatrick for (j = 100; j > 0; j--)
264466195a2Spatrick ;
265466195a2Spatrick
26624e5cb75Skettenis oclock = agtimer_readcnt64();
267466195a2Spatrick while (1) {
268466195a2Spatrick for (j = 100; j > 0; j--)
269466195a2Spatrick ;
27024e5cb75Skettenis clock = agtimer_readcnt64();
271466195a2Spatrick delta = clock - oclock;
272466195a2Spatrick if (delta > delaycnt)
273466195a2Spatrick break;
274466195a2Spatrick }
275466195a2Spatrick }
276466195a2Spatrick
277466195a2Spatrick void
agtimer_setstatclockrate(int newhz)278466195a2Spatrick agtimer_setstatclockrate(int newhz)
279466195a2Spatrick {
280466195a2Spatrick }
281466195a2Spatrick
282466195a2Spatrick void
agtimer_startclock(void)283466195a2Spatrick agtimer_startclock(void)
284466195a2Spatrick {
285466195a2Spatrick uint32_t reg;
286466195a2Spatrick
28718cf9b3aScheloha clockintr_cpu_init(&agtimer_intrclock);
288466195a2Spatrick
289466195a2Spatrick reg = agtimer_get_ctrl();
290df7cbe9dSkettenis reg &= ~GTIMER_CNTP_CTL_IMASK;
291466195a2Spatrick reg |= GTIMER_CNTP_CTL_ENABLE;
29218cf9b3aScheloha agtimer_set_tval(INT32_MAX);
293466195a2Spatrick agtimer_set_ctrl(reg);
29418cf9b3aScheloha
29518cf9b3aScheloha clockintr_trigger();
296466195a2Spatrick }
29724e5cb75Skettenis
29824e5cb75Skettenis void
agtimer_init(void)29924e5cb75Skettenis agtimer_init(void)
30024e5cb75Skettenis {
30124e5cb75Skettenis uint32_t id_pfr1, cntfrq = 0;
30224e5cb75Skettenis
30324e5cb75Skettenis /* Check for Generic Timer support. */
30424e5cb75Skettenis __asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1));
30524e5cb75Skettenis if ((id_pfr1 & 0x000f0000) == 0x00010000)
30624e5cb75Skettenis __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (cntfrq));
30724e5cb75Skettenis
30824e5cb75Skettenis if (cntfrq != 0) {
30924e5cb75Skettenis agtimer_frequency = cntfrq;
31024e5cb75Skettenis arm_clock_register(NULL, agtimer_delay, NULL, NULL);
31124e5cb75Skettenis }
31224e5cb75Skettenis }
313