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(&timer_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(&timer_intrclock);
348 clockintr_trigger();
349 }
350