tick.c revision 331722
1/*-
2 * Copyright 2003-2011 Netlogic Microsystems (Netlogic). All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in
13 *    the documentation and/or other materials provided with the
14 *    distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY Netlogic Microsystems ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NETLOGIC OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * NETLOGIC_BSD */
29
30/*
31 * Simple driver for the 32-bit interval counter built in to all
32 * MIPS32 CPUs.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: stable/11/sys/mips/nlm/tick.c 331722 2018-03-29 02:50:57Z eadler $");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/sysctl.h>
41#include <sys/bus.h>
42#include <sys/kernel.h>
43#include <sys/module.h>
44#include <sys/rman.h>
45#include <sys/power.h>
46#include <sys/smp.h>
47#include <sys/time.h>
48#include <sys/timeet.h>
49#include <sys/timetc.h>
50
51#include <machine/hwfunc.h>
52#include <machine/clock.h>
53#include <machine/locore.h>
54#include <machine/md_var.h>
55#include <machine/intr_machdep.h>
56
57#include <mips/nlm/interrupt.h>
58
59uint64_t counter_freq;
60
61struct timecounter *platform_timecounter;
62
63static DPCPU_DEFINE(uint32_t, cycles_per_tick);
64static uint32_t cycles_per_usec;
65
66static DPCPU_DEFINE(volatile uint32_t, counter_upper);
67static DPCPU_DEFINE(volatile uint32_t, counter_lower_last);
68static DPCPU_DEFINE(uint32_t, compare_ticks);
69static DPCPU_DEFINE(uint32_t, lost_ticks);
70
71struct clock_softc {
72	int intr_rid;
73	struct resource *intr_res;
74	void *intr_handler;
75	struct timecounter tc;
76	struct eventtimer et;
77};
78static struct clock_softc *softc;
79
80/*
81 * Device methods
82 */
83static int clock_probe(device_t);
84static void clock_identify(driver_t *, device_t);
85static int clock_attach(device_t);
86static unsigned counter_get_timecount(struct timecounter *tc);
87
88void
89mips_timer_early_init(uint64_t clock_hz)
90{
91	/* Initialize clock early so that we can use DELAY sooner */
92	counter_freq = clock_hz;
93	cycles_per_usec = (clock_hz / (1000 * 1000));
94}
95
96void
97platform_initclocks(void)
98{
99
100	if (platform_timecounter != NULL)
101		tc_init(platform_timecounter);
102}
103
104static uint64_t
105tick_ticker(void)
106{
107	uint64_t ret;
108	uint32_t ticktock;
109	uint32_t t_lower_last, t_upper;
110
111	/*
112	 * Disable preemption because we are working with cpu specific data.
113	 */
114	critical_enter();
115
116	/*
117	 * Note that even though preemption is disabled, interrupts are
118	 * still enabled. In particular there is a race with clock_intr()
119	 * reading the values of 'counter_upper' and 'counter_lower_last'.
120	 *
121	 * XXX this depends on clock_intr() being executed periodically
122	 * so that 'counter_upper' and 'counter_lower_last' are not stale.
123	 */
124	do {
125		t_upper = DPCPU_GET(counter_upper);
126		t_lower_last = DPCPU_GET(counter_lower_last);
127	} while (t_upper != DPCPU_GET(counter_upper));
128
129	ticktock = mips_rd_count();
130
131	critical_exit();
132
133	/* COUNT register wrapped around */
134	if (ticktock < t_lower_last)
135		t_upper++;
136
137	ret = ((uint64_t)t_upper << 32) | ticktock;
138	return (ret);
139}
140
141void
142mips_timer_init_params(uint64_t platform_counter_freq, int double_count)
143{
144
145	/*
146	 * XXX: Do not use printf here: uart code 8250 may use DELAY so this
147	 * function should  be called before cninit.
148	 */
149	counter_freq = platform_counter_freq;
150	/*
151	 * XXX: Some MIPS32 cores update the Count register only every two
152	 * pipeline cycles.
153	 * We know this because of status registers in CP0, make it automatic.
154	 */
155	if (double_count != 0)
156		counter_freq /= 2;
157
158	cycles_per_usec = counter_freq / (1 * 1000 * 1000);
159	set_cputicker(tick_ticker, counter_freq, 1);
160}
161
162static int
163sysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS)
164{
165	int error;
166	uint64_t freq;
167
168	if (softc == NULL)
169		return (EOPNOTSUPP);
170	freq = counter_freq;
171	error = sysctl_handle_64(oidp, &freq, sizeof(freq), req);
172	if (error == 0 && req->newptr != NULL) {
173		counter_freq = freq;
174		softc->et.et_frequency = counter_freq;
175		softc->tc.tc_frequency = counter_freq;
176	}
177	return (error);
178}
179
180SYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_U64 | CTLFLAG_RW,
181    NULL, 0, sysctl_machdep_counter_freq, "QU",
182    "Timecounter frequency in Hz");
183
184static unsigned
185counter_get_timecount(struct timecounter *tc)
186{
187
188	return (mips_rd_count());
189}
190
191/*
192 * Wait for about n microseconds (at least!).
193 */
194void
195DELAY(int n)
196{
197	uint32_t cur, last, delta, usecs;
198
199	/*
200	 * This works by polling the timer and counting the number of
201	 * microseconds that go by.
202	 */
203	last = mips_rd_count();
204	delta = usecs = 0;
205
206	while (n > usecs) {
207		cur = mips_rd_count();
208
209		/* Check to see if the timer has wrapped around. */
210		if (cur < last)
211			delta += cur + (0xffffffff - last) + 1;
212		else
213			delta += cur - last;
214
215		last = cur;
216
217		if (delta >= cycles_per_usec) {
218			usecs += delta / cycles_per_usec;
219			delta %= cycles_per_usec;
220		}
221	}
222}
223
224static int
225clock_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
226{
227	uint32_t fdiv, div, next;
228
229	if (period != 0)
230		div = (et->et_frequency * period) >> 32;
231	else
232		div = 0;
233	if (first != 0)
234		fdiv = (et->et_frequency * first) >> 32;
235	else
236		fdiv = div;
237	DPCPU_SET(cycles_per_tick, div);
238	next = mips_rd_count() + fdiv;
239	DPCPU_SET(compare_ticks, next);
240	mips_wr_compare(next);
241	return (0);
242}
243
244static int
245clock_stop(struct eventtimer *et)
246{
247
248	DPCPU_SET(cycles_per_tick, 0);
249	mips_wr_compare(0xffffffff);
250	return (0);
251}
252
253/*
254 * Device section of file below
255 */
256static int
257clock_intr(void *arg)
258{
259	struct clock_softc *sc = (struct clock_softc *)arg;
260	uint32_t cycles_per_tick;
261	uint32_t count, compare_last, compare_next, lost_ticks;
262
263	cycles_per_tick = DPCPU_GET(cycles_per_tick);
264	/*
265	 * Set next clock edge.
266	 */
267	count = mips_rd_count();
268	compare_last = DPCPU_GET(compare_ticks);
269	if (cycles_per_tick > 0) {
270		compare_next = count + cycles_per_tick;
271		DPCPU_SET(compare_ticks, compare_next);
272		mips_wr_compare(compare_next);
273	} else	/* In one-shot mode timer should be stopped after the event. */
274		mips_wr_compare(0xffffffff);
275
276	/* COUNT register wrapped around */
277	if (count < DPCPU_GET(counter_lower_last)) {
278		DPCPU_SET(counter_upper, DPCPU_GET(counter_upper) + 1);
279	}
280	DPCPU_SET(counter_lower_last, count);
281
282	if (cycles_per_tick > 0) {
283
284		/*
285		 * Account for the "lost time" between when the timer interrupt
286		 * fired and when 'clock_intr' actually started executing.
287		 */
288		lost_ticks = DPCPU_GET(lost_ticks);
289		lost_ticks += count - compare_last;
290
291		/*
292		 * If the COUNT and COMPARE registers are no longer in sync
293		 * then make up some reasonable value for the 'lost_ticks'.
294		 *
295		 * This could happen, for e.g., after we resume normal
296		 * operations after exiting the debugger.
297		 */
298		if (lost_ticks > 2 * cycles_per_tick)
299			lost_ticks = cycles_per_tick;
300
301		while (lost_ticks >= cycles_per_tick) {
302			if (sc->et.et_active)
303				sc->et.et_event_cb(&sc->et, sc->et.et_arg);
304			lost_ticks -= cycles_per_tick;
305		}
306		DPCPU_SET(lost_ticks, lost_ticks);
307	}
308	if (sc->et.et_active)
309		sc->et.et_event_cb(&sc->et, sc->et.et_arg);
310	return (FILTER_HANDLED);
311}
312
313static int
314clock_probe(device_t dev)
315{
316
317	device_set_desc(dev, "Generic MIPS32 ticker");
318	return (BUS_PROBE_NOWILDCARD);
319}
320
321static void
322clock_identify(driver_t * drv, device_t parent)
323{
324
325	BUS_ADD_CHILD(parent, 0, "clock", 0);
326}
327
328static int
329clock_attach(device_t dev)
330{
331	struct clock_softc *sc;
332
333	if (device_get_unit(dev) != 0)
334		panic("can't attach more clocks");
335
336	softc = sc = device_get_softc(dev);
337	cpu_establish_hardintr("compare", clock_intr, NULL,
338	    sc, IRQ_TIMER, INTR_TYPE_CLK, &sc->intr_handler);
339
340	sc->tc.tc_get_timecount = counter_get_timecount;
341	sc->tc.tc_counter_mask = 0xffffffff;
342	sc->tc.tc_frequency = counter_freq;
343	sc->tc.tc_name = "MIPS32";
344	sc->tc.tc_quality = 800;
345	sc->tc.tc_priv = sc;
346	tc_init(&sc->tc);
347	sc->et.et_name = "MIPS32";
348#if 0
349	sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT |
350	    ET_FLAGS_PERCPU;
351#endif
352	sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_PERCPU;
353	sc->et.et_quality = 800;
354	sc->et.et_frequency = counter_freq;
355	sc->et.et_min_period = 0x00004000LLU; /* To be safe. */
356	sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
357	sc->et.et_start = clock_start;
358	sc->et.et_stop = clock_stop;
359	sc->et.et_priv = sc;
360	et_register(&sc->et);
361	return (0);
362}
363
364static device_method_t clock_methods[] = {
365	/* Device interface */
366	DEVMETHOD(device_probe, clock_probe),
367	DEVMETHOD(device_identify, clock_identify),
368	DEVMETHOD(device_attach, clock_attach),
369	DEVMETHOD(device_detach, bus_generic_detach),
370	DEVMETHOD(device_shutdown, bus_generic_shutdown),
371
372	{0, 0}
373};
374
375static driver_t clock_driver = {
376	"clock",
377	clock_methods,
378	sizeof(struct clock_softc),
379};
380
381static devclass_t clock_devclass;
382
383DRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0);
384