xref: /openbsd/sys/arch/arm/cortex/amptimer.c (revision 0ed1bf01)
1 /* $OpenBSD: amptimer.c,v 1.20 2023/09/17 14:50:51 cheloha Exp $ */
2 /*
3  * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/clockintr.h>
21 #include <sys/device.h>
22 #include <sys/kernel.h>
23 #include <sys/stdint.h>
24 #include <sys/timetc.h>
25 
26 #include <arm/cpufunc.h>
27 #include <machine/bus.h>
28 #include <machine/intr.h>
29 #include <arm/cortex/cortex.h>
30 
31 /* offset from periphbase */
32 #define GTIMER_ADDR	0x200
33 #define GTIMER_SIZE	0x100
34 
35 /* registers */
36 #define GTIMER_CNT_LOW		0x00
37 #define GTIMER_CNT_HIGH		0x04
38 #define GTIMER_CTRL		0x08
39 #define GTIMER_CTRL_AA		(1 << 3)
40 #define GTIMER_CTRL_IRQ		(1 << 2)
41 #define GTIMER_CTRL_COMP	(1 << 1)
42 #define GTIMER_CTRL_TIMER	(1 << 0)
43 #define GTIMER_STATUS		0x0c
44 #define GTIMER_STATUS_EVENT	(1 << 0)
45 #define GTIMER_CMP_LOW		0x10
46 #define GTIMER_CMP_HIGH		0x14
47 #define GTIMER_AUTOINC		0x18
48 
49 /* offset from periphbase */
50 #define PTIMER_ADDR		0x600
51 #define PTIMER_SIZE		0x100
52 
53 /* registers */
54 #define PTIMER_LOAD		0x0
55 #define PTIMER_CNT		0x4
56 #define PTIMER_CTRL		0x8
57 #define PTIMER_CTRL_ENABLE	(1<<0)
58 #define PTIMER_CTRL_AUTORELOAD	(1<<1)
59 #define PTIMER_CTRL_IRQEN	(1<<2)
60 #define PTIMER_STATUS		0xC
61 #define PTIMER_STATUS_EVENT	(1<<0)
62 
63 #define TIMER_FREQUENCY		396 * 1000 * 1000 /* ARM core clock */
64 int32_t amptimer_frequency = TIMER_FREQUENCY;
65 
66 u_int amptimer_get_timecount(struct timecounter *);
67 
68 static struct timecounter amptimer_timecounter = {
69 	.tc_get_timecount = amptimer_get_timecount,
70 	.tc_counter_mask = 0xffffffff,
71 	.tc_frequency = 0,
72 	.tc_name = "amptimer",
73 	.tc_quality = 0,
74 	.tc_priv = NULL,
75 	.tc_user = 0,
76 };
77 
78 void amptimer_rearm(void *, uint64_t);
79 void amptimer_trigger(void *);
80 
81 struct intrclock amptimer_intrclock = {
82 	.ic_rearm = amptimer_rearm,
83 	.ic_trigger = amptimer_trigger
84 };
85 
86 #define MAX_ARM_CPUS	8
87 
88 struct amptimer_softc {
89 	struct device		sc_dev;
90 	bus_space_tag_t		sc_iot;
91 	bus_space_handle_t	sc_ioh;
92 	bus_space_handle_t	sc_pioh;
93 
94 	u_int32_t		sc_ticks_per_second;
95 	u_int64_t		sc_nsec_cycle_ratio;
96 	u_int64_t		sc_nsec_max;
97 };
98 
99 int		amptimer_match(struct device *, void *, void *);
100 void		amptimer_attach(struct device *, struct device *, void *);
101 uint64_t	amptimer_readcnt64(struct amptimer_softc *sc);
102 int		amptimer_intr(void *);
103 void		amptimer_cpu_initclocks(void);
104 void		amptimer_delay(u_int);
105 void		amptimer_setstatclockrate(int stathz);
106 void		amptimer_set_clockrate(int32_t new_frequency);
107 void		amptimer_startclock(void);
108 
109 /* hack - XXXX
110  * gptimer connects directly to ampintc, not thru the generic
111  * interface because it uses an 'internal' interrupt
112  * not a peripheral interrupt.
113  */
114 void	*ampintc_intr_establish(int, int, int, struct cpu_info *,
115 	    int (*)(void *), void *, char *);
116 
117 
118 
119 const struct cfattach amptimer_ca = {
120 	sizeof (struct amptimer_softc), amptimer_match, amptimer_attach
121 };
122 
123 struct cfdriver amptimer_cd = {
124 	NULL, "amptimer", DV_DULL
125 };
126 
127 uint64_t
amptimer_readcnt64(struct amptimer_softc * sc)128 amptimer_readcnt64(struct amptimer_softc *sc)
129 {
130 	uint32_t high0, high1, low;
131 	bus_space_tag_t iot = sc->sc_iot;
132 	bus_space_handle_t ioh = sc->sc_ioh;
133 
134 	do {
135 		high0 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
136 		low = bus_space_read_4(iot, ioh, GTIMER_CNT_LOW);
137 		high1 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
138 	} while (high0 != high1);
139 
140 	return ((((uint64_t)high1) << 32) | low);
141 }
142 
143 int
amptimer_match(struct device * parent,void * cfdata,void * aux)144 amptimer_match(struct device *parent, void *cfdata, void *aux)
145 {
146 	if ((cpufunc_id() & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9)
147 		return (1);
148 
149 	return 0;
150 }
151 
152 void
amptimer_attach(struct device * parent,struct device * self,void * args)153 amptimer_attach(struct device *parent, struct device *self, void *args)
154 {
155 	struct amptimer_softc *sc = (struct amptimer_softc *)self;
156 	struct cortex_attach_args *ia = args;
157 	bus_space_handle_t ioh, pioh;
158 
159 	sc->sc_iot = ia->ca_iot;
160 
161 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + GTIMER_ADDR,
162 	    GTIMER_SIZE, 0, &ioh))
163 		panic("amptimer_attach: bus_space_map global timer failed!");
164 
165 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + PTIMER_ADDR,
166 	    PTIMER_SIZE, 0, &pioh))
167 		panic("amptimer_attach: bus_space_map priv timer failed!");
168 
169 	sc->sc_ticks_per_second = amptimer_frequency;
170 	sc->sc_nsec_cycle_ratio =
171 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
172 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
173 	printf(": %d kHz\n", sc->sc_ticks_per_second / 1000);
174 
175 	sc->sc_ioh = ioh;
176 	sc->sc_pioh = pioh;
177 
178 	/* disable global timer */
179 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, 0);
180 
181 	/* XXX ??? reset counters to 0 - gives us uptime in the counter */
182 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_LOW, 0);
183 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_HIGH, 0);
184 
185 	/* enable global timer */
186 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, GTIMER_CTRL_TIMER);
187 
188 	/* Disable private timer, clear event flag. */
189 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL, 0);
190 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
191 	    PTIMER_STATUS_EVENT);
192 
193 	/*
194 	 * private timer and interrupts not enabled until
195 	 * timer configures
196 	 */
197 
198 	arm_clock_register(amptimer_cpu_initclocks, amptimer_delay,
199 	    amptimer_setstatclockrate, amptimer_startclock);
200 
201 	amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
202 	amptimer_timecounter.tc_priv = sc;
203 	tc_init(&amptimer_timecounter);
204 
205 	amptimer_intrclock.ic_cookie = sc;
206 }
207 
208 u_int
amptimer_get_timecount(struct timecounter * tc)209 amptimer_get_timecount(struct timecounter *tc)
210 {
211 	struct amptimer_softc *sc = amptimer_timecounter.tc_priv;
212 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
213 }
214 
215 void
amptimer_rearm(void * cookie,uint64_t nsecs)216 amptimer_rearm(void *cookie, uint64_t nsecs)
217 {
218 	struct amptimer_softc *sc = cookie;
219 	uint32_t cycles, reg;
220 
221 	if (nsecs > sc->sc_nsec_max)
222 		nsecs = sc->sc_nsec_max;
223 	cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32;
224 	if (cycles == 0)
225 		cycles = 1;
226 
227 	/* clear old status */
228 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
229 	    PTIMER_STATUS_EVENT);
230 
231 	/*
232 	 * Make sure the private timer counter and interrupts are enabled.
233 	 * XXX Why wouldn't they be?
234 	 */
235 	reg = bus_space_read_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL);
236 	if ((reg & (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN)) !=
237 	    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN))
238 		bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL,
239 		    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN));
240 
241 	/* Start the downcounter. */
242 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD, cycles);
243 }
244 
245 void
amptimer_trigger(void * cookie)246 amptimer_trigger(void *cookie)
247 {
248 	struct amptimer_softc *sc = cookie;
249 
250 	/* Clear event flag, then arm counter to fire immediately. */
251 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
252 	    PTIMER_STATUS_EVENT);
253 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD, 1);
254 }
255 
256 int
amptimer_intr(void * frame)257 amptimer_intr(void *frame)
258 {
259 	return clockintr_dispatch(frame);
260 }
261 
262 void
amptimer_set_clockrate(int32_t new_frequency)263 amptimer_set_clockrate(int32_t new_frequency)
264 {
265 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
266 
267 	amptimer_frequency = new_frequency;
268 
269 	if (sc == NULL)
270 		return;
271 
272 	sc->sc_ticks_per_second = amptimer_frequency;
273 	sc->sc_nsec_cycle_ratio =
274 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
275 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
276 
277 	amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
278 
279 	printf("amptimer0: adjusting clock: new rate %d kHz\n",
280 	    sc->sc_ticks_per_second / 1000);
281 }
282 
283 void
amptimer_cpu_initclocks(void)284 amptimer_cpu_initclocks(void)
285 {
286 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
287 
288 	stathz = hz;
289 	profhz = hz * 10;
290 	statclock_is_randomized = 1;
291 
292 	if (sc->sc_ticks_per_second != amptimer_frequency) {
293 		amptimer_set_clockrate(amptimer_frequency);
294 	}
295 
296 	/* establish interrupts */
297 	/* XXX - irq */
298 	ampintc_intr_establish(29, IST_EDGE_RISING, IPL_CLOCK,
299 	    NULL, amptimer_intr, NULL, "tick");
300 
301 	/* Enable private timer counter and interrupt. */
302 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL,
303 	    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN));
304 }
305 
306 void
amptimer_delay(u_int usecs)307 amptimer_delay(u_int usecs)
308 {
309 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
310 	u_int32_t		clock, oclock, delta, delaycnt;
311 	volatile int		j;
312 	int			csec, usec;
313 
314 	if (usecs > (0x80000000 / (sc->sc_ticks_per_second))) {
315 		csec = usecs / 10000;
316 		usec = usecs % 10000;
317 
318 		delaycnt = (sc->sc_ticks_per_second / 100) * csec +
319 		    (sc->sc_ticks_per_second / 100) * usec / 10000;
320 	} else {
321 		delaycnt = sc->sc_ticks_per_second * usecs / 1000000;
322 	}
323 	if (delaycnt <= 1)
324 		for (j = 100; j > 0; j--)
325 			;
326 
327 	oclock = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
328 	while (1) {
329 		for (j = 100; j > 0; j--)
330 			;
331 		clock = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
332 		    GTIMER_CNT_LOW);
333 		delta = clock - oclock;
334 		if (delta > delaycnt)
335 			break;
336 	}
337 }
338 
339 void
amptimer_setstatclockrate(int newhz)340 amptimer_setstatclockrate(int newhz)
341 {
342 }
343 
344 void
amptimer_startclock(void)345 amptimer_startclock(void)
346 {
347 	clockintr_cpu_init(&amptimer_intrclock);
348 	clockintr_trigger();
349 }
350