FreeBSD/Linux Kernel Cross Reference
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: releng/8.0/sys/mips/mips/tick.c 181236 2008-08-03 14:11:06Z trhodes $");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/sysctl.h>
39 #include <sys/bus.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/rman.h>
43 #include <sys/power.h>
44 #include <sys/smp.h>
45 #include <sys/time.h>
46 #include <sys/timetc.h>
47
48 #include <machine/clock.h>
49 #include <machine/locore.h>
50 #include <machine/md_var.h>
51
52 uint64_t counter_freq;
53 uint64_t cycles_per_tick;
54 uint64_t cycles_per_usec;
55 uint64_t cycles_per_sec;
56 uint64_t cycles_per_hz;
57
58 u_int32_t counter_upper = 0;
59 u_int32_t counter_lower_last = 0;
60 int tick_started = 0;
61
62 struct clk_ticks
63 {
64 u_long hard_ticks;
65 u_long stat_ticks;
66 u_long prof_ticks;
67 /*
68 * pad for cache line alignment of pcpu info
69 * cache-line-size - number of used bytes
70 */
71 char pad[32-(3*sizeof (u_long))];
72 } static pcpu_ticks[MAXCPU];
73
74 /*
75 * Device methods
76 */
77 static int clock_probe(device_t);
78 static void clock_identify(driver_t *, device_t);
79 static int clock_attach(device_t);
80 static unsigned counter_get_timecount(struct timecounter *tc);
81
82 static struct timecounter counter_timecounter = {
83 counter_get_timecount, /* get_timecount */
84 0, /* no poll_pps */
85 0xffffffffu, /* counter_mask */
86 0, /* frequency */
87 "MIPS32", /* name */
88 800, /* quality (adjusted in code) */
89 };
90
91 void
92 mips_timer_early_init(uint64_t clock_hz)
93 {
94 /* Initialize clock early so that we can use DELAY sooner */
95 counter_freq = clock_hz;
96 cycles_per_usec = (clock_hz / (1000 * 1000));
97 }
98
99 void
100 cpu_initclocks(void)
101 {
102
103 if (!tick_started) {
104 tc_init(&counter_timecounter);
105 tick_started++;
106 }
107 }
108
109 static uint64_t
110 tick_ticker(void)
111 {
112 uint64_t ret;
113 uint32_t ticktock;
114
115 /*
116 * XXX: MIPS64 platforms can read 64-bits of counter directly.
117 * Also: the tc code is supposed to cope with things wrapping
118 * from the time counter, so I'm not sure why all these hoops
119 * are even necessary.
120 */
121 ticktock = mips_rd_count();
122 critical_enter();
123 if (ticktock < counter_lower_last)
124 counter_upper++;
125 counter_lower_last = ticktock;
126 critical_exit();
127
128 ret = ((uint64_t) counter_upper << 32) | counter_lower_last;
129 return (ret);
130 }
131
132 void
133 mips_timer_init_params(uint64_t platform_counter_freq, int double_count)
134 {
135
136 /*
137 * XXX: Do not use printf here: uart code 8250 may use DELAY so this
138 * function should be called before cninit.
139 */
140 counter_freq = platform_counter_freq;
141 cycles_per_tick = counter_freq / 1000;
142 if (double_count)
143 cycles_per_tick *= 2;
144 cycles_per_hz = counter_freq / hz;
145 cycles_per_usec = counter_freq / (1 * 1000 * 1000);
146 cycles_per_sec = counter_freq ;
147
148 counter_timecounter.tc_frequency = counter_freq;
149 /*
150 * XXX: Some MIPS32 cores update the Count register only every two
151 * pipeline cycles.
152 * XXX2: We can read this from the hardware register on some
153 * systems. Need to investigate.
154 */
155 if (double_count != 0) {
156 cycles_per_hz /= 2;
157 cycles_per_usec /= 2;
158 cycles_per_sec /= 2;
159 }
160 printf("hz=%d cyl_per_hz:%jd cyl_per_usec:%jd freq:%jd cyl_per_hz:%jd cyl_per_sec:%jd\n",
161 hz,
162 cycles_per_tick,
163 cycles_per_usec,
164 counter_freq,
165 cycles_per_hz,
166 cycles_per_sec
167 );
168 set_cputicker(tick_ticker, counter_freq, 1);
169 }
170
171 static int
172 sysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS)
173 {
174 int error;
175 uint64_t freq;
176
177 if (counter_timecounter.tc_frequency == 0)
178 return (EOPNOTSUPP);
179 freq = counter_freq;
180 error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
181 if (error == 0 && req->newptr != NULL) {
182 counter_freq = freq;
183 counter_timecounter.tc_frequency = counter_freq;
184 }
185 return (error);
186 }
187
188 SYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_QUAD | CTLFLAG_RW,
189 0, sizeof(u_int), sysctl_machdep_counter_freq, "IU",
190 "Timecounter frequency in Hz");
191
192 static unsigned
193 counter_get_timecount(struct timecounter *tc)
194 {
195
196 return (mips_rd_count());
197 }
198
199
200 void
201 cpu_startprofclock(void)
202 {
203 /* nothing to do */
204 }
205
206 void
207 cpu_stopprofclock(void)
208 {
209 /* nothing to do */
210 }
211
212 /*
213 * Wait for about n microseconds (at least!).
214 */
215 void
216 DELAY(int n)
217 {
218 uint32_t cur, last, delta, usecs;
219
220 /*
221 * This works by polling the timer and counting the number of
222 * microseconds that go by.
223 */
224 last = mips_rd_count();
225 delta = usecs = 0;
226
227 while (n > usecs) {
228 cur = mips_rd_count();
229
230 /* Check to see if the timer has wrapped around. */
231 if (cur < last)
232 delta += (cur + (cycles_per_hz - last));
233 else
234 delta += (cur - last);
235
236 last = cur;
237
238 if (delta >= cycles_per_usec) {
239 usecs += delta / cycles_per_usec;
240 delta %= cycles_per_usec;
241 }
242 }
243 }
244
245 #ifdef TARGET_OCTEON
246 int64_t wheel_run = 0;
247
248 void octeon_led_run_wheel(void);
249
250 #endif
251 /*
252 * Device section of file below
253 */
254 static int
255 clock_intr(void *arg)
256 {
257 struct clk_ticks *cpu_ticks;
258 struct trapframe *tf;
259 uint32_t ltick;
260 /*
261 * Set next clock edge.
262 */
263 ltick = mips_rd_count();
264 mips_wr_compare(ltick + cycles_per_tick);
265 cpu_ticks = &pcpu_ticks[PCPU_GET(cpuid)];
266 critical_enter();
267 if (ltick < counter_lower_last) {
268 counter_upper++;
269 counter_lower_last = ltick;
270 }
271 /*
272 * Magic. Setting up with an arg of NULL means we get passed tf.
273 */
274 tf = (struct trapframe *)arg;
275
276 /* Fire hardclock at hz. */
277 cpu_ticks->hard_ticks += cycles_per_tick;
278 if (cpu_ticks->hard_ticks >= cycles_per_hz) {
279 cpu_ticks->hard_ticks -= cycles_per_hz;
280 if (PCPU_GET(cpuid) == 0)
281 hardclock(USERMODE(tf->sr), tf->pc);
282 else
283 hardclock_cpu(USERMODE(tf->sr));
284 }
285 /* Fire statclock at stathz. */
286 cpu_ticks->stat_ticks += stathz;
287 if (cpu_ticks->stat_ticks >= cycles_per_hz) {
288 cpu_ticks->stat_ticks -= cycles_per_hz;
289 statclock(USERMODE(tf->sr));
290 }
291
292 /* Fire profclock at profhz, but only when needed. */
293 cpu_ticks->prof_ticks += profhz;
294 if (cpu_ticks->prof_ticks >= cycles_per_hz) {
295 cpu_ticks->prof_ticks -= cycles_per_hz;
296 if (profprocs != 0)
297 profclock(USERMODE(tf->sr), tf->pc);
298 }
299 critical_exit();
300 #ifdef TARGET_OCTEON
301 /* Run the FreeBSD display once every hz ticks */
302 wheel_run += cycles_per_tick;
303 if (wheel_run >= cycles_per_sec) {
304 wheel_run = 0;
305 octeon_led_run_wheel();
306 }
307 #endif
308 return (FILTER_HANDLED);
309 }
310
311 static int
312 clock_probe(device_t dev)
313 {
314
315 if (device_get_unit(dev) != 0)
316 panic("can't attach more clocks");
317
318 device_set_desc(dev, "Generic MIPS32 ticker");
319 return (0);
320 }
321
322 static void
323 clock_identify(driver_t * drv, device_t parent)
324 {
325
326 BUS_ADD_CHILD(parent, 0, "clock", 0);
327 }
328
329 static int
330 clock_attach(device_t dev)
331 {
332 struct resource *irq;
333 int error;
334 int rid;
335
336 rid = 0;
337 irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 5, 5, 1, RF_ACTIVE);
338 if (irq == NULL) {
339 device_printf(dev, "failed to allocate irq\n");
340 return (ENXIO);
341 }
342 error = bus_setup_intr(dev, irq, INTR_TYPE_CLK, clock_intr, NULL,
343 NULL, NULL);
344
345 if (error != 0) {
346 device_printf(dev, "bus_setup_intr returned %d\n", error);
347 return (error);
348 }
349 mips_wr_compare(mips_rd_count() + counter_freq / hz);
350 return (0);
351 }
352
353 static device_method_t clock_methods[] = {
354 /* Device interface */
355 DEVMETHOD(device_probe, clock_probe),
356 DEVMETHOD(device_identify, clock_identify),
357 DEVMETHOD(device_attach, clock_attach),
358 DEVMETHOD(device_detach, bus_generic_detach),
359 DEVMETHOD(device_shutdown, bus_generic_shutdown),
360
361 {0, 0}
362 };
363
364 static driver_t clock_driver = {
365 "clock", clock_methods, 32
366 };
367
368 static devclass_t clock_devclass;
369
370 DRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0);
Cache object: be63d2478651372988d1cf47bcacebbc
|