generic_timer.c revision 270075
1/*-
2 * Copyright (c) 2011 The FreeBSD Foundation
3 * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
4 * All rights reserved.
5 *
6 * Based on mpcore_timer.c developed by Ben Gray <ben.r.gray@gmail.com>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the company nor the name of the author may be used to
17 *    endorse or promote products derived from this software without specific
18 *    prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/**
34 *      Cortex-A15 (and probably A7) Generic Timer
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: stable/10/sys/arm/arm/generic_timer.c 270075 2014-08-17 01:28:03Z ian $");
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/bus.h>
43#include <sys/kernel.h>
44#include <sys/module.h>
45#include <sys/malloc.h>
46#include <sys/rman.h>
47#include <sys/timeet.h>
48#include <sys/timetc.h>
49#include <sys/watchdog.h>
50#include <machine/bus.h>
51#include <machine/cpu.h>
52#include <machine/intr.h>
53
54#include <dev/fdt/fdt_common.h>
55#include <dev/ofw/openfirm.h>
56#include <dev/ofw/ofw_bus.h>
57#include <dev/ofw/ofw_bus_subr.h>
58
59#include <machine/bus.h>
60#include <machine/fdt.h>
61
62#define	GT_CTRL_ENABLE		(1 << 0)
63#define	GT_CTRL_INT_MASK	(1 << 1)
64#define	GT_CTRL_INT_STAT	(1 << 2)
65#define	GT_REG_CTRL		0
66#define	GT_REG_TVAL		1
67
68#define	GT_CNTKCTL_PL0PTEN	(1 << 9) /* PL0 Physical timer reg access */
69#define	GT_CNTKCTL_PL0VTEN	(1 << 8) /* PL0 Virtual timer reg access */
70#define	GT_CNTKCTL_EVNTI	(1 << 4) /* Virtual counter event bits */
71#define	GT_CNTKCTL_EVNTDIR	(1 << 3) /* Virtual counter event transition */
72#define	GT_CNTKCTL_EVNTEN	(1 << 2) /* Enables virtual counter events */
73#define	GT_CNTKCTL_PL0VCTEN	(1 << 1) /* PL0 CNTVCT and CNTFRQ access */
74#define	GT_CNTKCTL_PL0PCTEN	(1 << 0) /* PL0 CNTPCT and CNTFRQ access */
75
76struct arm_tmr_softc {
77	struct resource		*res[4];
78	void			*ihl[4];
79	uint32_t		clkfreq;
80	struct eventtimer	et;
81};
82
83static struct arm_tmr_softc *arm_tmr_sc = NULL;
84
85static struct resource_spec timer_spec[] = {
86	{ SYS_RES_IRQ,		0,	RF_ACTIVE },	/* Secure */
87	{ SYS_RES_IRQ,		1,	RF_ACTIVE },	/* Non-secure */
88	{ SYS_RES_IRQ,		2,	RF_ACTIVE },	/* Virt */
89	{ SYS_RES_IRQ,		3,	RF_ACTIVE },	/* Hyp */
90	{ -1, 0 }
91};
92
93static timecounter_get_t arm_tmr_get_timecount;
94
95static struct timecounter arm_tmr_timecount = {
96	.tc_name           = "ARM MPCore Timecounter",
97	.tc_get_timecount  = arm_tmr_get_timecount,
98	.tc_poll_pps       = NULL,
99	.tc_counter_mask   = ~0u,
100	.tc_frequency      = 0,
101	.tc_quality        = 1000,
102};
103
104static inline int
105get_freq(void)
106{
107	uint32_t val;
108
109	__asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
110
111	return (val);
112}
113
114static inline int
115set_freq(uint32_t val)
116{
117
118	__asm volatile("mcr p15, 0, %[val], c14, c0, 0" : :
119	    [val] "r" (val));
120	isb();
121
122	return (val);
123}
124
125
126static inline long
127get_cntpct(void)
128{
129	uint64_t val;
130
131	__asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val));
132
133	return (val);
134}
135
136static inline int
137set_ctrl(uint32_t val)
138{
139
140	__asm volatile("mcr p15, 0, %[val], c14, c2, 1" : :
141	    [val] "r" (val));
142	isb();
143
144	return (0);
145}
146
147static inline int
148set_tval(uint32_t val)
149{
150
151	__asm volatile("mcr p15, 0, %[val], c14, c2, 0" : :
152	    [val] "r" (val));
153	isb();
154
155	return (0);
156}
157
158static inline int
159get_ctrl(void)
160{
161	uint32_t val;
162
163	__asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
164
165	return (val);
166}
167
168static inline int
169get_tval(void)
170{
171	uint32_t val;
172
173	__asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
174
175	return (val);
176}
177
178static inline void
179disable_user_access(void)
180{
181	uint32_t cntkctl;
182
183	__asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
184	cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN |
185	    GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN);
186	__asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
187	isb();
188}
189
190static unsigned
191arm_tmr_get_timecount(struct timecounter *tc)
192{
193
194	return (get_cntpct());
195}
196
197static int
198arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
199{
200	struct arm_tmr_softc *sc;
201	int counts, ctrl;
202
203	sc = (struct arm_tmr_softc *)et->et_priv;
204
205	if (first != 0) {
206		counts = ((uint32_t)et->et_frequency * first) >> 32;
207		ctrl = get_ctrl();
208		ctrl &= ~GT_CTRL_INT_MASK;
209		ctrl |= GT_CTRL_ENABLE;
210		set_tval(counts);
211		set_ctrl(ctrl);
212		return (0);
213	}
214
215	return (EINVAL);
216
217}
218
219static int
220arm_tmr_stop(struct eventtimer *et)
221{
222	int ctrl;
223
224	ctrl = get_ctrl();
225	ctrl &= GT_CTRL_ENABLE;
226	set_ctrl(ctrl);
227
228	return (0);
229}
230
231static int
232arm_tmr_intr(void *arg)
233{
234	struct arm_tmr_softc *sc;
235	int ctrl;
236
237	sc = (struct arm_tmr_softc *)arg;
238	ctrl = get_ctrl();
239	if (ctrl & GT_CTRL_INT_STAT) {
240		ctrl |= GT_CTRL_INT_MASK;
241		set_ctrl(ctrl);
242	}
243
244	if (sc->et.et_active)
245		sc->et.et_event_cb(&sc->et, sc->et.et_arg);
246
247	return (FILTER_HANDLED);
248}
249
250static int
251arm_tmr_probe(device_t dev)
252{
253
254	if (!ofw_bus_status_okay(dev))
255		return (ENXIO);
256
257	if (!ofw_bus_is_compatible(dev, "arm,armv7-timer"))
258		return (ENXIO);
259
260	device_set_desc(dev, "ARMv7 Generic Timer");
261	return (BUS_PROBE_DEFAULT);
262}
263
264
265static int
266arm_tmr_attach(device_t dev)
267{
268	struct arm_tmr_softc *sc;
269	phandle_t node;
270	pcell_t clock;
271	int error;
272	int i;
273
274	sc = device_get_softc(dev);
275	if (arm_tmr_sc)
276		return (ENXIO);
277
278	/* Get the base clock frequency */
279	node = ofw_bus_get_node(dev);
280	error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock));
281	if (error > 0) {
282		sc->clkfreq = fdt32_to_cpu(clock);
283	}
284
285	if (sc->clkfreq == 0) {
286		/* Try to get clock frequency from timer */
287		sc->clkfreq = get_freq();
288	}
289
290	if (sc->clkfreq == 0) {
291		device_printf(dev, "No clock frequency specified\n");
292		return (ENXIO);
293	}
294
295	if (bus_alloc_resources(dev, timer_spec, sc->res)) {
296		device_printf(dev, "could not allocate resources\n");
297		return (ENXIO);
298	};
299
300	arm_tmr_sc = sc;
301
302	/* Setup secure and non-secure IRQs handler */
303	for (i = 0; i < 2; i++) {
304		error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK,
305		    arm_tmr_intr, NULL, sc, &sc->ihl[i]);
306		if (error) {
307			device_printf(dev, "Unable to alloc int resource.\n");
308			return (ENXIO);
309		}
310	}
311
312	disable_user_access();
313
314	arm_tmr_timecount.tc_frequency = sc->clkfreq;
315	tc_init(&arm_tmr_timecount);
316
317	sc->et.et_name = "ARM MPCore Eventtimer";
318	sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
319	sc->et.et_quality = 1000;
320
321	sc->et.et_frequency = sc->clkfreq;
322	sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
323	sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
324	sc->et.et_start = arm_tmr_start;
325	sc->et.et_stop = arm_tmr_stop;
326	sc->et.et_priv = sc;
327	et_register(&sc->et);
328
329	return (0);
330}
331
332static device_method_t arm_tmr_methods[] = {
333	DEVMETHOD(device_probe,		arm_tmr_probe),
334	DEVMETHOD(device_attach,	arm_tmr_attach),
335	{ 0, 0 }
336};
337
338static driver_t arm_tmr_driver = {
339	"generic_timer",
340	arm_tmr_methods,
341	sizeof(struct arm_tmr_softc),
342};
343
344static devclass_t arm_tmr_devclass;
345
346EARLY_DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
347    BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
348
349void
350DELAY(int usec)
351{
352	int32_t counts, counts_per_usec;
353	uint32_t first, last;
354
355	/*
356	 * Check the timers are setup, if not just
357	 * use a for loop for the meantime
358	 */
359	if (arm_tmr_sc == NULL) {
360		for (; usec > 0; usec--)
361			for (counts = 200; counts > 0; counts--)
362				/*
363				 * Prevent gcc from optimizing
364				 * out the loop
365				 */
366				cpufunc_nullop();
367		return;
368	}
369
370	/* Get the number of times to count */
371	counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
372
373	/*
374	 * Clamp the timeout at a maximum value (about 32 seconds with
375	 * a 66MHz clock). *Nobody* should be delay()ing for anywhere
376	 * near that length of time and if they are, they should be hung
377	 * out to dry.
378	 */
379	if (usec >= (0x80000000U / counts_per_usec))
380		counts = (0x80000000U / counts_per_usec) - 1;
381	else
382		counts = usec * counts_per_usec;
383
384	first = get_cntpct();
385
386	while (counts > 0) {
387		last = get_cntpct();
388		counts -= (int32_t)(last - first);
389		first = last;
390	}
391}
392