1239268Sgonzo/*-
2239268Sgonzo * Copyright (c) 2011 The FreeBSD Foundation
3239268Sgonzo * All rights reserved.
4239268Sgonzo *
5239268Sgonzo * Developed by Ben Gray <ben.r.gray@gmail.com>
6239268Sgonzo *
7239268Sgonzo * Redistribution and use in source and binary forms, with or without
8239268Sgonzo * modification, are permitted provided that the following conditions
9239268Sgonzo * are met:
10239268Sgonzo * 1. Redistributions of source code must retain the above copyright
11239268Sgonzo *    notice, this list of conditions and the following disclaimer.
12239268Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
13239268Sgonzo *    notice, this list of conditions and the following disclaimer in the
14239268Sgonzo *    documentation and/or other materials provided with the distribution.
15239268Sgonzo * 3. The name of the company nor the name of the author may be used to
16239268Sgonzo *    endorse or promote products derived from this software without specific
17239268Sgonzo *    prior written permission.
18239268Sgonzo *
19239268Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20239268Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21239268Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22239268Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23239268Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24239268Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25239268Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26239268Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27239268Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28239268Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29239268Sgonzo * SUCH DAMAGE.
30239268Sgonzo */
31239268Sgonzo
32239268Sgonzo/**
33266347Sian * The ARM Cortex-A9 core can support a global timer plus a private and
34266347Sian * watchdog timer per core.  This driver reserves memory and interrupt
35266347Sian * resources for accessing both timer register sets, these resources are
36266347Sian * stored globally and used to setup the timecount and eventtimer.
37239268Sgonzo *
38266347Sian * The timecount timer uses the global 64-bit counter, whereas the
39266347Sian * per-CPU eventtimer uses the private 32-bit counters.
40239268Sgonzo *
41239268Sgonzo *
42266347Sian * REF: ARM Cortex-A9 MPCore, Technical Reference Manual (rev. r2p2)
43239268Sgonzo */
44239268Sgonzo
45239268Sgonzo#include <sys/cdefs.h>
46239268Sgonzo__FBSDID("$FreeBSD$");
47239268Sgonzo
48239268Sgonzo#include <sys/param.h>
49239268Sgonzo#include <sys/systm.h>
50239268Sgonzo#include <sys/bus.h>
51239268Sgonzo#include <sys/kernel.h>
52239268Sgonzo#include <sys/module.h>
53239268Sgonzo#include <sys/malloc.h>
54239268Sgonzo#include <sys/rman.h>
55239268Sgonzo#include <sys/timeet.h>
56239268Sgonzo#include <sys/timetc.h>
57239268Sgonzo#include <sys/watchdog.h>
58239268Sgonzo#include <machine/bus.h>
59239268Sgonzo#include <machine/cpu.h>
60239268Sgonzo#include <machine/intr.h>
61239268Sgonzo
62239268Sgonzo#include <dev/fdt/fdt_common.h>
63239268Sgonzo#include <dev/ofw/openfirm.h>
64239268Sgonzo#include <dev/ofw/ofw_bus.h>
65239268Sgonzo#include <dev/ofw/ofw_bus_subr.h>
66239268Sgonzo
67239268Sgonzo#include <machine/bus.h>
68239268Sgonzo#include <machine/fdt.h>
69239268Sgonzo
70266347Sian#include <arm/arm/mpcore_timervar.h>
71266347Sian
72239268Sgonzo/* Private (per-CPU) timer register map */
73239268Sgonzo#define PRV_TIMER_LOAD                 0x0000
74239268Sgonzo#define PRV_TIMER_COUNT                0x0004
75239268Sgonzo#define PRV_TIMER_CTRL                 0x0008
76239268Sgonzo#define PRV_TIMER_INTR                 0x000C
77239268Sgonzo
78239268Sgonzo#define PRV_TIMER_CTR_PRESCALER_SHIFT  8
79239268Sgonzo#define PRV_TIMER_CTRL_IRQ_ENABLE      (1UL << 2)
80239268Sgonzo#define PRV_TIMER_CTRL_AUTO_RELOAD     (1UL << 1)
81239268Sgonzo#define PRV_TIMER_CTRL_TIMER_ENABLE    (1UL << 0)
82239268Sgonzo
83239268Sgonzo#define PRV_TIMER_INTR_EVENT           (1UL << 0)
84239268Sgonzo
85239268Sgonzo/* Global timer register map */
86239268Sgonzo#define GBL_TIMER_COUNT_LOW            0x0000
87239268Sgonzo#define GBL_TIMER_COUNT_HIGH           0x0004
88239268Sgonzo#define GBL_TIMER_CTRL                 0x0008
89239268Sgonzo#define GBL_TIMER_INTR                 0x000C
90239268Sgonzo
91239268Sgonzo#define GBL_TIMER_CTR_PRESCALER_SHIFT  8
92239268Sgonzo#define GBL_TIMER_CTRL_AUTO_INC        (1UL << 3)
93239268Sgonzo#define GBL_TIMER_CTRL_IRQ_ENABLE      (1UL << 2)
94239268Sgonzo#define GBL_TIMER_CTRL_COMP_ENABLE     (1UL << 1)
95239268Sgonzo#define GBL_TIMER_CTRL_TIMER_ENABLE    (1UL << 0)
96239268Sgonzo
97239268Sgonzo#define GBL_TIMER_INTR_EVENT           (1UL << 0)
98239268Sgonzo
99239268Sgonzostruct arm_tmr_softc {
100239268Sgonzo	struct resource *	tmr_res[4];
101239268Sgonzo	bus_space_tag_t		prv_bst;
102239268Sgonzo	bus_space_tag_t		gbl_bst;
103239268Sgonzo	bus_space_handle_t	prv_bsh;
104239268Sgonzo	bus_space_handle_t	gbl_bsh;
105266347Sian	uint64_t		clkfreq;
106239268Sgonzo	struct eventtimer	et;
107239268Sgonzo};
108239268Sgonzo
109239268Sgonzostatic struct resource_spec arm_tmr_spec[] = {
110239268Sgonzo	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },	/* Global registers */
111239268Sgonzo	{ SYS_RES_IRQ,		0,	RF_ACTIVE },    /* Global timer interrupt (unused) */
112239268Sgonzo	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },	/* Private (per-CPU) registers */
113239268Sgonzo	{ SYS_RES_IRQ,		1,	RF_ACTIVE },    /* Private timer interrupt */
114239268Sgonzo	{ -1, 0 }
115239268Sgonzo};
116239268Sgonzo
117239268Sgonzostatic struct arm_tmr_softc *arm_tmr_sc = NULL;
118239268Sgonzo
119266347Sianstatic uint64_t platform_arm_tmr_freq = 0;
120253971Scognet
121239268Sgonzo#define	tmr_prv_read_4(reg)		\
122239268Sgonzo    bus_space_read_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg)
123239268Sgonzo#define	tmr_prv_write_4(reg, val)		\
124239268Sgonzo    bus_space_write_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg, val)
125239268Sgonzo#define	tmr_gbl_read_4(reg)		\
126239268Sgonzo    bus_space_read_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg)
127239268Sgonzo#define	tmr_gbl_write_4(reg, val)		\
128239268Sgonzo    bus_space_write_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg, val)
129239268Sgonzo
130239268Sgonzo
131239268Sgonzostatic timecounter_get_t arm_tmr_get_timecount;
132239268Sgonzo
133239268Sgonzostatic struct timecounter arm_tmr_timecount = {
134266207Sian	.tc_name           = "MPCore",
135239268Sgonzo	.tc_get_timecount  = arm_tmr_get_timecount,
136239268Sgonzo	.tc_poll_pps       = NULL,
137239268Sgonzo	.tc_counter_mask   = ~0u,
138239268Sgonzo	.tc_frequency      = 0,
139266207Sian	.tc_quality        = 800,
140239268Sgonzo};
141239268Sgonzo
142239268Sgonzo/**
143239268Sgonzo *	arm_tmr_get_timecount - reads the timecount (global) timer
144239268Sgonzo *	@tc: pointer to arm_tmr_timecount struct
145239268Sgonzo *
146239268Sgonzo *	We only read the lower 32-bits, the timecount stuff only uses 32-bits
147239268Sgonzo *	so (for now?) ignore the upper 32-bits.
148239268Sgonzo *
149239268Sgonzo *	RETURNS
150239268Sgonzo *	The lower 32-bits of the counter.
151239268Sgonzo */
152239268Sgonzostatic unsigned
153239268Sgonzoarm_tmr_get_timecount(struct timecounter *tc)
154239268Sgonzo{
155239268Sgonzo	return (tmr_gbl_read_4(GBL_TIMER_COUNT_LOW));
156239268Sgonzo}
157239268Sgonzo
158239268Sgonzo/**
159239268Sgonzo *	arm_tmr_start - starts the eventtimer (private) timer
160239268Sgonzo *	@et: pointer to eventtimer struct
161239268Sgonzo *	@first: the number of seconds and fractional sections to trigger in
162239268Sgonzo *	@period: the period (in seconds and fractional sections) to set
163239268Sgonzo *
164239268Sgonzo *	If the eventtimer is required to be in oneshot mode, period will be
165239268Sgonzo *	NULL and first will point to the time to trigger.  If in periodic mode
166239268Sgonzo *	period will contain the time period and first may optionally contain
167239268Sgonzo *	the time for the first period.
168239268Sgonzo *
169239268Sgonzo *	RETURNS
170239268Sgonzo *	Always returns 0
171239268Sgonzo */
172239268Sgonzostatic int
173247463Smavarm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
174239268Sgonzo{
175239268Sgonzo	uint32_t load, count;
176239268Sgonzo	uint32_t ctrl;
177239268Sgonzo
178266347Sian	tmr_prv_write_4(PRV_TIMER_CTRL, 0);
179266347Sian	tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
180266347Sian
181239268Sgonzo	ctrl = PRV_TIMER_CTRL_IRQ_ENABLE | PRV_TIMER_CTRL_TIMER_ENABLE;
182239268Sgonzo
183247463Smav	if (period != 0) {
184247463Smav		load = ((uint32_t)et->et_frequency * period) >> 32;
185239268Sgonzo		ctrl |= PRV_TIMER_CTRL_AUTO_RELOAD;
186247463Smav	} else
187239268Sgonzo		load = 0;
188239268Sgonzo
189247463Smav	if (first != 0)
190266347Sian		count = (uint32_t)((et->et_frequency * first) >> 32);
191247463Smav	else
192239268Sgonzo		count = load;
193239268Sgonzo
194239268Sgonzo	tmr_prv_write_4(PRV_TIMER_LOAD, load);
195239268Sgonzo	tmr_prv_write_4(PRV_TIMER_COUNT, count);
196266347Sian	tmr_prv_write_4(PRV_TIMER_CTRL, ctrl);
197239268Sgonzo
198239268Sgonzo	return (0);
199239268Sgonzo}
200239268Sgonzo
201239268Sgonzo/**
202239268Sgonzo *	arm_tmr_stop - stops the eventtimer (private) timer
203239268Sgonzo *	@et: pointer to eventtimer struct
204239268Sgonzo *
205239268Sgonzo *	Simply stops the private timer by clearing all bits in the ctrl register.
206239268Sgonzo *
207239268Sgonzo *	RETURNS
208239268Sgonzo *	Always returns 0
209239268Sgonzo */
210239268Sgonzostatic int
211239268Sgonzoarm_tmr_stop(struct eventtimer *et)
212239268Sgonzo{
213239268Sgonzo	tmr_prv_write_4(PRV_TIMER_CTRL, 0);
214266347Sian	tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
215239268Sgonzo	return (0);
216239268Sgonzo}
217239268Sgonzo
218239268Sgonzo/**
219239268Sgonzo *	arm_tmr_intr - ISR for the eventtimer (private) timer
220239268Sgonzo *	@arg: pointer to arm_tmr_softc struct
221239268Sgonzo *
222239268Sgonzo *	Clears the event register and then calls the eventtimer callback.
223239268Sgonzo *
224239268Sgonzo *	RETURNS
225239268Sgonzo *	Always returns FILTER_HANDLED
226239268Sgonzo */
227239268Sgonzostatic int
228239268Sgonzoarm_tmr_intr(void *arg)
229239268Sgonzo{
230239268Sgonzo	struct arm_tmr_softc *sc = (struct arm_tmr_softc *)arg;
231239268Sgonzo
232239268Sgonzo	tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
233239268Sgonzo
234239268Sgonzo	if (sc->et.et_active)
235239268Sgonzo		sc->et.et_event_cb(&sc->et, sc->et.et_arg);
236239268Sgonzo
237239268Sgonzo	return (FILTER_HANDLED);
238239268Sgonzo}
239239268Sgonzo
240239268Sgonzo
241239268Sgonzo
242239268Sgonzo
243239268Sgonzo/**
244239268Sgonzo *	arm_tmr_probe - timer probe routine
245239268Sgonzo *	@dev: new device
246239268Sgonzo *
247239268Sgonzo *	The probe function returns success when probed with the fdt compatible
248239268Sgonzo *	string set to "arm,mpcore-timers".
249239268Sgonzo *
250239268Sgonzo *	RETURNS
251239268Sgonzo *	BUS_PROBE_DEFAULT if the fdt device is compatible, otherwise ENXIO.
252239268Sgonzo */
253239268Sgonzostatic int
254239268Sgonzoarm_tmr_probe(device_t dev)
255239268Sgonzo{
256266152Sian
257266152Sian	if (!ofw_bus_status_okay(dev))
258266152Sian		return (ENXIO);
259266152Sian
260239268Sgonzo	if (!ofw_bus_is_compatible(dev, "arm,mpcore-timers"))
261239268Sgonzo		return (ENXIO);
262239268Sgonzo
263266207Sian	device_set_desc(dev, "ARM MPCore Timers");
264239268Sgonzo	return (BUS_PROBE_DEFAULT);
265239268Sgonzo}
266239268Sgonzo
267239268Sgonzo/**
268239268Sgonzo *	arm_tmr_attach - attaches the timer to the simplebus
269239268Sgonzo *	@dev: new device
270239268Sgonzo *
271239268Sgonzo *	Reserves memory and interrupt resources, stores the softc structure
272239268Sgonzo *	globally and registers both the timecount and eventtimer objects.
273239268Sgonzo *
274239268Sgonzo *	RETURNS
275239268Sgonzo *	Zero on sucess or ENXIO if an error occuried.
276239268Sgonzo */
277239268Sgonzostatic int
278239268Sgonzoarm_tmr_attach(device_t dev)
279239268Sgonzo{
280239268Sgonzo	struct arm_tmr_softc *sc = device_get_softc(dev);
281239268Sgonzo	phandle_t node;
282239268Sgonzo	pcell_t clock;
283239268Sgonzo	void *ihl;
284266347Sian	boolean_t fixed_freq;
285239268Sgonzo
286239268Sgonzo	if (arm_tmr_sc)
287239268Sgonzo		return (ENXIO);
288239268Sgonzo
289266347Sian	if (platform_arm_tmr_freq == ARM_TMR_FREQUENCY_VARIES) {
290266347Sian		fixed_freq = false;
291266347Sian	} else {
292266347Sian		fixed_freq = true;
293266347Sian		if (platform_arm_tmr_freq != 0) {
294266347Sian			sc->clkfreq = platform_arm_tmr_freq;
295266347Sian		} else {
296266347Sian			/* Get the base clock frequency */
297266347Sian			node = ofw_bus_get_node(dev);
298266347Sian			if ((OF_getencprop(node, "clock-frequency", &clock,
299266347Sian			    sizeof(clock))) <= 0) {
300266347Sian				device_printf(dev, "missing clock-frequency "
301266347Sian				    "attribute in FDT\n");
302266347Sian				return (ENXIO);
303266347Sian			}
304266352Sian			sc->clkfreq = clock;
305253971Scognet		}
306239268Sgonzo	}
307239268Sgonzo
308239268Sgonzo	if (bus_alloc_resources(dev, arm_tmr_spec, sc->tmr_res)) {
309239268Sgonzo		device_printf(dev, "could not allocate resources\n");
310239268Sgonzo		return (ENXIO);
311239268Sgonzo	}
312239268Sgonzo
313239268Sgonzo	/* Global timer interface */
314239268Sgonzo	sc->gbl_bst = rman_get_bustag(sc->tmr_res[0]);
315239268Sgonzo	sc->gbl_bsh = rman_get_bushandle(sc->tmr_res[0]);
316239268Sgonzo
317239268Sgonzo	/* Private per-CPU timer interface */
318239268Sgonzo	sc->prv_bst = rman_get_bustag(sc->tmr_res[2]);
319239268Sgonzo	sc->prv_bsh = rman_get_bushandle(sc->tmr_res[2]);
320239268Sgonzo
321239268Sgonzo	arm_tmr_sc = sc;
322239268Sgonzo
323239268Sgonzo	/* Disable both timers to start off */
324239268Sgonzo	tmr_prv_write_4(PRV_TIMER_CTRL, 0x00000000);
325239268Sgonzo	tmr_gbl_write_4(GBL_TIMER_CTRL, 0x00000000);
326239268Sgonzo
327239268Sgonzo	if (bus_setup_intr(dev, sc->tmr_res[3], INTR_TYPE_CLK, arm_tmr_intr,
328239268Sgonzo			NULL, sc, &ihl) != 0) {
329239268Sgonzo		bus_release_resources(dev, arm_tmr_spec, sc->tmr_res);
330239268Sgonzo		device_printf(dev, "Unable to setup the clock irq handler.\n");
331239268Sgonzo		return (ENXIO);
332239268Sgonzo	}
333239268Sgonzo
334266347Sian	/*
335266347Sian	 * If the clock is fixed-frequency, setup and enable the global timer to
336266347Sian	 * use as the timecounter.  If it's variable frequency it won't work as
337266347Sian	 * a timecounter.  We also can't use it for DELAY(), so hopefully the
338266347Sian	 * platform provides its own implementation.  If it doesn't, ours will
339266347Sian	 * get used, but since the frequency isn't set, it will only use the
340266347Sian	 * bogus loop counter.
341266347Sian	 */
342266347Sian	if (fixed_freq)  {
343266347Sian		tmr_gbl_write_4(GBL_TIMER_CTRL, GBL_TIMER_CTRL_TIMER_ENABLE);
344266347Sian		arm_tmr_timecount.tc_frequency = sc->clkfreq;
345266347Sian		tc_init(&arm_tmr_timecount);
346266347Sian	}
347266347Sian
348266347Sian	/*
349266347Sian	 * Setup and register the eventtimer.  Most event timers set their min
350266347Sian	 * and max period values to some value calculated from the clock
351266347Sian	 * frequency.  We might not know yet what our runtime clock frequency
352266347Sian	 * will be, so we just use some safe values.  A max of 2 seconds ensures
353266347Sian	 * that even if our base clock frequency is 2GHz (meaning a 4GHz CPU),
354266347Sian	 * we won't overflow our 32-bit timer count register.  A min of 20
355266347Sian	 * nanoseconds is pretty much completely arbitrary.
356266347Sian	 */
357266207Sian	sc->et.et_name = "MPCore";
358239268Sgonzo	sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
359239268Sgonzo	sc->et.et_quality = 1000;
360239268Sgonzo	sc->et.et_frequency = sc->clkfreq;
361266347Sian	sc->et.et_min_period = 20 * SBT_1NS;
362266347Sian	sc->et.et_max_period =  2 * SBT_1S;
363239268Sgonzo	sc->et.et_start = arm_tmr_start;
364239268Sgonzo	sc->et.et_stop = arm_tmr_stop;
365239268Sgonzo	sc->et.et_priv = sc;
366239268Sgonzo	et_register(&sc->et);
367239268Sgonzo
368239268Sgonzo	return (0);
369239268Sgonzo}
370239268Sgonzo
371239268Sgonzostatic device_method_t arm_tmr_methods[] = {
372239268Sgonzo	DEVMETHOD(device_probe,		arm_tmr_probe),
373239268Sgonzo	DEVMETHOD(device_attach,	arm_tmr_attach),
374239268Sgonzo	{ 0, 0 }
375239268Sgonzo};
376239268Sgonzo
377239268Sgonzostatic driver_t arm_tmr_driver = {
378239268Sgonzo	"mp_tmr",
379239268Sgonzo	arm_tmr_methods,
380239268Sgonzo	sizeof(struct arm_tmr_softc),
381239268Sgonzo};
382239268Sgonzo
383239268Sgonzostatic devclass_t arm_tmr_devclass;
384239268Sgonzo
385270075SianEARLY_DRIVER_MODULE(mp_tmr, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
386270075Sian    BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
387239268Sgonzo
388266347Sian/*
389266347Sian * Handle a change in clock frequency.  The mpcore timer runs at half the CPU
390266347Sian * frequency.  When the CPU frequency changes due to power-saving or thermal
391266347Sian * managment, the platform-specific code that causes the frequency change calls
392266347Sian * this routine to inform the clock driver, and we in turn inform the event
393266347Sian * timer system, which actually updates the value in et->frequency for us and
394266347Sian * reschedules the current event(s) in a way that's atomic with respect to
395266347Sian * start/stop/intr code that may be running on various CPUs at the time of the
396266347Sian * call.
397266347Sian *
398266347Sian * This routine can also be called by a platform's early init code.  If the
399266347Sian * value passed is ARM_TMR_FREQUENCY_VARIES, that will cause the attach() code
400266347Sian * to register as an eventtimer, but not a timecounter.  If the value passed in
401266347Sian * is any other non-zero value it is used as the fixed frequency for the timer.
402266347Sian */
403266347Sianvoid
404266347Sianarm_tmr_change_frequency(uint64_t newfreq)
405266347Sian{
406266347Sian
407266347Sian	if (arm_tmr_sc == NULL)
408266347Sian		platform_arm_tmr_freq = newfreq;
409266347Sian	else
410266347Sian		et_change_frequency(&arm_tmr_sc->et, newfreq);
411266347Sian}
412266347Sian
413239268Sgonzo/**
414239268Sgonzo *	DELAY - Delay for at least usec microseconds.
415239268Sgonzo *	@usec: number of microseconds to delay by
416239268Sgonzo *
417239268Sgonzo *	This function is called all over the kernel and is suppose to provide a
418239268Sgonzo *	consistent delay.  This function may also be called before the console
419239268Sgonzo *	is setup so no printf's can be called here.
420239268Sgonzo *
421239268Sgonzo *	RETURNS:
422239268Sgonzo *	nothing
423239268Sgonzo */
424266207Sianstatic void __used /* Must emit function code for the weak ref below. */
425266207Sianarm_tmr_DELAY(int usec)
426239268Sgonzo{
427239268Sgonzo	int32_t counts_per_usec;
428239268Sgonzo	int32_t counts;
429239268Sgonzo	uint32_t first, last;
430239268Sgonzo
431239268Sgonzo	/* Check the timers are setup, if not just use a for loop for the meantime */
432266347Sian	if (arm_tmr_sc == NULL || arm_tmr_timecount.tc_frequency == 0) {
433239268Sgonzo		for (; usec > 0; usec--)
434239268Sgonzo			for (counts = 200; counts > 0; counts--)
435239268Sgonzo				cpufunc_nullop();	/* Prevent gcc from optimizing
436239268Sgonzo							 * out the loop
437239268Sgonzo							 */
438239268Sgonzo		return;
439239268Sgonzo	}
440239268Sgonzo
441239268Sgonzo	/* Get the number of times to count */
442239268Sgonzo	counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
443239268Sgonzo
444239268Sgonzo	/*
445239268Sgonzo	 * Clamp the timeout at a maximum value (about 32 seconds with
446239268Sgonzo	 * a 66MHz clock). *Nobody* should be delay()ing for anywhere
447239268Sgonzo	 * near that length of time and if they are, they should be hung
448239268Sgonzo	 * out to dry.
449239268Sgonzo	 */
450239268Sgonzo	if (usec >= (0x80000000U / counts_per_usec))
451239268Sgonzo		counts = (0x80000000U / counts_per_usec) - 1;
452239268Sgonzo	else
453239268Sgonzo		counts = usec * counts_per_usec;
454239268Sgonzo
455239268Sgonzo	first = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW);
456239268Sgonzo
457239268Sgonzo	while (counts > 0) {
458239268Sgonzo		last = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW);
459239268Sgonzo		counts -= (int32_t)(last - first);
460239268Sgonzo		first = last;
461239268Sgonzo	}
462239268Sgonzo}
463266207Sian
464266207Sian/*
465266207Sian * Supply a DELAY() implementation via weak linkage.  A platform may want to use
466266207Sian * the mpcore per-cpu eventtimers but provide its own DELAY() routine,
467266207Sian * especially when the core frequency can change on the fly.
468266207Sian */
469266207Sian__weak_reference(arm_tmr_DELAY, DELAY);
470266207Sian
471