xref: /openbsd-src/sys/arch/arm/cortex/agtimer.c (revision 8550894424f8a4aa4aafb6cd57229dd6ed7cd9dd)
1 /* $OpenBSD: agtimer.c,v 1.16 2023/01/17 02:58:22 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/device.h>
23 #include <sys/kernel.h>
24 #include <sys/stdint.h>
25 #include <sys/timetc.h>
26 
27 #include <machine/intr.h>
28 #include <machine/bus.h>
29 #include <machine/fdt.h>
30 
31 #include <arm/cpufunc.h>
32 
33 #include <dev/ofw/fdt.h>
34 #include <dev/ofw/openfirm.h>
35 
36 /* registers */
37 #define GTIMER_CNTP_CTL_ENABLE		(1 << 0)
38 #define GTIMER_CNTP_CTL_IMASK		(1 << 1)
39 #define GTIMER_CNTP_CTL_ISTATUS		(1 << 2)
40 
41 #define TIMER_FREQUENCY		24 * 1000 * 1000 /* ARM core clock */
42 int32_t agtimer_frequency = TIMER_FREQUENCY;
43 
44 u_int agtimer_get_timecount(struct timecounter *);
45 
46 static struct timecounter agtimer_timecounter = {
47 	.tc_get_timecount = agtimer_get_timecount,
48 	.tc_poll_pps = NULL,
49 	.tc_counter_mask = 0xffffffff,
50 	.tc_frequency = 0,
51 	.tc_name = "agtimer",
52 	.tc_quality = 0,
53 	.tc_priv = NULL,
54 };
55 
56 struct agtimer_softc {
57 	struct device		sc_dev;
58 	int			sc_node;
59 	u_int32_t		sc_ticks_per_second;
60 	uint64_t		sc_nsec_cycle_ratio;
61 	uint64_t		sc_nsec_max;
62 };
63 
64 int		agtimer_match(struct device *, void *, void *);
65 void		agtimer_attach(struct device *, struct device *, void *);
66 uint64_t	agtimer_readcnt64(void);
67 int		agtimer_intr(void *);
68 void		agtimer_cpu_initclocks(void);
69 void		agtimer_delay(u_int);
70 void		agtimer_setstatclockrate(int stathz);
71 void		agtimer_set_clockrate(int32_t new_frequency);
72 void		agtimer_startclock(void);
73 
74 const struct cfattach agtimer_ca = {
75 	sizeof (struct agtimer_softc), agtimer_match, agtimer_attach
76 };
77 
78 struct cfdriver agtimer_cd = {
79 	NULL, "agtimer", DV_DULL
80 };
81 
82 void agtimer_rearm(void *, uint64_t);
83 void agtimer_trigger(void *);
84 
85 struct intrclock agtimer_intrclock = {
86 	.ic_rearm = agtimer_rearm,
87 	.ic_trigger = agtimer_trigger
88 };
89 
90 uint64_t
91 agtimer_readcnt64(void)
92 {
93 	uint64_t val;
94 
95 	__asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val));
96 
97 	return (val);
98 }
99 
100 static inline int
101 agtimer_get_ctrl(void)
102 {
103 	uint32_t val;
104 
105 	__asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
106 
107 	return (val);
108 }
109 
110 static inline int
111 agtimer_set_ctrl(uint32_t val)
112 {
113 	__asm volatile("mcr p15, 0, %[val], c14, c2, 1" : :
114 	    [val] "r" (val));
115 
116 	cpu_drain_writebuf();
117 	//isb();
118 
119 	return (0);
120 }
121 
122 static inline int
123 agtimer_set_tval(uint32_t val)
124 {
125 	__asm volatile("mcr p15, 0, %[val], c14, c2, 0" : :
126 	    [val] "r" (val));
127 	cpu_drain_writebuf();
128 	//isb();
129 
130 	return (0);
131 }
132 
133 int
134 agtimer_match(struct device *parent, void *cfdata, void *aux)
135 {
136 	struct fdt_attach_args *faa = (struct fdt_attach_args *)aux;
137 
138 	return OF_is_compatible(faa->fa_node, "arm,armv7-timer");
139 }
140 
141 void
142 agtimer_attach(struct device *parent, struct device *self, void *aux)
143 {
144 	struct agtimer_softc *sc = (struct agtimer_softc *)self;
145 	struct fdt_attach_args *faa = aux;
146 
147 	sc->sc_node = faa->fa_node;
148 
149 	agtimer_frequency =
150 	    OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency);
151 	sc->sc_ticks_per_second = agtimer_frequency;
152 	sc->sc_nsec_cycle_ratio =
153 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
154 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
155 	printf(": %d kHz\n", sc->sc_ticks_per_second / 1000);
156 
157 	/* XXX: disable user access */
158 
159 	/*
160 	 * private timer and interrupts not enabled until
161 	 * timer configures
162 	 */
163 
164 	arm_clock_register(agtimer_cpu_initclocks, agtimer_delay,
165 	    agtimer_setstatclockrate, agtimer_startclock);
166 
167 	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
168 	agtimer_timecounter.tc_priv = sc;
169 	tc_init(&agtimer_timecounter);
170 
171 	agtimer_intrclock.ic_cookie = sc;
172 }
173 
174 u_int
175 agtimer_get_timecount(struct timecounter *tc)
176 {
177 	return agtimer_readcnt64();
178 }
179 
180 void
181 agtimer_rearm(void *cookie, uint64_t nsecs)
182 {
183 	struct agtimer_softc *sc = cookie;
184 	uint32_t cycles;
185 
186 	if (nsecs > sc->sc_nsec_max)
187 		nsecs = sc->sc_nsec_max;
188 	cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32;
189 	if (cycles > INT32_MAX)
190 		cycles = INT32_MAX;
191 	agtimer_set_tval(cycles);
192 }
193 
194 void
195 agtimer_trigger(void *unused)
196 {
197 	agtimer_set_tval(0);
198 }
199 
200 int
201 agtimer_intr(void *frame)
202 {
203 	return clockintr_dispatch(frame);
204 }
205 
206 void
207 agtimer_set_clockrate(int32_t new_frequency)
208 {
209 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
210 
211 	agtimer_frequency = new_frequency;
212 
213 	if (sc == NULL)
214 		return;
215 
216 	sc->sc_ticks_per_second = agtimer_frequency;
217 	sc->sc_nsec_cycle_ratio =
218 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
219 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
220 
221 	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
222 
223 	printf("agtimer0: adjusting clock: new rate %d kHz\n",
224 	    sc->sc_ticks_per_second / 1000);
225 }
226 
227 void
228 agtimer_cpu_initclocks(void)
229 {
230 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
231 	uint32_t		 reg;
232 
233 	stathz = hz;
234 	profhz = stathz * 10;
235 	clockintr_init(CL_RNDSTAT);
236 
237 	if (sc->sc_ticks_per_second != agtimer_frequency) {
238 		agtimer_set_clockrate(agtimer_frequency);
239 	}
240 
241 	clockintr_cpu_init(&agtimer_intrclock);
242 
243 	/* Setup secure and non-secure timer IRQs. */
244 	arm_intr_establish_fdt_idx(sc->sc_node, 0, IPL_CLOCK,
245 	    agtimer_intr, NULL, "tick");
246 	arm_intr_establish_fdt_idx(sc->sc_node, 1, IPL_CLOCK,
247 	    agtimer_intr, NULL, "tick");
248 
249 	reg = agtimer_get_ctrl();
250 	reg &= ~GTIMER_CNTP_CTL_IMASK;
251 	reg |= GTIMER_CNTP_CTL_ENABLE;
252 	agtimer_set_tval(INT32_MAX);
253 	agtimer_set_ctrl(reg);
254 
255 	clockintr_trigger();
256 }
257 
258 void
259 agtimer_delay(u_int usecs)
260 {
261 	u_int32_t		clock, oclock, delta, delaycnt;
262 	volatile int		j;
263 	int			csec, usec;
264 
265 	if (usecs > (0x80000000 / agtimer_frequency)) {
266 		csec = usecs / 10000;
267 		usec = usecs % 10000;
268 
269 		delaycnt = (agtimer_frequency / 100) * csec +
270 		    (agtimer_frequency / 100) * usec / 10000;
271 	} else {
272 		delaycnt = agtimer_frequency * usecs / 1000000;
273 	}
274 	if (delaycnt <= 1)
275 		for (j = 100; j > 0; j--)
276 			;
277 
278 	oclock = agtimer_readcnt64();
279 	while (1) {
280 		for (j = 100; j > 0; j--)
281 			;
282 		clock = agtimer_readcnt64();
283 		delta = clock - oclock;
284 		if (delta > delaycnt)
285 			break;
286 	}
287 }
288 
289 void
290 agtimer_setstatclockrate(int newhz)
291 {
292 	clockintr_setstatclockrate(newhz);
293 }
294 
295 void
296 agtimer_startclock(void)
297 {
298 	uint32_t reg;
299 
300 	clockintr_cpu_init(&agtimer_intrclock);
301 
302 	reg = agtimer_get_ctrl();
303 	reg &= ~GTIMER_CNTP_CTL_IMASK;
304 	reg |= GTIMER_CNTP_CTL_ENABLE;
305 	agtimer_set_tval(INT32_MAX);
306 	agtimer_set_ctrl(reg);
307 
308 	clockintr_trigger();
309 }
310 
311 void
312 agtimer_init(void)
313 {
314 	uint32_t id_pfr1, cntfrq = 0;
315 
316 	/* Check for Generic Timer support. */
317 	__asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1));
318 	if ((id_pfr1 & 0x000f0000) == 0x00010000)
319 		__asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (cntfrq));
320 
321 	if (cntfrq != 0) {
322 		agtimer_frequency = cntfrq;
323 		arm_clock_register(NULL, agtimer_delay, NULL, NULL);
324 	}
325 }
326