generic_timer.c revision 271189
1252372Sray/*-
2252372Sray * Copyright (c) 2011 The FreeBSD Foundation
3252372Sray * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
4252372Sray * All rights reserved.
5252372Sray *
6252372Sray * Based on mpcore_timer.c developed by Ben Gray <ben.r.gray@gmail.com>
7252372Sray *
8252372Sray * Redistribution and use in source and binary forms, with or without
9252372Sray * modification, are permitted provided that the following conditions
10252372Sray * are met:
11252372Sray * 1. Redistributions of source code must retain the above copyright
12252372Sray *    notice, this list of conditions and the following disclaimer.
13252372Sray * 2. Redistributions in binary form must reproduce the above copyright
14252372Sray *    notice, this list of conditions and the following disclaimer in the
15252372Sray *    documentation and/or other materials provided with the distribution.
16252372Sray * 3. The name of the company nor the name of the author may be used to
17252372Sray *    endorse or promote products derived from this software without specific
18252372Sray *    prior written permission.
19252372Sray *
20252372Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21252372Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22252372Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23252372Sray * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24252372Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25252372Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26252372Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27252372Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28252372Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29252372Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30252372Sray * SUCH DAMAGE.
31252372Sray */
32252372Sray
33252372Sray/**
34252372Sray *      Cortex-A15 (and probably A7) Generic Timer
35252372Sray */
36252372Sray
37252372Sray#include <sys/cdefs.h>
38252372Sray__FBSDID("$FreeBSD: head/sys/arm/arm/generic_timer.c 271189 2014-09-06 13:21:07Z andrew $");
39252372Sray
40252372Sray#include <sys/param.h>
41252372Sray#include <sys/systm.h>
42252372Sray#include <sys/bus.h>
43252372Sray#include <sys/kernel.h>
44252372Sray#include <sys/module.h>
45252372Sray#include <sys/malloc.h>
46252372Sray#include <sys/rman.h>
47252372Sray#include <sys/timeet.h>
48252372Sray#include <sys/timetc.h>
49252372Sray#include <sys/watchdog.h>
50252372Sray#include <machine/bus.h>
51252372Sray#include <machine/cpu.h>
52252372Sray#include <machine/intr.h>
53252372Sray
54252372Sray#include <dev/fdt/fdt_common.h>
55252372Sray#include <dev/ofw/openfirm.h>
56252372Sray#include <dev/ofw/ofw_bus.h>
57252372Sray#include <dev/ofw/ofw_bus_subr.h>
58252372Sray
59252372Sray#include <machine/bus.h>
60252372Sray#include <machine/fdt.h>
61252372Sray
62252780Sray#define	GT_CTRL_ENABLE		(1 << 0)
63252780Sray#define	GT_CTRL_INT_MASK	(1 << 1)
64252780Sray#define	GT_CTRL_INT_STAT	(1 << 2)
65252780Sray#define	GT_REG_CTRL		0
66252780Sray#define	GT_REG_TVAL		1
67252372Sray
68252780Sray#define	GT_CNTKCTL_PL0PTEN	(1 << 9) /* PL0 Physical timer reg access */
69252780Sray#define	GT_CNTKCTL_PL0VTEN	(1 << 8) /* PL0 Virtual timer reg access */
70271189Sandrew#define	GT_CNTKCTL_EVNTI	(0xf << 4) /* Virtual counter event bits */
71252780Sray#define	GT_CNTKCTL_EVNTDIR	(1 << 3) /* Virtual counter event transition */
72252780Sray#define	GT_CNTKCTL_EVNTEN	(1 << 2) /* Enables virtual counter events */
73252780Sray#define	GT_CNTKCTL_PL0VCTEN	(1 << 1) /* PL0 CNTVCT and CNTFRQ access */
74252780Sray#define	GT_CNTKCTL_PL0PCTEN	(1 << 0) /* PL0 CNTPCT and CNTFRQ access */
75252372Sray
76252372Sraystruct arm_tmr_softc {
77264065Sbr	struct resource		*res[4];
78264065Sbr	void			*ihl[4];
79252372Sray	uint32_t		clkfreq;
80252372Sray	struct eventtimer	et;
81271189Sandrew	bool			physical;
82252372Sray};
83252372Sray
84252372Sraystatic struct arm_tmr_softc *arm_tmr_sc = NULL;
85252372Sray
86264065Sbrstatic struct resource_spec timer_spec[] = {
87264065Sbr	{ SYS_RES_IRQ,		0,	RF_ACTIVE },	/* Secure */
88264065Sbr	{ SYS_RES_IRQ,		1,	RF_ACTIVE },	/* Non-secure */
89264065Sbr	{ SYS_RES_IRQ,		2,	RF_ACTIVE },	/* Virt */
90264065Sbr	{ SYS_RES_IRQ,		3,	RF_ACTIVE },	/* Hyp */
91264065Sbr	{ -1, 0 }
92264065Sbr};
93264065Sbr
94252372Sraystatic timecounter_get_t arm_tmr_get_timecount;
95252372Sray
96252372Sraystatic struct timecounter arm_tmr_timecount = {
97252372Sray	.tc_name           = "ARM MPCore Timecounter",
98252372Sray	.tc_get_timecount  = arm_tmr_get_timecount,
99252372Sray	.tc_poll_pps       = NULL,
100252372Sray	.tc_counter_mask   = ~0u,
101252372Sray	.tc_frequency      = 0,
102252372Sray	.tc_quality        = 1000,
103252372Sray};
104252372Sray
105271189Sandrewstatic int
106252372Srayget_freq(void)
107252372Sray{
108252372Sray	uint32_t val;
109252372Sray
110271189Sandrew	/* cntfrq */
111252372Sray	__asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
112252372Sray
113252372Sray	return (val);
114252372Sray}
115252372Sray
116271189Sandrewstatic long
117271189Sandrewget_cntxct(bool physical)
118252372Sray{
119271189Sandrew	uint64_t val;
120252372Sray
121252372Sray	isb();
122271189Sandrew	if (physical)
123271189Sandrew		/* cntpct */
124271189Sandrew		__asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val));
125271189Sandrew	else
126271189Sandrew		/* cntvct */
127271189Sandrew		__asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (val));
128252372Sray
129252372Sray	return (val);
130252372Sray}
131252372Sray
132271189Sandrewstatic int
133271189Sandrewset_ctrl(uint32_t val, bool physical)
134252372Sray{
135252372Sray
136271189Sandrew	if (physical)
137271189Sandrew		/* cntp_ctl */
138271189Sandrew		__asm volatile("mcr p15, 0, %[val], c14, c2, 1" : :
139271189Sandrew		    [val] "r" (val));
140271189Sandrew	else
141271189Sandrew		/* cntv_ctl */
142271189Sandrew		__asm volatile("mcr p15, 0, %[val], c14, c3, 1" : :
143271189Sandrew		    [val] "r" (val));
144252372Sray	isb();
145252372Sray
146252372Sray	return (0);
147252372Sray}
148252372Sray
149271189Sandrewstatic int
150271189Sandrewset_tval(uint32_t val, bool physical)
151252372Sray{
152252372Sray
153271189Sandrew	if (physical)
154271189Sandrew		/* cntp_tval */
155271189Sandrew		__asm volatile("mcr p15, 0, %[val], c14, c2, 0" : :
156271189Sandrew		    [val] "r" (val));
157271189Sandrew	else
158271189Sandrew		/* cntv_tval */
159271189Sandrew		__asm volatile("mcr p15, 0, %[val], c14, c3, 0" : :
160271189Sandrew		    [val] "r" (val));
161252372Sray	isb();
162252372Sray
163252372Sray	return (0);
164252372Sray}
165252372Sray
166271189Sandrewstatic int
167271189Sandrewget_ctrl(bool physical)
168252372Sray{
169252372Sray	uint32_t val;
170252372Sray
171271189Sandrew	if (physical)
172271189Sandrew		/* cntp_ctl */
173271189Sandrew		__asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
174271189Sandrew	else
175271189Sandrew		/* cntv_ctl */
176271189Sandrew		__asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val));
177252372Sray
178252372Sray	return (val);
179252372Sray}
180252372Sray
181271189Sandrewstatic void
182252372Sraydisable_user_access(void)
183252372Sray{
184252372Sray	uint32_t cntkctl;
185252372Sray
186252372Sray	__asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
187252780Sray	cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN |
188252780Sray	    GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN);
189252372Sray	__asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
190252372Sray	isb();
191252372Sray}
192252372Sray
193252372Sraystatic unsigned
194252372Srayarm_tmr_get_timecount(struct timecounter *tc)
195252372Sray{
196252372Sray
197271189Sandrew	return (get_cntxct(arm_tmr_sc->physical));
198252372Sray}
199252372Sray
200252372Sraystatic int
201252372Srayarm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
202252372Sray{
203252372Sray	struct arm_tmr_softc *sc;
204252372Sray	int counts, ctrl;
205252372Sray
206252372Sray	sc = (struct arm_tmr_softc *)et->et_priv;
207252372Sray
208252372Sray	if (first != 0) {
209252372Sray		counts = ((uint32_t)et->et_frequency * first) >> 32;
210271189Sandrew		ctrl = get_ctrl(sc->physical);
211252780Sray		ctrl &= ~GT_CTRL_INT_MASK;
212252780Sray		ctrl |= GT_CTRL_ENABLE;
213271189Sandrew		set_tval(counts, sc->physical);
214271189Sandrew		set_ctrl(ctrl, sc->physical);
215252372Sray		return (0);
216252372Sray	}
217252372Sray
218252372Sray	return (EINVAL);
219252372Sray
220252372Sray}
221252372Sray
222252372Sraystatic int
223252372Srayarm_tmr_stop(struct eventtimer *et)
224252372Sray{
225271189Sandrew	struct arm_tmr_softc *sc;
226252372Sray	int ctrl;
227252372Sray
228271189Sandrew	sc = (struct arm_tmr_softc *)et->et_priv;
229271189Sandrew
230271189Sandrew	ctrl = get_ctrl(sc->physical);
231252780Sray	ctrl &= GT_CTRL_ENABLE;
232271189Sandrew	set_ctrl(ctrl, sc->physical);
233252372Sray
234252372Sray	return (0);
235252372Sray}
236252372Sray
237252372Sraystatic int
238252372Srayarm_tmr_intr(void *arg)
239252372Sray{
240252372Sray	struct arm_tmr_softc *sc;
241252372Sray	int ctrl;
242252372Sray
243252372Sray	sc = (struct arm_tmr_softc *)arg;
244271189Sandrew	ctrl = get_ctrl(sc->physical);
245252780Sray	if (ctrl & GT_CTRL_INT_STAT) {
246252780Sray		ctrl |= GT_CTRL_INT_MASK;
247271189Sandrew		set_ctrl(ctrl, sc->physical);
248252372Sray	}
249252372Sray
250252372Sray	if (sc->et.et_active)
251252372Sray		sc->et.et_event_cb(&sc->et, sc->et.et_arg);
252252372Sray
253252372Sray	return (FILTER_HANDLED);
254252372Sray}
255252372Sray
256252372Sraystatic int
257252372Srayarm_tmr_probe(device_t dev)
258252372Sray{
259252372Sray
260261410Sian	if (!ofw_bus_status_okay(dev))
261261410Sian		return (ENXIO);
262261410Sian
263252372Sray	if (!ofw_bus_is_compatible(dev, "arm,armv7-timer"))
264252372Sray		return (ENXIO);
265252372Sray
266252372Sray	device_set_desc(dev, "ARMv7 Generic Timer");
267252372Sray	return (BUS_PROBE_DEFAULT);
268252372Sray}
269252372Sray
270252372Sray
271252372Sraystatic int
272252372Srayarm_tmr_attach(device_t dev)
273252372Sray{
274252372Sray	struct arm_tmr_softc *sc;
275252372Sray	phandle_t node;
276252372Sray	pcell_t clock;
277252372Sray	int error;
278264065Sbr	int i;
279252372Sray
280252372Sray	sc = device_get_softc(dev);
281252372Sray	if (arm_tmr_sc)
282252372Sray		return (ENXIO);
283252372Sray
284252372Sray	/* Get the base clock frequency */
285252372Sray	node = ofw_bus_get_node(dev);
286252372Sray	error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock));
287264065Sbr	if (error > 0) {
288264065Sbr		sc->clkfreq = fdt32_to_cpu(clock);
289264065Sbr	}
290264065Sbr
291264065Sbr	if (sc->clkfreq == 0) {
292264065Sbr		/* Try to get clock frequency from timer */
293264065Sbr		sc->clkfreq = get_freq();
294264065Sbr	}
295264065Sbr
296264065Sbr	if (sc->clkfreq == 0) {
297264065Sbr		device_printf(dev, "No clock frequency specified\n");
298252372Sray		return (ENXIO);
299252372Sray	}
300252372Sray
301264065Sbr	if (bus_alloc_resources(dev, timer_spec, sc->res)) {
302264065Sbr		device_printf(dev, "could not allocate resources\n");
303264065Sbr		return (ENXIO);
304271189Sandrew	}
305252372Sray
306271189Sandrew	sc->physical = true;
307271189Sandrew
308252372Sray	arm_tmr_sc = sc;
309252372Sray
310271189Sandrew	/* Setup secure, non-secure and virtual IRQs handler */
311271189Sandrew	for (i = 0; i < 3; i++) {
312264065Sbr		error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK,
313264065Sbr		    arm_tmr_intr, NULL, sc, &sc->ihl[i]);
314264065Sbr		if (error) {
315264065Sbr			device_printf(dev, "Unable to alloc int resource.\n");
316264065Sbr			return (ENXIO);
317264065Sbr		}
318252372Sray	}
319252372Sray
320252372Sray	disable_user_access();
321252372Sray
322252427Sray	arm_tmr_timecount.tc_frequency = sc->clkfreq;
323252372Sray	tc_init(&arm_tmr_timecount);
324252372Sray
325252372Sray	sc->et.et_name = "ARM MPCore Eventtimer";
326252372Sray	sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
327252372Sray	sc->et.et_quality = 1000;
328252372Sray
329252372Sray	sc->et.et_frequency = sc->clkfreq;
330252372Sray	sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
331252372Sray	sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
332252372Sray	sc->et.et_start = arm_tmr_start;
333252372Sray	sc->et.et_stop = arm_tmr_stop;
334252372Sray	sc->et.et_priv = sc;
335252372Sray	et_register(&sc->et);
336252372Sray
337252372Sray	return (0);
338252372Sray}
339252372Sray
340252372Sraystatic device_method_t arm_tmr_methods[] = {
341252372Sray	DEVMETHOD(device_probe,		arm_tmr_probe),
342252372Sray	DEVMETHOD(device_attach,	arm_tmr_attach),
343252372Sray	{ 0, 0 }
344252372Sray};
345252372Sray
346252372Sraystatic driver_t arm_tmr_driver = {
347252372Sray	"generic_timer",
348252372Sray	arm_tmr_methods,
349252372Sray	sizeof(struct arm_tmr_softc),
350252372Sray};
351252372Sray
352252372Sraystatic devclass_t arm_tmr_devclass;
353252372Sray
354269605SianEARLY_DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
355269605Sian    BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
356252372Sray
357252372Srayvoid
358252372SrayDELAY(int usec)
359252372Sray{
360252372Sray	int32_t counts, counts_per_usec;
361252372Sray	uint32_t first, last;
362252372Sray
363252372Sray	/*
364252372Sray	 * Check the timers are setup, if not just
365252372Sray	 * use a for loop for the meantime
366252427Sray	 */
367252372Sray	if (arm_tmr_sc == NULL) {
368252372Sray		for (; usec > 0; usec--)
369252372Sray			for (counts = 200; counts > 0; counts--)
370252372Sray				/*
371252372Sray				 * Prevent gcc from optimizing
372252372Sray				 * out the loop
373252372Sray				 */
374252372Sray				cpufunc_nullop();
375252372Sray		return;
376252372Sray	}
377252372Sray
378252372Sray	/* Get the number of times to count */
379252427Sray	counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
380252372Sray
381252372Sray	/*
382252372Sray	 * Clamp the timeout at a maximum value (about 32 seconds with
383252372Sray	 * a 66MHz clock). *Nobody* should be delay()ing for anywhere
384252372Sray	 * near that length of time and if they are, they should be hung
385252372Sray	 * out to dry.
386252372Sray	 */
387252372Sray	if (usec >= (0x80000000U / counts_per_usec))
388252372Sray		counts = (0x80000000U / counts_per_usec) - 1;
389252372Sray	else
390252372Sray		counts = usec * counts_per_usec;
391252372Sray
392271189Sandrew	first = get_cntxct(arm_tmr_sc->physical);
393252372Sray
394252372Sray	while (counts > 0) {
395271189Sandrew		last = get_cntxct(arm_tmr_sc->physical);
396252372Sray		counts -= (int32_t)(last - first);
397252372Sray		first = last;
398252372Sray	}
399252372Sray}
400