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(&timer_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(&timer_intrclock);
3488e3880d2Scheloha clockintr_trigger();
3497e60210cSpatrick }
350