]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/mips/mips/tick.c
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sys / mips / mips / 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
55 uint64_t counter_freq;
56
57 struct timecounter *platform_timecounter;
58
59 static uint64_t cycles_per_tick;
60 static uint64_t cycles_per_usec;
61 static uint64_t cycles_per_hz, cycles_per_stathz, cycles_per_profhz;
62
63 static u_int32_t counter_upper = 0;
64 static u_int32_t counter_lower_last = 0;
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 } __aligned(CACHE_LINE_SIZE);
72
73 static struct clk_ticks pcpu_ticks[MAXCPU];
74
75 /*
76  * Device methods
77  */
78 static int clock_probe(device_t);
79 static void clock_identify(driver_t *, device_t);
80 static int clock_attach(device_t);
81 static unsigned counter_get_timecount(struct timecounter *tc);
82
83 static struct timecounter counter_timecounter = {
84         counter_get_timecount,  /* get_timecount */
85         0,                      /* no poll_pps */
86         0xffffffffu,            /* counter_mask */
87         0,                      /* frequency */
88         "MIPS32",               /* name */
89         800,                    /* quality (adjusted in code) */
90 };
91
92 void 
93 mips_timer_early_init(uint64_t clock_hz)
94 {
95         /* Initialize clock early so that we can use DELAY sooner */
96         counter_freq = clock_hz;
97         cycles_per_usec = (clock_hz / (1000 * 1000));
98 }
99
100 void
101 platform_initclocks(void)
102 {
103
104         tc_init(&counter_timecounter);
105
106         if (platform_timecounter != NULL)
107                 tc_init(platform_timecounter);
108 }
109
110 static uint64_t
111 tick_ticker(void)
112 {
113         uint64_t ret;
114         uint32_t ticktock;
115
116         /*
117          * XXX: MIPS64 platforms can read 64-bits of counter directly.
118          * Also: the tc code is supposed to cope with things wrapping
119          * from the time counter, so I'm not sure why all these hoops
120          * are even necessary.
121          */
122         ticktock = mips_rd_count();
123         critical_enter();
124         if (ticktock < counter_lower_last)
125                 counter_upper++;
126         counter_lower_last = ticktock;
127         critical_exit();
128
129         ret = ((uint64_t) counter_upper << 32) | counter_lower_last;
130         return (ret);
131 }
132
133 void
134 mips_timer_init_params(uint64_t platform_counter_freq, int double_count)
135 {
136
137         stathz = hz;
138         profhz = hz;
139
140         /*
141          * XXX: Do not use printf here: uart code 8250 may use DELAY so this
142          * function should  be called before cninit.
143          */
144         counter_freq = platform_counter_freq;
145         /*
146          * XXX: Some MIPS32 cores update the Count register only every two
147          * pipeline cycles.
148          * We know this because of status registers in CP0, make it automatic.
149          */
150         if (double_count != 0)
151                 counter_freq /= 2;
152
153         cycles_per_tick = counter_freq / 1000;
154         cycles_per_hz = counter_freq / hz;
155         cycles_per_stathz = counter_freq / stathz;
156         cycles_per_profhz = counter_freq / profhz;
157         cycles_per_usec = counter_freq / (1 * 1000 * 1000);
158         
159         counter_timecounter.tc_frequency = counter_freq;
160         printf("hz=%d cyl_per_tick:%jd cyl_per_usec:%jd freq:%jd "
161                "cyl_per_hz:%jd cyl_per_stathz:%jd cyl_per_profhz:%jd\n",
162                hz,
163                cycles_per_tick,
164                cycles_per_usec,
165                counter_freq,
166                cycles_per_hz,
167                cycles_per_stathz,
168                cycles_per_profhz);
169         set_cputicker(tick_ticker, counter_freq, 1);
170 }
171
172 static int
173 sysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS)
174 {
175         int error;
176         uint64_t freq;
177
178         if (counter_timecounter.tc_frequency == 0)
179                 return (EOPNOTSUPP);
180         freq = counter_freq;
181         error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
182         if (error == 0 && req->newptr != NULL) {
183                 counter_freq = freq;
184                 counter_timecounter.tc_frequency = counter_freq;
185         }
186         return (error);
187 }
188
189 SYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_QUAD | CTLFLAG_RW,
190     0, sizeof(u_int), sysctl_machdep_counter_freq, "IU",
191     "Timecounter frequency in Hz");
192
193 static unsigned
194 counter_get_timecount(struct timecounter *tc)
195 {
196
197         return (mips_rd_count());
198 }
199
200
201 void
202 cpu_startprofclock(void)
203 {
204         /* nothing to do */
205 }
206
207 void
208 cpu_stopprofclock(void)
209 {
210         /* nothing to do */
211 }
212
213 /*
214  * Wait for about n microseconds (at least!).
215  */
216 void
217 DELAY(int n)
218 {
219         uint32_t cur, last, delta, usecs;
220
221         /*
222          * This works by polling the timer and counting the number of
223          * microseconds that go by.
224          */
225         last = mips_rd_count();
226         delta = usecs = 0;
227
228         while (n > usecs) {
229                 cur = mips_rd_count();
230
231                 /* Check to see if the timer has wrapped around. */
232                 if (cur < last)
233                         delta += cur + (0xffffffff - last) + 1;
234                 else
235                         delta += cur - last;
236
237                 last = cur;
238
239                 if (delta >= cycles_per_usec) {
240                         usecs += delta / cycles_per_usec;
241                         delta %= cycles_per_usec;
242                 }
243         }
244 }
245
246 #if 0 /* TARGET_OCTEON */
247 int64_t wheel_run = 0;
248
249 void octeon_led_run_wheel();
250
251 #endif
252 /*
253  * Device section of file below
254  */
255 static int
256 clock_intr(void *arg)
257 {
258         struct clk_ticks *cpu_ticks;
259         struct trapframe *tf;
260         uint32_t count, compare, delta;
261
262         cpu_ticks = &pcpu_ticks[PCPU_GET(cpuid)];
263
264         /*
265          * Set next clock edge.
266          */
267         count = mips_rd_count();
268         compare = cpu_ticks->compare_ticks;
269         cpu_ticks->compare_ticks = count + cycles_per_tick;
270         mips_wr_compare(cpu_ticks->compare_ticks);
271         critical_enter();
272         if (count < counter_lower_last) {
273                 counter_upper++;
274                 counter_lower_last = count;
275         }
276         /*
277          * Magic.  Setting up with an arg of NULL means we get passed tf.
278          */
279         tf = (struct trapframe *)arg;
280
281         delta = cycles_per_tick;
282
283         /*
284          * Account for the "lost time" between when the timer interrupt fired
285          * and when 'clock_intr' actually started executing.
286          */
287         delta += count - compare;
288
289         /*
290          * If the COUNT and COMPARE registers are no longer in sync then make
291          * up some reasonable value for the 'delta'.
292          *
293          * This could happen, for e.g., after we resume normal operations after
294          * exiting the debugger.
295          */
296         if (delta > cycles_per_hz)
297                 delta = cycles_per_hz;
298 #ifdef KDTRACE_HOOKS
299         /*
300          * If the DTrace hooks are configured and a callback function
301          * has been registered, then call it to process the high speed
302          * timers.
303          */
304         int cpu = PCPU_GET(cpuid);
305         if (cyclic_clock_func[cpu] != NULL)
306                 (*cyclic_clock_func[cpu])(tf);
307 #endif
308         /* Fire hardclock at hz. */
309         cpu_ticks->hard_ticks += delta;
310         if (cpu_ticks->hard_ticks >= cycles_per_hz) {
311                 cpu_ticks->hard_ticks -= cycles_per_hz;
312                 if (PCPU_GET(cpuid) == 0)
313                         hardclock(TRAPF_USERMODE(tf), tf->pc);
314                 else
315                         hardclock_cpu(TRAPF_USERMODE(tf));
316         }
317
318         /* Fire statclock at stathz. */
319         cpu_ticks->stat_ticks += delta;
320         if (cpu_ticks->stat_ticks >= cycles_per_stathz) {
321                 cpu_ticks->stat_ticks -= cycles_per_stathz;
322                 statclock(TRAPF_USERMODE(tf));
323         }
324
325         /* Fire profclock at profhz, but only when needed. */
326         cpu_ticks->prof_ticks += delta;
327         if (cpu_ticks->prof_ticks >= cycles_per_profhz) {
328                 cpu_ticks->prof_ticks -= cycles_per_profhz;
329                 if (profprocs != 0)
330                         profclock(TRAPF_USERMODE(tf), tf->pc);
331         }
332         critical_exit();
333 #if 0 /* TARGET_OCTEON */
334         /* Run the FreeBSD display once every hz ticks  */
335         wheel_run += cycles_per_tick;
336         if (wheel_run >= cycles_per_usec * 1000000ULL) {
337                 wheel_run = 0;
338                 octeon_led_run_wheel();
339         }
340 #endif
341         return (FILTER_HANDLED);
342 }
343
344 static int
345 clock_probe(device_t dev)
346 {
347
348         if (device_get_unit(dev) != 0)
349                 panic("can't attach more clocks");
350
351         device_set_desc(dev, "Generic MIPS32 ticker");
352         return (0);
353 }
354
355 static void
356 clock_identify(driver_t * drv, device_t parent)
357 {
358
359         BUS_ADD_CHILD(parent, 0, "clock", 0);
360 }
361
362 static int
363 clock_attach(device_t dev)
364 {
365         struct resource *irq;
366         int error;
367         int rid;
368
369         rid = 0;
370         irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 5, 5, 1, RF_ACTIVE);
371         if (irq == NULL) {
372                 device_printf(dev, "failed to allocate irq\n");
373                 return (ENXIO);
374         }
375         error = bus_setup_intr(dev, irq, INTR_TYPE_CLK, clock_intr, NULL,
376             NULL, NULL);
377
378         if (error != 0) {
379                 device_printf(dev, "bus_setup_intr returned %d\n", error);
380                 return (error);
381         }
382
383         mips_wr_compare(mips_rd_count() + counter_freq / hz);
384         return (0);
385 }
386
387 static device_method_t clock_methods[] = {
388         /* Device interface */
389         DEVMETHOD(device_probe, clock_probe),
390         DEVMETHOD(device_identify, clock_identify),
391         DEVMETHOD(device_attach, clock_attach),
392         DEVMETHOD(device_detach, bus_generic_detach),
393         DEVMETHOD(device_shutdown, bus_generic_shutdown),
394
395         {0, 0}
396 };
397
398 static driver_t clock_driver = {
399         "clock", clock_methods, 32
400 };
401
402 static devclass_t clock_devclass;
403
404 DRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0);