]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/mips/rmi/tick.c
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sys / mips / rmi / tick.c
1 /*-
2  * Copyright (c) 2006-2007 Bruce M. Simpson.
3  * Copyright (c) 2003-2004 Juli Mallett.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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
25  * SUCH DAMAGE.
26  */
27
28 /*
29  * Simple driver for the 32-bit interval counter built in to all
30  * MIPS32 CPUs.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include "opt_cputype.h"
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/timetc.h>
49
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>
56
57
58 uint64_t counter_freq;
59
60 struct timecounter *platform_timecounter;
61
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;
65
66 struct clk_ticks {
67         u_long hard_ticks;
68         u_long stat_ticks;
69         u_long prof_ticks;
70         uint32_t compare_ticks;
71         u_int32_t counter_upper;
72         u_int32_t counter_lower_last;
73 } __aligned(CACHE_LINE_SIZE);
74
75 static struct clk_ticks pcpu_ticks[MAXCPU];
76
77 /*
78  * Device methods
79  */
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);
84
85 static struct timecounter counter_timecounter = {
86         counter_get_timecount,  /* get_timecount */
87         0,                      /* no poll_pps */
88         0xffffffffu,            /* counter_mask */
89         0,                      /* frequency */
90         "MIPS32",               /* name */
91         800,                    /* quality (adjusted in code) */
92 };
93
94 void 
95 mips_timer_early_init(uint64_t clock_hz)
96 {
97         /* Initialize clock early so that we can use DELAY sooner */
98         counter_freq = clock_hz;
99         cycles_per_usec = (clock_hz / (1000 * 1000));
100 }
101
102 void
103 platform_initclocks(void)
104 {
105
106         tc_init(&counter_timecounter);
107
108         if (platform_timecounter != NULL)
109                 tc_init(platform_timecounter);
110 }
111
112 static uint64_t
113 tick_ticker(void)
114 {
115         uint64_t ret;
116         uint32_t ticktock;
117         struct clk_ticks *cpu_ticks;
118
119         cpu_ticks = &pcpu_ticks[PCPU_GET(cpuid)];
120         ticktock = mips_rd_count();
121         /*
122          * XXX: get full time update logic from r210854
123          */
124         critical_enter();
125         if (ticktock < cpu_ticks->counter_lower_last)
126                 cpu_ticks->counter_upper++;
127         cpu_ticks->counter_lower_last = ticktock;
128         critical_exit();
129
130         ret = ((uint64_t) cpu_ticks->counter_upper << 32) |
131             cpu_ticks->counter_lower_last;
132         return (ret);
133 }
134
135 void
136 mips_timer_init_params(uint64_t platform_counter_freq, int double_count)
137 {
138
139         stathz = hz;
140         profhz = hz;
141
142         /*
143          * XXX: Do not use printf here: uart code 8250 may use DELAY so this
144          * function should  be called before cninit.
145          */
146         counter_freq = platform_counter_freq;
147         /*
148          * XXX: Some MIPS32 cores update the Count register only every two
149          * pipeline cycles.
150          * We know this because of status registers in CP0, make it automatic.
151          */
152         if (double_count != 0)
153                 counter_freq /= 2;
154
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);
160         
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",
164                hz,
165                cycles_per_tick,
166                cycles_per_usec,
167                counter_freq,
168                cycles_per_hz,
169                cycles_per_stathz,
170                cycles_per_profhz);
171         set_cputicker(tick_ticker, counter_freq, 1);
172 }
173
174 static int
175 sysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS)
176 {
177         int error;
178         uint64_t freq;
179
180         if (counter_timecounter.tc_frequency == 0)
181                 return (EOPNOTSUPP);
182         freq = counter_freq;
183         error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
184         if (error == 0 && req->newptr != NULL) {
185                 counter_freq = freq;
186                 counter_timecounter.tc_frequency = counter_freq;
187         }
188         return (error);
189 }
190
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");
194
195 static unsigned
196 counter_get_timecount(struct timecounter *tc)
197 {
198
199         return (mips_rd_count());
200 }
201
202
203 void
204 cpu_startprofclock(void)
205 {
206         /* nothing to do */
207 }
208
209 void
210 cpu_stopprofclock(void)
211 {
212         /* nothing to do */
213 }
214
215 /*
216  * Wait for about n microseconds (at least!).
217  */
218 void
219 DELAY(int n)
220 {
221         uint32_t cur, last, delta, usecs;
222
223         /*
224          * This works by polling the timer and counting the number of
225          * microseconds that go by.
226          */
227         last = mips_rd_count();
228         delta = usecs = 0;
229
230         while (n > usecs) {
231                 cur = mips_rd_count();
232
233                 /* Check to see if the timer has wrapped around. */
234                 if (cur < last)
235                         delta += cur + (0xffffffff - last) + 1;
236                 else
237                         delta += cur - last;
238
239                 last = cur;
240
241                 if (delta >= cycles_per_usec) {
242                         usecs += delta / cycles_per_usec;
243                         delta %= cycles_per_usec;
244                 }
245         }
246 }
247
248 /*
249  * Device section of file below
250  */
251 static int
252 clock_intr(void *arg)
253 {
254         struct clk_ticks *cpu_ticks;
255         struct trapframe *tf;
256         uint32_t count, compare, delta;
257
258         cpu_ticks = &pcpu_ticks[PCPU_GET(cpuid)];
259
260         /*
261          * Set next clock edge.
262          */
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);
267         critical_enter();
268         if (count < cpu_ticks->counter_lower_last) {
269                 cpu_ticks->counter_upper++;
270         }
271         cpu_ticks->counter_lower_last = count;
272         /*
273          * Magic.  Setting up with an arg of NULL means we get passed tf.
274          */
275         tf = (struct trapframe *)arg;
276
277         delta = cycles_per_tick;
278
279         /*
280          * Account for the "lost time" between when the timer interrupt fired
281          * and when 'clock_intr' actually started executing.
282          */
283         delta += count - compare;
284
285         /*
286          * If the COUNT and COMPARE registers are no longer in sync then make
287          * up some reasonable value for the 'delta'.
288          *
289          * This could happen, for e.g., after we resume normal operations after
290          * exiting the debugger.
291          */
292         if (delta > cycles_per_hz)
293                 delta = cycles_per_hz;
294 #ifdef KDTRACE_HOOKS
295         /*
296          * If the DTrace hooks are configured and a callback function
297          * has been registered, then call it to process the high speed
298          * timers.
299          */
300         int cpu = PCPU_GET(cpuid);
301         if (cyclic_clock_func[cpu] != NULL)
302                 (*cyclic_clock_func[cpu])(tf);
303 #endif
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);
310                 else
311                         hardclock_cpu(TRAPF_USERMODE(tf));
312         }
313
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));
319         }
320
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;
325                 if (profprocs != 0)
326                         profclock(TRAPF_USERMODE(tf), tf->pc);
327         }
328         critical_exit();
329         return (FILTER_HANDLED);
330 }
331
332 static int
333 clock_probe(device_t dev)
334 {
335
336         if (device_get_unit(dev) != 0)
337                 panic("can't attach more clocks");
338
339         device_set_desc(dev, "Generic MIPS32 ticker");
340         return (0);
341 }
342
343 static void
344 clock_identify(driver_t * drv, device_t parent)
345 {
346
347         BUS_ADD_CHILD(parent, 0, "clock", 0);
348 }
349
350 static int
351 clock_attach(device_t dev)
352 {
353
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);
357         return (0);
358 }
359
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),
367
368         {0, 0}
369 };
370
371 static driver_t clock_driver = {
372         "clock", clock_methods, 32
373 };
374
375 static devclass_t clock_devclass;
376
377 DRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0);