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