1 /* $OpenBSD: clock.c,v 1.54 2023/10/24 13:20:10 claudio Exp $ */
2
3 /*
4 * Copyright (c) 2001-2004 Opsycon AB (www.opsycon.se / www.opsycon.com)
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 ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * 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 /*
30 * Clock code for systems using the on-cpu counter register, when both the
31 * counter and comparator registers are available (i.e. everything MIPS-III
32 * or MIPS-IV capable but the R8000).
33 *
34 * On most processors, this register counts at half the pipeline frequency.
35 */
36
37 #include <sys/param.h>
38 #include <sys/kernel.h>
39 #include <sys/systm.h>
40 #include <sys/clockintr.h>
41 #include <sys/device.h>
42 #include <sys/evcount.h>
43 #include <sys/stdint.h>
44
45 #include <machine/autoconf.h>
46 #include <machine/cpu.h>
47 #include <mips64/mips_cpu.h>
48
49 static struct evcount cp0_clock_count;
50 static int cp0_clock_irq = 5;
51 uint64_t cp0_nsec_cycle_ratio;
52 uint64_t cp0_nsec_max;
53
54 int clockmatch(struct device *, void *, void *);
55 void clockattach(struct device *, struct device *, void *);
56
57 struct cfdriver clock_cd = {
58 NULL, "clock", DV_DULL
59 };
60
61 const struct cfattach clock_ca = {
62 sizeof(struct device), clockmatch, clockattach
63 };
64
65 void cp0_rearm_int5(void *, uint64_t);
66 void cp0_trigger_int5_wrapper(void *);
67
68 const struct intrclock cp0_intrclock = {
69 .ic_rearm = cp0_rearm_int5,
70 .ic_trigger = cp0_trigger_int5_wrapper
71 };
72
73 void cp0_initclock(void);
74 uint32_t cp0_int5(uint32_t, struct trapframe *);
75 void cp0_startclock(struct cpu_info *);
76 void cp0_trigger_int5(void);
77 void cp0_trigger_int5_masked(void);
78
79 int
clockmatch(struct device * parent,void * vcf,void * aux)80 clockmatch(struct device *parent, void *vcf, void *aux)
81 {
82 struct mainbus_attach_args *maa = aux;
83
84 return strcmp(maa->maa_name, clock_cd.cd_name) == 0;
85 }
86
87 void
clockattach(struct device * parent,struct device * self,void * aux)88 clockattach(struct device *parent, struct device *self, void *aux)
89 {
90 uint64_t cp0_freq = curcpu()->ci_hw.clock / CP0_CYCLE_DIVIDER;
91
92 printf(": int 5\n");
93
94 cp0_nsec_cycle_ratio = cp0_freq * (1ULL << 32) / 1000000000;
95 cp0_nsec_max = UINT64_MAX / cp0_nsec_cycle_ratio;
96
97 /*
98 * We need to register the interrupt now, for idle_mask to
99 * be computed correctly.
100 */
101 set_intr(INTPRI_CLOCK, CR_INT_5, cp0_int5);
102 evcount_attach(&cp0_clock_count, "clock", &cp0_clock_irq);
103 evcount_percpu(&cp0_clock_count);
104
105 /* try to avoid getting clock interrupts early */
106 cp0_set_compare(cp0_get_count() - 1);
107
108 md_initclock = cp0_initclock;
109 md_startclock = cp0_startclock;
110 md_triggerclock = cp0_trigger_int5;
111 }
112
113 /*
114 * Interrupt handler for targets using the internal count register
115 * as interval clock. Normally the system is run with the clock
116 * interrupt always enabled. Masking is done here and if the clock
117 * cannot be run the tick is handled later when the clock is logically
118 * unmasked again.
119 */
120 uint32_t
cp0_int5(uint32_t mask,struct trapframe * tf)121 cp0_int5(uint32_t mask, struct trapframe *tf)
122 {
123 struct cpu_info *ci = curcpu();
124 int s;
125
126 evcount_inc(&cp0_clock_count);
127
128 cp0_set_compare(cp0_get_count() - 1); /* clear INT5 */
129
130 /*
131 * Just ignore the interrupt if we're not ready to process it.
132 * cpu_initclocks() will retrigger it later.
133 */
134 if (!ci->ci_clock_started)
135 return CR_INT_5;
136
137 /*
138 * If the clock interrupt is logically masked, defer all
139 * work until it is logically unmasked from splx(9).
140 */
141 if (tf->ipl >= IPL_CLOCK) {
142 ci->ci_clock_deferred = 1;
143 return CR_INT_5;
144 }
145 ci->ci_clock_deferred = 0;
146
147 /*
148 * Process clock interrupt.
149 */
150 s = splclock();
151 #ifdef MULTIPROCESSOR
152 register_t sr;
153
154 sr = getsr();
155 ENABLEIPI();
156 #endif
157 clockintr_dispatch(tf);
158 #ifdef MULTIPROCESSOR
159 setsr(sr);
160 #endif
161 ci->ci_ipl = s;
162 return CR_INT_5; /* Clock is always on 5 */
163 }
164
165 /*
166 * Arm INT5 to fire after the given number of nanoseconds have elapsed.
167 * Only try once. If we miss, let cp0_trigger_int5_masked() handle it.
168 */
169 void
cp0_rearm_int5(void * unused,uint64_t nsecs)170 cp0_rearm_int5(void *unused, uint64_t nsecs)
171 {
172 uint32_t cycles, t0;
173 register_t sr;
174
175 if (nsecs > cp0_nsec_max)
176 nsecs = cp0_nsec_max;
177 cycles = (nsecs * cp0_nsec_cycle_ratio) >> 32;
178
179 /*
180 * Set compare, then immediately reread count. If at least
181 * "cycles" CP0 ticks have elapsed and INT5 isn't already
182 * pending, we missed.
183 */
184 sr = disableintr();
185 t0 = cp0_get_count();
186 cp0_set_compare(t0 + cycles);
187 if (cycles <= cp0_get_count() - t0) {
188 if (!ISSET(cp0_get_cause(), CR_INT_5))
189 cp0_trigger_int5_masked();
190 }
191 setsr(sr);
192 }
193
194 void
cp0_trigger_int5(void)195 cp0_trigger_int5(void)
196 {
197 register_t sr;
198
199 sr = disableintr();
200 cp0_trigger_int5_masked();
201 setsr(sr);
202 }
203
204 /*
205 * Arm INT5 to fire as soon as possible.
206 *
207 * We need to spin until either (a) INT5 is pending or (b) the compare
208 * register leads the count register, i.e. we know INT5 will be pending
209 * very soon.
210 *
211 * To ensure we don't spin forever, double the compensatory offset
212 * added to the compare value every time we miss the count register.
213 * The initial offset of 16 cycles was chosen experimentally. It
214 * is the smallest power of two that doesn't require multiple loops
215 * to arm the timer on most Octeon hardware.
216 */
217 void
cp0_trigger_int5_masked(void)218 cp0_trigger_int5_masked(void)
219 {
220 uint32_t offset = 16, t0;
221
222 while (!ISSET(cp0_get_cause(), CR_INT_5)) {
223 t0 = cp0_get_count();
224 cp0_set_compare(t0 + offset);
225 if (cp0_get_count() - t0 < offset)
226 return;
227 offset *= 2;
228 }
229 }
230
231 void
cp0_trigger_int5_wrapper(void * unused)232 cp0_trigger_int5_wrapper(void *unused)
233 {
234 cp0_trigger_int5();
235 }
236
237 void
cp0_initclock(void)238 cp0_initclock(void)
239 {
240 KASSERT(CPU_IS_PRIMARY(curcpu()));
241
242 stathz = hz;
243 profhz = stathz * 10;
244 statclock_is_randomized = 1;
245 }
246
247 /*
248 * Start the clock interrupt dispatch cycle.
249 */
250 void
cp0_startclock(struct cpu_info * ci)251 cp0_startclock(struct cpu_info *ci)
252 {
253 int s;
254
255 if (!CPU_IS_PRIMARY(ci)) {
256 /* try to avoid getting clock interrupts early */
257 cp0_set_compare(cp0_get_count() - 1);
258
259 cp0_calibrate(ci);
260 }
261
262 clockintr_cpu_init(&cp0_intrclock);
263
264 /* Start the clock. */
265 s = splclock();
266 ci->ci_clock_started = 1;
267 clockintr_trigger();
268 splx(s);
269 }
270