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