xref: /openbsd/sys/arch/arm/cortex/amptimer.c (revision 0ed1bf01)
1*0ed1bf01Scheloha /* $OpenBSD: amptimer.c,v 1.20 2023/09/17 14:50:51 cheloha Exp $ */
2027018efSpatrick /*
3027018efSpatrick  * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
4027018efSpatrick  *
5027018efSpatrick  * Permission to use, copy, modify, and distribute this software for any
6027018efSpatrick  * purpose with or without fee is hereby granted, provided that the above
7027018efSpatrick  * copyright notice and this permission notice appear in all copies.
8027018efSpatrick  *
9027018efSpatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10027018efSpatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11027018efSpatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12027018efSpatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13027018efSpatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14027018efSpatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15027018efSpatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16027018efSpatrick  */
17027018efSpatrick 
18027018efSpatrick #include <sys/param.h>
19027018efSpatrick #include <sys/systm.h>
208e3880d2Scheloha #include <sys/clockintr.h>
21027018efSpatrick #include <sys/device.h>
22027018efSpatrick #include <sys/kernel.h>
238e3880d2Scheloha #include <sys/stdint.h>
24027018efSpatrick #include <sys/timetc.h>
25027018efSpatrick 
26027018efSpatrick #include <arm/cpufunc.h>
27027018efSpatrick #include <machine/bus.h>
28027018efSpatrick #include <machine/intr.h>
29027018efSpatrick #include <arm/cortex/cortex.h>
30027018efSpatrick 
31027018efSpatrick /* offset from periphbase */
32027018efSpatrick #define GTIMER_ADDR	0x200
33027018efSpatrick #define GTIMER_SIZE	0x100
34027018efSpatrick 
35027018efSpatrick /* registers */
36027018efSpatrick #define GTIMER_CNT_LOW		0x00
37027018efSpatrick #define GTIMER_CNT_HIGH		0x04
38027018efSpatrick #define GTIMER_CTRL		0x08
39027018efSpatrick #define GTIMER_CTRL_AA		(1 << 3)
40027018efSpatrick #define GTIMER_CTRL_IRQ		(1 << 2)
41027018efSpatrick #define GTIMER_CTRL_COMP	(1 << 1)
42027018efSpatrick #define GTIMER_CTRL_TIMER	(1 << 0)
43027018efSpatrick #define GTIMER_STATUS		0x0c
44027018efSpatrick #define GTIMER_STATUS_EVENT	(1 << 0)
45027018efSpatrick #define GTIMER_CMP_LOW		0x10
46027018efSpatrick #define GTIMER_CMP_HIGH		0x14
47027018efSpatrick #define GTIMER_AUTOINC		0x18
48027018efSpatrick 
497e60210cSpatrick /* offset from periphbase */
507e60210cSpatrick #define PTIMER_ADDR		0x600
517e60210cSpatrick #define PTIMER_SIZE		0x100
527e60210cSpatrick 
537e60210cSpatrick /* registers */
547e60210cSpatrick #define PTIMER_LOAD		0x0
557e60210cSpatrick #define PTIMER_CNT		0x4
567e60210cSpatrick #define PTIMER_CTRL		0x8
577e60210cSpatrick #define PTIMER_CTRL_ENABLE	(1<<0)
587e60210cSpatrick #define PTIMER_CTRL_AUTORELOAD	(1<<1)
597e60210cSpatrick #define PTIMER_CTRL_IRQEN	(1<<2)
607e60210cSpatrick #define PTIMER_STATUS		0xC
617e60210cSpatrick #define PTIMER_STATUS_EVENT	(1<<0)
627e60210cSpatrick 
637e60210cSpatrick #define TIMER_FREQUENCY		396 * 1000 * 1000 /* ARM core clock */
64027018efSpatrick int32_t amptimer_frequency = TIMER_FREQUENCY;
65027018efSpatrick 
66027018efSpatrick u_int amptimer_get_timecount(struct timecounter *);
67027018efSpatrick 
68027018efSpatrick static struct timecounter amptimer_timecounter = {
698611d3cdScheloha 	.tc_get_timecount = amptimer_get_timecount,
708611d3cdScheloha 	.tc_counter_mask = 0xffffffff,
718611d3cdScheloha 	.tc_frequency = 0,
728611d3cdScheloha 	.tc_name = "amptimer",
738611d3cdScheloha 	.tc_quality = 0,
748611d3cdScheloha 	.tc_priv = NULL,
758611d3cdScheloha 	.tc_user = 0,
76027018efSpatrick };
77027018efSpatrick 
788e3880d2Scheloha void amptimer_rearm(void *, uint64_t);
798e3880d2Scheloha void amptimer_trigger(void *);
807e60210cSpatrick 
818e3880d2Scheloha struct intrclock amptimer_intrclock = {
828e3880d2Scheloha 	.ic_rearm = amptimer_rearm,
838e3880d2Scheloha 	.ic_trigger = amptimer_trigger
847e60210cSpatrick };
857e60210cSpatrick 
868e3880d2Scheloha #define MAX_ARM_CPUS	8
878e3880d2Scheloha 
88027018efSpatrick struct amptimer_softc {
89027018efSpatrick 	struct device		sc_dev;
90027018efSpatrick 	bus_space_tag_t		sc_iot;
91027018efSpatrick 	bus_space_handle_t	sc_ioh;
927e60210cSpatrick 	bus_space_handle_t	sc_pioh;
937e60210cSpatrick 
94027018efSpatrick 	u_int32_t		sc_ticks_per_second;
958e3880d2Scheloha 	u_int64_t		sc_nsec_cycle_ratio;
968e3880d2Scheloha 	u_int64_t		sc_nsec_max;
97027018efSpatrick };
98027018efSpatrick 
99027018efSpatrick int		amptimer_match(struct device *, void *, void *);
100027018efSpatrick void		amptimer_attach(struct device *, struct device *, void *);
101027018efSpatrick uint64_t	amptimer_readcnt64(struct amptimer_softc *sc);
102027018efSpatrick int		amptimer_intr(void *);
103027018efSpatrick void		amptimer_cpu_initclocks(void);
104027018efSpatrick void		amptimer_delay(u_int);
105027018efSpatrick void		amptimer_setstatclockrate(int stathz);
106027018efSpatrick void		amptimer_set_clockrate(int32_t new_frequency);
1077e60210cSpatrick void		amptimer_startclock(void);
108027018efSpatrick 
109027018efSpatrick /* hack - XXXX
110027018efSpatrick  * gptimer connects directly to ampintc, not thru the generic
111e6a06f73Smmcc  * interface because it uses an 'internal' interrupt
112027018efSpatrick  * not a peripheral interrupt.
113027018efSpatrick  */
114789e88a4Spatrick void	*ampintc_intr_establish(int, int, int, struct cpu_info *,
115789e88a4Spatrick 	    int (*)(void *), void *, char *);
116027018efSpatrick 
117027018efSpatrick 
118027018efSpatrick 
119e3ee5e84Smpi const struct cfattach amptimer_ca = {
120027018efSpatrick 	sizeof (struct amptimer_softc), amptimer_match, amptimer_attach
121027018efSpatrick };
122027018efSpatrick 
123027018efSpatrick struct cfdriver amptimer_cd = {
124027018efSpatrick 	NULL, "amptimer", DV_DULL
125027018efSpatrick };
126027018efSpatrick 
127027018efSpatrick uint64_t
amptimer_readcnt64(struct amptimer_softc * sc)128027018efSpatrick amptimer_readcnt64(struct amptimer_softc *sc)
129027018efSpatrick {
130027018efSpatrick 	uint32_t high0, high1, low;
131027018efSpatrick 	bus_space_tag_t iot = sc->sc_iot;
132027018efSpatrick 	bus_space_handle_t ioh = sc->sc_ioh;
133027018efSpatrick 
134027018efSpatrick 	do {
135027018efSpatrick 		high0 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
136027018efSpatrick 		low = bus_space_read_4(iot, ioh, GTIMER_CNT_LOW);
137027018efSpatrick 		high1 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
138027018efSpatrick 	} while (high0 != high1);
139027018efSpatrick 
140027018efSpatrick 	return ((((uint64_t)high1) << 32) | low);
141027018efSpatrick }
142027018efSpatrick 
143027018efSpatrick int
amptimer_match(struct device * parent,void * cfdata,void * aux)144027018efSpatrick amptimer_match(struct device *parent, void *cfdata, void *aux)
145027018efSpatrick {
1460e8af838Spatrick 	if ((cpufunc_id() & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9)
147027018efSpatrick 		return (1);
1480e8af838Spatrick 
1490e8af838Spatrick 	return 0;
150027018efSpatrick }
151027018efSpatrick 
152027018efSpatrick void
amptimer_attach(struct device * parent,struct device * self,void * args)153027018efSpatrick amptimer_attach(struct device *parent, struct device *self, void *args)
154027018efSpatrick {
155027018efSpatrick 	struct amptimer_softc *sc = (struct amptimer_softc *)self;
156027018efSpatrick 	struct cortex_attach_args *ia = args;
1577e60210cSpatrick 	bus_space_handle_t ioh, pioh;
158027018efSpatrick 
159027018efSpatrick 	sc->sc_iot = ia->ca_iot;
160027018efSpatrick 
161027018efSpatrick 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + GTIMER_ADDR,
162027018efSpatrick 	    GTIMER_SIZE, 0, &ioh))
1637e60210cSpatrick 		panic("amptimer_attach: bus_space_map global timer failed!");
1647e60210cSpatrick 
1657e60210cSpatrick 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + PTIMER_ADDR,
1667e60210cSpatrick 	    PTIMER_SIZE, 0, &pioh))
1677e60210cSpatrick 		panic("amptimer_attach: bus_space_map priv timer failed!");
168027018efSpatrick 
169027018efSpatrick 	sc->sc_ticks_per_second = amptimer_frequency;
1708e3880d2Scheloha 	sc->sc_nsec_cycle_ratio =
1718e3880d2Scheloha 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
1728e3880d2Scheloha 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
1732252d02cSkettenis 	printf(": %d kHz\n", sc->sc_ticks_per_second / 1000);
174027018efSpatrick 
175027018efSpatrick 	sc->sc_ioh = ioh;
1767e60210cSpatrick 	sc->sc_pioh = pioh;
177027018efSpatrick 
178027018efSpatrick 	/* disable global timer */
179027018efSpatrick 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, 0);
180027018efSpatrick 
181027018efSpatrick 	/* XXX ??? reset counters to 0 - gives us uptime in the counter */
182027018efSpatrick 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_LOW, 0);
183027018efSpatrick 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_HIGH, 0);
184027018efSpatrick 
185027018efSpatrick 	/* enable global timer */
186027018efSpatrick 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, GTIMER_CTRL_TIMER);
187027018efSpatrick 
1888e3880d2Scheloha 	/* Disable private timer, clear event flag. */
1897e60210cSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL, 0);
1907e60210cSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
1917e60210cSpatrick 	    PTIMER_STATUS_EVENT);
192027018efSpatrick 
193027018efSpatrick 	/*
1947e60210cSpatrick 	 * private timer and interrupts not enabled until
195027018efSpatrick 	 * timer configures
196027018efSpatrick 	 */
197027018efSpatrick 
198027018efSpatrick 	arm_clock_register(amptimer_cpu_initclocks, amptimer_delay,
1997e60210cSpatrick 	    amptimer_setstatclockrate, amptimer_startclock);
200027018efSpatrick 
201027018efSpatrick 	amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
202027018efSpatrick 	amptimer_timecounter.tc_priv = sc;
203027018efSpatrick 	tc_init(&amptimer_timecounter);
2048e3880d2Scheloha 
2058e3880d2Scheloha 	amptimer_intrclock.ic_cookie = sc;
206027018efSpatrick }
207027018efSpatrick 
208027018efSpatrick u_int
amptimer_get_timecount(struct timecounter * tc)209027018efSpatrick amptimer_get_timecount(struct timecounter *tc)
210027018efSpatrick {
211027018efSpatrick 	struct amptimer_softc *sc = amptimer_timecounter.tc_priv;
212027018efSpatrick 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
213027018efSpatrick }
214027018efSpatrick 
2158e3880d2Scheloha void
amptimer_rearm(void * cookie,uint64_t nsecs)2168e3880d2Scheloha amptimer_rearm(void *cookie, uint64_t nsecs)
217027018efSpatrick {
2188e3880d2Scheloha 	struct amptimer_softc *sc = cookie;
2198e3880d2Scheloha 	uint32_t cycles, reg;
220027018efSpatrick 
2218e3880d2Scheloha 	if (nsecs > sc->sc_nsec_max)
2228e3880d2Scheloha 		nsecs = sc->sc_nsec_max;
2238e3880d2Scheloha 	cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32;
2248e3880d2Scheloha 	if (cycles == 0)
2258e3880d2Scheloha 		cycles = 1;
226027018efSpatrick 
2277e60210cSpatrick 	/* clear old status */
2287e60210cSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
2297e60210cSpatrick 	    PTIMER_STATUS_EVENT);
2307e60210cSpatrick 
2318e3880d2Scheloha 	/*
2328e3880d2Scheloha 	 * Make sure the private timer counter and interrupts are enabled.
2338e3880d2Scheloha 	 * XXX Why wouldn't they be?
2348e3880d2Scheloha 	 */
2357e60210cSpatrick 	reg = bus_space_read_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL);
2367e60210cSpatrick 	if ((reg & (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN)) !=
2377e60210cSpatrick 	    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN))
2387e60210cSpatrick 		bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL,
2397e60210cSpatrick 		    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN));
2407e60210cSpatrick 
2418e3880d2Scheloha 	/* Start the downcounter. */
2428e3880d2Scheloha 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD, cycles);
2438e3880d2Scheloha }
244027018efSpatrick 
2458e3880d2Scheloha void
amptimer_trigger(void * cookie)2468e3880d2Scheloha amptimer_trigger(void *cookie)
2478e3880d2Scheloha {
2488e3880d2Scheloha 	struct amptimer_softc *sc = cookie;
2498e3880d2Scheloha 
2508e3880d2Scheloha 	/* Clear event flag, then arm counter to fire immediately. */
2518e3880d2Scheloha 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
2528e3880d2Scheloha 	    PTIMER_STATUS_EVENT);
2538e3880d2Scheloha 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD, 1);
2548e3880d2Scheloha }
2558e3880d2Scheloha 
2568e3880d2Scheloha int
amptimer_intr(void * frame)2578e3880d2Scheloha amptimer_intr(void *frame)
2588e3880d2Scheloha {
2598e3880d2Scheloha 	return clockintr_dispatch(frame);
260027018efSpatrick }
261027018efSpatrick 
262027018efSpatrick void
amptimer_set_clockrate(int32_t new_frequency)263027018efSpatrick amptimer_set_clockrate(int32_t new_frequency)
264027018efSpatrick {
265027018efSpatrick 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
266027018efSpatrick 
267027018efSpatrick 	amptimer_frequency = new_frequency;
268027018efSpatrick 
269027018efSpatrick 	if (sc == NULL)
270027018efSpatrick 		return;
271027018efSpatrick 
272027018efSpatrick 	sc->sc_ticks_per_second = amptimer_frequency;
2738e3880d2Scheloha 	sc->sc_nsec_cycle_ratio =
2748e3880d2Scheloha 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
2758e3880d2Scheloha 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
2768e3880d2Scheloha 
277027018efSpatrick 	amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
2788e3880d2Scheloha 
2792252d02cSkettenis 	printf("amptimer0: adjusting clock: new rate %d kHz\n",
280027018efSpatrick 	    sc->sc_ticks_per_second / 1000);
281027018efSpatrick }
282027018efSpatrick 
283027018efSpatrick void
amptimer_cpu_initclocks(void)28437e35e27Sjsg amptimer_cpu_initclocks(void)
285027018efSpatrick {
286027018efSpatrick 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
287027018efSpatrick 
288027018efSpatrick 	stathz = hz;
289027018efSpatrick 	profhz = hz * 10;
290b3ef18bdScheloha 	statclock_is_randomized = 1;
291027018efSpatrick 
292027018efSpatrick 	if (sc->sc_ticks_per_second != amptimer_frequency) {
293027018efSpatrick 		amptimer_set_clockrate(amptimer_frequency);
294027018efSpatrick 	}
295027018efSpatrick 
296027018efSpatrick 	/* establish interrupts */
297027018efSpatrick 	/* XXX - irq */
29823a4b636Spatrick 	ampintc_intr_establish(29, IST_EDGE_RISING, IPL_CLOCK,
299789e88a4Spatrick 	    NULL, amptimer_intr, NULL, "tick");
300027018efSpatrick 
3018e3880d2Scheloha 	/* Enable private timer counter and interrupt. */
3027e60210cSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL,
3037e60210cSpatrick 	    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN));
304027018efSpatrick }
305027018efSpatrick 
306027018efSpatrick void
amptimer_delay(u_int usecs)307027018efSpatrick amptimer_delay(u_int usecs)
308027018efSpatrick {
309027018efSpatrick 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
310027018efSpatrick 	u_int32_t		clock, oclock, delta, delaycnt;
311027018efSpatrick 	volatile int		j;
312027018efSpatrick 	int			csec, usec;
313027018efSpatrick 
314027018efSpatrick 	if (usecs > (0x80000000 / (sc->sc_ticks_per_second))) {
315027018efSpatrick 		csec = usecs / 10000;
316027018efSpatrick 		usec = usecs % 10000;
317027018efSpatrick 
318027018efSpatrick 		delaycnt = (sc->sc_ticks_per_second / 100) * csec +
319027018efSpatrick 		    (sc->sc_ticks_per_second / 100) * usec / 10000;
320027018efSpatrick 	} else {
321027018efSpatrick 		delaycnt = sc->sc_ticks_per_second * usecs / 1000000;
322027018efSpatrick 	}
323027018efSpatrick 	if (delaycnt <= 1)
324027018efSpatrick 		for (j = 100; j > 0; j--)
325027018efSpatrick 			;
326027018efSpatrick 
327027018efSpatrick 	oclock = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
328027018efSpatrick 	while (1) {
329027018efSpatrick 		for (j = 100; j > 0; j--)
330027018efSpatrick 			;
331027018efSpatrick 		clock = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
332027018efSpatrick 		    GTIMER_CNT_LOW);
333027018efSpatrick 		delta = clock - oclock;
334027018efSpatrick 		if (delta > delaycnt)
335027018efSpatrick 			break;
336027018efSpatrick 	}
337027018efSpatrick }
338027018efSpatrick 
339027018efSpatrick void
amptimer_setstatclockrate(int newhz)340027018efSpatrick amptimer_setstatclockrate(int newhz)
341027018efSpatrick {
342027018efSpatrick }
3437e60210cSpatrick 
3447e60210cSpatrick void
amptimer_startclock(void)3457e60210cSpatrick amptimer_startclock(void)
3467e60210cSpatrick {
3478e3880d2Scheloha 	clockintr_cpu_init(&amptimer_intrclock);
3488e3880d2Scheloha 	clockintr_trigger();
3497e60210cSpatrick }
350