2 * Copyright (c) 2006-2007 Bruce M. Simpson.
3 * Copyright (c) 2003-2004 Juli Mallett.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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 the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * Simple driver for the 32-bit interval counter built in to all
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include "opt_cputype.h"
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/sysctl.h>
42 #include <sys/kernel.h>
43 #include <sys/module.h>
45 #include <sys/power.h>
48 #include <sys/timetc.h>
50 #include <machine/hwfunc.h>
51 #include <machine/clock.h>
52 #include <machine/locore.h>
53 #include <machine/md_var.h>
54 #include <machine/intr_machdep.h>
55 #include <mips/rmi/interrupt.h>
58 uint64_t counter_freq;
60 struct timecounter *platform_timecounter;
62 static uint64_t cycles_per_tick;
63 static uint64_t cycles_per_usec;
64 static uint64_t cycles_per_hz, cycles_per_stathz, cycles_per_profhz;
70 uint32_t compare_ticks;
71 u_int32_t counter_upper;
72 u_int32_t counter_lower_last;
73 } __aligned(CACHE_LINE_SIZE);
75 static struct clk_ticks pcpu_ticks[MAXCPU];
80 static int clock_probe(device_t);
81 static void clock_identify(driver_t *, device_t);
82 static int clock_attach(device_t);
83 static unsigned counter_get_timecount(struct timecounter *tc);
85 static struct timecounter counter_timecounter = {
86 counter_get_timecount, /* get_timecount */
88 0xffffffffu, /* counter_mask */
91 800, /* quality (adjusted in code) */
95 mips_timer_early_init(uint64_t clock_hz)
97 /* Initialize clock early so that we can use DELAY sooner */
98 counter_freq = clock_hz;
99 cycles_per_usec = (clock_hz / (1000 * 1000));
103 platform_initclocks(void)
106 tc_init(&counter_timecounter);
108 if (platform_timecounter != NULL)
109 tc_init(platform_timecounter);
117 struct clk_ticks *cpu_ticks;
119 cpu_ticks = &pcpu_ticks[PCPU_GET(cpuid)];
120 ticktock = mips_rd_count();
122 * XXX: get full time update logic from r210854
125 if (ticktock < cpu_ticks->counter_lower_last)
126 cpu_ticks->counter_upper++;
127 cpu_ticks->counter_lower_last = ticktock;
130 ret = ((uint64_t) cpu_ticks->counter_upper << 32) |
131 cpu_ticks->counter_lower_last;
136 mips_timer_init_params(uint64_t platform_counter_freq, int double_count)
143 * XXX: Do not use printf here: uart code 8250 may use DELAY so this
144 * function should be called before cninit.
146 counter_freq = platform_counter_freq;
148 * XXX: Some MIPS32 cores update the Count register only every two
150 * We know this because of status registers in CP0, make it automatic.
152 if (double_count != 0)
155 cycles_per_tick = counter_freq / 1000;
156 cycles_per_hz = counter_freq / hz;
157 cycles_per_stathz = counter_freq / stathz;
158 cycles_per_profhz = counter_freq / profhz;
159 cycles_per_usec = counter_freq / (1 * 1000 * 1000);
161 counter_timecounter.tc_frequency = counter_freq;
162 printf("hz=%d cyl_per_tick:%jd cyl_per_usec:%jd freq:%jd "
163 "cyl_per_hz:%jd cyl_per_stathz:%jd cyl_per_profhz:%jd\n",
171 set_cputicker(tick_ticker, counter_freq, 1);
175 sysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS)
180 if (counter_timecounter.tc_frequency == 0)
183 error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
184 if (error == 0 && req->newptr != NULL) {
186 counter_timecounter.tc_frequency = counter_freq;
191 SYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_QUAD | CTLFLAG_RW,
192 0, sizeof(u_int), sysctl_machdep_counter_freq, "IU",
193 "Timecounter frequency in Hz");
196 counter_get_timecount(struct timecounter *tc)
199 return (mips_rd_count());
204 cpu_startprofclock(void)
210 cpu_stopprofclock(void)
216 * Wait for about n microseconds (at least!).
221 uint32_t cur, last, delta, usecs;
224 * This works by polling the timer and counting the number of
225 * microseconds that go by.
227 last = mips_rd_count();
231 cur = mips_rd_count();
233 /* Check to see if the timer has wrapped around. */
235 delta += cur + (0xffffffff - last) + 1;
241 if (delta >= cycles_per_usec) {
242 usecs += delta / cycles_per_usec;
243 delta %= cycles_per_usec;
249 * Device section of file below
252 clock_intr(void *arg)
254 struct clk_ticks *cpu_ticks;
255 struct trapframe *tf;
256 uint32_t count, compare, delta;
258 cpu_ticks = &pcpu_ticks[PCPU_GET(cpuid)];
261 * Set next clock edge.
263 count = mips_rd_count();
264 compare = cpu_ticks->compare_ticks;
265 cpu_ticks->compare_ticks = count + cycles_per_tick;
266 mips_wr_compare(cpu_ticks->compare_ticks);
268 if (count < cpu_ticks->counter_lower_last) {
269 cpu_ticks->counter_upper++;
271 cpu_ticks->counter_lower_last = count;
273 * Magic. Setting up with an arg of NULL means we get passed tf.
275 tf = (struct trapframe *)arg;
277 delta = cycles_per_tick;
280 * Account for the "lost time" between when the timer interrupt fired
281 * and when 'clock_intr' actually started executing.
283 delta += count - compare;
286 * If the COUNT and COMPARE registers are no longer in sync then make
287 * up some reasonable value for the 'delta'.
289 * This could happen, for e.g., after we resume normal operations after
290 * exiting the debugger.
292 if (delta > cycles_per_hz)
293 delta = cycles_per_hz;
296 * If the DTrace hooks are configured and a callback function
297 * has been registered, then call it to process the high speed
300 int cpu = PCPU_GET(cpuid);
301 if (cyclic_clock_func[cpu] != NULL)
302 (*cyclic_clock_func[cpu])(tf);
304 /* Fire hardclock at hz. */
305 cpu_ticks->hard_ticks += delta;
306 if (cpu_ticks->hard_ticks >= cycles_per_hz) {
307 cpu_ticks->hard_ticks -= cycles_per_hz;
308 if (PCPU_GET(cpuid) == 0)
309 hardclock(TRAPF_USERMODE(tf), tf->pc);
311 hardclock_cpu(TRAPF_USERMODE(tf));
314 /* Fire statclock at stathz. */
315 cpu_ticks->stat_ticks += delta;
316 if (cpu_ticks->stat_ticks >= cycles_per_stathz) {
317 cpu_ticks->stat_ticks -= cycles_per_stathz;
318 statclock(TRAPF_USERMODE(tf));
321 /* Fire profclock at profhz, but only when needed. */
322 cpu_ticks->prof_ticks += delta;
323 if (cpu_ticks->prof_ticks >= cycles_per_profhz) {
324 cpu_ticks->prof_ticks -= cycles_per_profhz;
326 profclock(TRAPF_USERMODE(tf), tf->pc);
329 return (FILTER_HANDLED);
333 clock_probe(device_t dev)
336 if (device_get_unit(dev) != 0)
337 panic("can't attach more clocks");
339 device_set_desc(dev, "Generic MIPS32 ticker");
344 clock_identify(driver_t * drv, device_t parent)
347 BUS_ADD_CHILD(parent, 0, "clock", 0);
351 clock_attach(device_t dev)
354 cpu_establish_hardintr("compare", clock_intr, NULL,
355 NULL, IRQ_TIMER, INTR_TYPE_CLK, NULL);
356 mips_wr_compare(mips_rd_count() + counter_freq / hz);
360 static device_method_t clock_methods[] = {
361 /* Device interface */
362 DEVMETHOD(device_probe, clock_probe),
363 DEVMETHOD(device_identify, clock_identify),
364 DEVMETHOD(device_attach, clock_attach),
365 DEVMETHOD(device_detach, bus_generic_detach),
366 DEVMETHOD(device_shutdown, bus_generic_shutdown),
371 static driver_t clock_driver = {
372 "clock", clock_methods, 32
375 static devclass_t clock_devclass;
377 DRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0);