]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/mips/mips/tick.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 <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);