]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
e5568f75 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
1c79356b | 11 | * |
e5568f75 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
e5568f75 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
1c79356b A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * @OSF_COPYRIGHT@ | |
24 | */ | |
25 | ||
26 | /* | |
27 | * File: i386/rtclock.c | |
28 | * Purpose: Routines for handling the machine dependent | |
29 | * real-time clock. This clock is generated by | |
30 | * the Intel 8254 Programmable Interval Timer. | |
31 | */ | |
32 | ||
33 | #include <cpus.h> | |
34 | #include <platforms.h> | |
1c79356b | 35 | #include <mach_kdb.h> |
55e303ae A |
36 | |
37 | #include <mach/mach_types.h> | |
38 | ||
1c79356b A |
39 | #include <kern/cpu_number.h> |
40 | #include <kern/cpu_data.h> | |
41 | #include <kern/clock.h> | |
55e303ae | 42 | #include <kern/host_notify.h> |
1c79356b A |
43 | #include <kern/macro_help.h> |
44 | #include <kern/misc_protos.h> | |
45 | #include <kern/spl.h> | |
46 | #include <machine/mach_param.h> /* HZ */ | |
47 | #include <mach/vm_prot.h> | |
48 | #include <vm/pmap.h> | |
49 | #include <vm/vm_kern.h> /* for kernel_map */ | |
50 | #include <i386/ipl.h> | |
51 | #include <i386/pit.h> | |
52 | #include <i386/pio.h> | |
53 | #include <i386/misc_protos.h> | |
54 | #include <i386/rtclock_entries.h> | |
55 | #include <i386/hardclock_entries.h> | |
55e303ae A |
56 | #include <i386/proc_reg.h> |
57 | #include <i386/machine_cpu.h> | |
58 | #include <pexpert/pexpert.h> | |
59 | ||
60 | #define DISPLAYENTER(x) printf("[RTCLOCK] entering " #x "\n"); | |
61 | #define DISPLAYEXIT(x) printf("[RTCLOCK] leaving " #x "\n"); | |
62 | #define DISPLAYVALUE(x,y) printf("[RTCLOCK] " #x ":" #y " = 0x%08x \n",y); | |
1c79356b A |
63 | |
64 | int sysclk_config(void); | |
65 | ||
66 | int sysclk_init(void); | |
67 | ||
68 | kern_return_t sysclk_gettime( | |
69 | mach_timespec_t *cur_time); | |
70 | ||
71 | kern_return_t sysclk_getattr( | |
72 | clock_flavor_t flavor, | |
73 | clock_attr_t attr, | |
74 | mach_msg_type_number_t *count); | |
75 | ||
76 | kern_return_t sysclk_setattr( | |
77 | clock_flavor_t flavor, | |
78 | clock_attr_t attr, | |
79 | mach_msg_type_number_t count); | |
80 | ||
81 | void sysclk_setalarm( | |
82 | mach_timespec_t *alarm_time); | |
83 | ||
84 | extern void (*IOKitRegisterInterruptHook)(void *, int irq, int isclock); | |
85 | ||
86 | /* | |
87 | * Lists of clock routines. | |
88 | */ | |
89 | struct clock_ops sysclk_ops = { | |
90 | sysclk_config, sysclk_init, | |
91 | sysclk_gettime, 0, | |
92 | sysclk_getattr, sysclk_setattr, | |
93 | sysclk_setalarm, | |
94 | }; | |
95 | ||
96 | int calend_config(void); | |
97 | ||
98 | int calend_init(void); | |
99 | ||
100 | kern_return_t calend_gettime( | |
101 | mach_timespec_t *cur_time); | |
102 | ||
1c79356b A |
103 | kern_return_t calend_getattr( |
104 | clock_flavor_t flavor, | |
105 | clock_attr_t attr, | |
106 | mach_msg_type_number_t *count); | |
107 | ||
108 | struct clock_ops calend_ops = { | |
109 | calend_config, calend_init, | |
55e303ae | 110 | calend_gettime, 0, |
1c79356b A |
111 | calend_getattr, 0, |
112 | 0, | |
113 | }; | |
114 | ||
115 | /* local data declarations */ | |
116 | mach_timespec_t *RtcTime = (mach_timespec_t *)0; | |
117 | mach_timespec_t *RtcAlrm; | |
118 | clock_res_t RtcDelt; | |
119 | ||
120 | /* global data declarations */ | |
121 | struct { | |
0b4e3aa0 | 122 | uint64_t abstime; |
1c79356b A |
123 | |
124 | mach_timespec_t time; | |
125 | mach_timespec_t alarm_time; /* time of next alarm */ | |
126 | ||
127 | mach_timespec_t calend_offset; | |
128 | boolean_t calend_is_set; | |
129 | ||
55e303ae A |
130 | int64_t calend_adjtotal; |
131 | int32_t calend_adjdelta; | |
132 | ||
0b4e3aa0 | 133 | uint64_t timer_deadline; |
1c79356b A |
134 | boolean_t timer_is_set; |
135 | clock_timer_func_t timer_expire; | |
136 | ||
137 | clock_res_t new_ires; /* pending new resolution (nano ) */ | |
138 | clock_res_t intr_nsec; /* interrupt resolution (nano) */ | |
55e303ae | 139 | mach_timebase_info_data_t timebase_const; |
1c79356b A |
140 | |
141 | decl_simple_lock_data(,lock) /* real-time clock device lock */ | |
142 | } rtclock; | |
143 | ||
144 | unsigned int clknum; /* clks per second */ | |
145 | unsigned int new_clknum; /* pending clknum */ | |
146 | unsigned int time_per_clk; /* time per clk in ZHZ */ | |
147 | unsigned int clks_per_int; /* clks per interrupt */ | |
148 | unsigned int clks_per_int_99; | |
55e303ae A |
149 | int rtc_intr_count; /* interrupt counter */ |
150 | int rtc_intr_hertz; /* interrupts per HZ */ | |
151 | int rtc_intr_freq; /* interrupt frequency */ | |
152 | int rtc_print_lost_tick; /* print lost tick */ | |
1c79356b | 153 | |
9bccf70c | 154 | uint32_t rtc_cyc_per_sec; /* processor cycles per seconds */ |
55e303ae | 155 | uint32_t rtc_quant_scale; /* used internally to convert clocks to nanos */ |
9bccf70c | 156 | |
1c79356b A |
157 | /* |
158 | * Macros to lock/unlock real-time clock device. | |
159 | */ | |
160 | #define LOCK_RTC(s) \ | |
161 | MACRO_BEGIN \ | |
162 | (s) = splclock(); \ | |
163 | simple_lock(&rtclock.lock); \ | |
164 | MACRO_END | |
165 | ||
166 | #define UNLOCK_RTC(s) \ | |
167 | MACRO_BEGIN \ | |
168 | simple_unlock(&rtclock.lock); \ | |
169 | splx(s); \ | |
170 | MACRO_END | |
171 | ||
172 | /* | |
173 | * i8254 control. ** MONUMENT ** | |
174 | * | |
175 | * The i8254 is a traditional PC device with some arbitrary characteristics. | |
176 | * Basically, it is a register that counts at a fixed rate and can be | |
177 | * programmed to generate an interrupt every N counts. The count rate is | |
178 | * clknum counts per second (see pit.h), historically 1193167 we believe. | |
179 | * Various constants are computed based on this value, and we calculate | |
180 | * them at init time for execution efficiency. To obtain sufficient | |
181 | * accuracy, some of the calculation are most easily done in floating | |
182 | * point and then converted to int. | |
183 | * | |
184 | * We want an interrupt every 10 milliseconds, approximately. The count | |
185 | * which will do that is clks_per_int. However, that many counts is not | |
186 | * *exactly* 10 milliseconds; it is a bit more or less depending on | |
187 | * roundoff. The actual time per tick is calculated and saved in | |
188 | * rtclock.intr_nsec, and it is that value which is added to the time | |
189 | * register on each tick. | |
190 | * | |
191 | * The i8254 counter can be read between interrupts in order to determine | |
192 | * the time more accurately. The counter counts down from the preset value | |
193 | * toward 0, and we have to handle the case where the counter has been | |
194 | * reset just before being read and before the interrupt has been serviced. | |
195 | * Given a count since the last interrupt, the time since then is given | |
196 | * by (count * time_per_clk). In order to minimize integer truncation, | |
197 | * we perform this calculation in an arbitrary unit of time which maintains | |
198 | * the maximum precision, i.e. such that one tick is 1.0e9 of these units, | |
199 | * or close to the precision of a 32-bit int. We then divide by this unit | |
200 | * (which doesn't lose precision) to get nanoseconds. For notation | |
201 | * purposes, this unit is defined as ZHZ = zanoseconds per nanosecond. | |
202 | * | |
203 | * This sequence to do all this is in sysclk_gettime. For efficiency, this | |
204 | * sequence also needs the value that the counter will have if it has just | |
55e303ae A |
205 | * overflowed, so we precompute that also. |
206 | * | |
207 | * The fix for certain really old certain platforms has been removed | |
1c79356b A |
208 | * (specifically the DEC XL5100) have been observed to have problem |
209 | * with latching the counter, and they occasionally (say, one out of | |
210 | * 100,000 times) return a bogus value. Hence, the present code reads | |
211 | * the counter twice and checks for a consistent pair of values. | |
55e303ae A |
212 | * the code was: |
213 | * do { | |
214 | * READ_8254(val); | |
215 | * READ_8254(val2); | |
216 | * } while ( val2 > val || val2 < val - 10 ); | |
217 | * | |
1c79356b A |
218 | * |
219 | * Some attributes of the rt clock can be changed, including the | |
220 | * interrupt resolution. We default to the minimum resolution (10 ms), | |
221 | * but allow a finer resolution to be requested. The assumed frequency | |
222 | * of the clock can also be set since it appears that the actual | |
223 | * frequency of real-world hardware can vary from the nominal by | |
224 | * 200 ppm or more. When the frequency is set, the values above are | |
225 | * recomputed and we continue without resetting or changing anything else. | |
226 | */ | |
227 | #define RTC_MINRES (NSEC_PER_SEC / HZ) /* nsec per tick */ | |
228 | #define RTC_MAXRES (RTC_MINRES / 20) /* nsec per tick */ | |
229 | #define ZANO (1000000000) | |
230 | #define ZHZ (ZANO / (NSEC_PER_SEC / HZ)) | |
231 | #define READ_8254(val) { \ | |
232 | outb(PITCTL_PORT, PIT_C0); \ | |
233 | (val) = inb(PITCTR0_PORT); \ | |
234 | (val) |= inb(PITCTR0_PORT) << 8 ; } | |
235 | ||
55e303ae A |
236 | #define UI_CPUFREQ_ROUNDING_FACTOR 10000000 |
237 | ||
1c79356b A |
238 | |
239 | /* | |
240 | * Forward decl. | |
241 | */ | |
242 | ||
1c79356b A |
243 | void rtc_setvals( unsigned int, clock_res_t ); |
244 | ||
9bccf70c A |
245 | static void rtc_set_cyc_per_sec(); |
246 | ||
55e303ae A |
247 | /* define assembly routines */ |
248 | ||
249 | ||
250 | /* | |
251 | * Inlines to get timestamp counter value. | |
252 | */ | |
253 | ||
254 | inline static uint64_t | |
255 | rdtsc_64(void) | |
256 | { | |
257 | uint64_t result; | |
258 | asm volatile("rdtsc": "=A" (result)); | |
259 | return result; | |
260 | } | |
261 | ||
262 | // create_mul_quant_GHZ create a constant that can be used to multiply | |
263 | // the TSC by to create nanoseconds. This is a 32 bit number | |
264 | // and the TSC *MUST* have a frequency higher than 1000Mhz for this routine to work | |
265 | // | |
266 | // The theory here is that we know how many TSCs-per-sec the processor runs at. Normally to convert this | |
267 | // to nanoseconds you would multiply the current time stamp by 1000000000 (a billion) then divide | |
268 | // by TSCs-per-sec to get nanoseconds. Unfortunatly the TSC is 64 bits which would leave us with | |
269 | // 96 bit intermediate results from the dultiply that must be divided by. | |
270 | // usually thats | |
271 | // uint96 = tsc * numer | |
272 | // nanos = uint96 / denom | |
273 | // Instead, we create this quant constant and it becomes the numerator, the denominator | |
274 | // can then be 0x100000000 which makes our division as simple as forgetting the lower 32 bits | |
275 | // of the result. We can also pass this number to user space as the numer and pass 0xFFFFFFFF | |
276 | // as the denom to converting raw counts to nanos. the difference is so small as to be undetectable | |
277 | // by anything. | |
278 | // unfortunatly we can not do this for sub GHZ processors. In that case, all we do is pass the CPU | |
279 | // speed in raw as the denom and we pass in 1000000000 as the numerator. No short cuts allowed | |
280 | ||
281 | inline static uint32_t | |
282 | create_mul_quant_GHZ(uint32_t quant) | |
283 | { | |
284 | return (uint32_t)((50000000ULL << 32) / quant); | |
285 | } | |
286 | ||
287 | // this routine takes a value of raw TSC ticks and applies the passed mul_quant | |
288 | // generated by create_mul_quant() This is our internal routine for creating | |
289 | // nanoseconds | |
290 | // since we don't really have uint96_t this routine basically does this.... | |
291 | // uint96_t intermediate = (*value) * scale | |
292 | // return (intermediate >> 32) | |
293 | inline static uint64_t | |
294 | fast_get_nano_from_abs(uint64_t value, int scale) | |
295 | { | |
296 | asm (" movl %%edx,%%esi \n\t" | |
297 | " mull %%ecx \n\t" | |
298 | " movl %%edx,%%edi \n\t" | |
299 | " movl %%esi,%%eax \n\t" | |
300 | " mull %%ecx \n\t" | |
301 | " xorl %%ecx,%%ecx \n\t" | |
302 | " addl %%edi,%%eax \n\t" | |
303 | " adcl %%ecx,%%edx " | |
304 | : "+A" (value) | |
305 | : "c" (scale) | |
306 | : "%esi", "%edi"); | |
307 | return value; | |
308 | } | |
309 | ||
310 | /* | |
311 | * this routine basically does this... | |
312 | * ts.tv_sec = nanos / 1000000000; create seconds | |
313 | * ts.tv_nsec = nanos % 1000000000; create remainder nanos | |
314 | */ | |
315 | inline static mach_timespec_t | |
316 | nanos_to_timespec(uint64_t nanos) | |
317 | { | |
318 | union { | |
319 | mach_timespec_t ts; | |
320 | uint64_t u64; | |
321 | } ret; | |
322 | ret.u64 = nanos; | |
323 | asm volatile("divl %1" : "+A" (ret.u64) : "r" (NSEC_PER_SEC)); | |
324 | return ret.ts; | |
325 | } | |
326 | ||
327 | // the following two routine perform the 96 bit arithmetic we need to | |
328 | // convert generic absolute<->nanoseconds | |
329 | // the multiply routine takes a uint64_t and a uint32_t and returns the result in a | |
330 | // uint32_t[3] array. the dicide routine takes this uint32_t[3] array and | |
331 | // divides it by a uint32_t returning a uint64_t | |
332 | inline static void | |
333 | longmul(uint64_t *abstime, uint32_t multiplicand, uint32_t *result) | |
334 | { | |
335 | asm volatile( | |
336 | " pushl %%ebx \n\t" | |
337 | " movl %%eax,%%ebx \n\t" | |
338 | " movl (%%eax),%%eax \n\t" | |
339 | " mull %%ecx \n\t" | |
340 | " xchg %%eax,%%ebx \n\t" | |
341 | " pushl %%edx \n\t" | |
342 | " movl 4(%%eax),%%eax \n\t" | |
343 | " mull %%ecx \n\t" | |
344 | " movl %2,%%ecx \n\t" | |
345 | " movl %%ebx,(%%ecx) \n\t" | |
346 | " popl %%ebx \n\t" | |
347 | " addl %%ebx,%%eax \n\t" | |
348 | " popl %%ebx \n\t" | |
349 | " movl %%eax,4(%%ecx) \n\t" | |
350 | " adcl $0,%%edx \n\t" | |
351 | " movl %%edx,8(%%ecx) // and save it" | |
352 | : : "a"(abstime), "c"(multiplicand), "m"(result)); | |
353 | ||
354 | } | |
355 | ||
356 | inline static uint64_t | |
357 | longdiv(uint32_t *numer, uint32_t denom) | |
358 | { | |
359 | uint64_t result; | |
360 | asm volatile( | |
361 | " pushl %%ebx \n\t" | |
362 | " movl %%eax,%%ebx \n\t" | |
363 | " movl 8(%%eax),%%edx \n\t" | |
364 | " movl 4(%%eax),%%eax \n\t" | |
365 | " divl %%ecx \n\t" | |
366 | " xchg %%ebx,%%eax \n\t" | |
367 | " movl (%%eax),%%eax \n\t" | |
368 | " divl %%ecx \n\t" | |
369 | " xchg %%ebx,%%edx \n\t" | |
370 | " popl %%ebx \n\t" | |
371 | : "=A"(result) : "a"(numer),"c"(denom)); | |
372 | return result; | |
373 | } | |
374 | ||
375 | #define PIT_Mode4 0x08 /* turn on mode 4 one shot software trigger */ | |
376 | ||
377 | // Enable or disable timer 2. | |
378 | inline static void | |
379 | enable_PIT2() | |
380 | { | |
381 | asm volatile( | |
382 | " inb $97,%%al \n\t" | |
383 | " and $253,%%al \n\t" | |
384 | " or $1,%%al \n\t" | |
385 | " outb %%al,$97 \n\t" | |
386 | : : : "%al" ); | |
387 | } | |
388 | ||
389 | inline static void | |
390 | disable_PIT2() | |
391 | { | |
392 | asm volatile( | |
393 | " inb $97,%%al \n\t" | |
394 | " and $253,%%al \n\t" | |
395 | " outb %%al,$97 \n\t" | |
396 | : : : "%al" ); | |
397 | } | |
398 | ||
399 | // ctimeRDTSC() routine sets up counter 2 to count down 1/20 of a second | |
400 | // it pauses until the value is latched in the counter | |
401 | // and then reads the time stamp counter to return to the caller | |
402 | // utility routine | |
403 | // Code to calculate how many processor cycles are in a second... | |
404 | inline static void | |
405 | set_PIT2(int value) | |
406 | { | |
407 | // first, tell the clock we are going to write 16 bytes to the counter and enable one-shot mode | |
408 | // then write the two bytes into the clock register. | |
409 | // loop until the value is "realized" in the clock, this happens on the next tick | |
410 | // | |
411 | asm volatile( | |
412 | " movb $184,%%al \n\t" | |
413 | " outb %%al,$67 \n\t" | |
414 | " movb %%dl,%%al \n\t" | |
415 | " outb %%al,$66 \n\t" | |
416 | " movb %%dh,%%al \n\t" | |
417 | " outb %%al,$66 \n" | |
418 | "1: inb $66,%%al \n\t" | |
419 | " inb $66,%%al \n\t" | |
420 | " cmp %%al,%%dh \n\t" | |
421 | " jne 1b" | |
422 | : : "d"(value) : "%al"); | |
423 | } | |
424 | ||
425 | inline static uint64_t | |
426 | get_PIT2(unsigned int *value) | |
427 | { | |
428 | // this routine first latches the time, then gets the time stamp so we know | |
429 | // how long the read will take later. Reads | |
430 | register uint64_t result; | |
431 | asm volatile( | |
432 | " xorl %%ecx,%%ecx \n\t" | |
433 | " movb $128,%%al \n\t" | |
434 | " outb %%al,$67 \n\t" | |
435 | " rdtsc \n\t" | |
436 | " pushl %%eax \n\t" | |
437 | " inb $66,%%al \n\t" | |
438 | " movb %%al,%%cl \n\t" | |
439 | " inb $66,%%al \n\t" | |
440 | " movb %%al,%%ch \n\t" | |
441 | " popl %%eax " | |
442 | : "=A"(result), "=c"(*value)); | |
443 | return result; | |
444 | } | |
445 | ||
446 | static uint32_t | |
447 | timeRDTSC(void) | |
448 | { | |
449 | uint64_t latchTime; | |
450 | uint64_t saveTime,intermediate; | |
451 | unsigned int timerValue,x; | |
452 | boolean_t int_enabled; | |
453 | uint64_t fact[6] = { 2000011734ll, | |
454 | 2000045259ll, | |
455 | 2000078785ll, | |
456 | 2000112312ll, | |
457 | 2000145841ll, | |
458 | 2000179371ll}; | |
459 | ||
460 | int_enabled = ml_set_interrupts_enabled(FALSE); | |
461 | ||
462 | enable_PIT2(); // turn on PIT2 | |
463 | set_PIT2(0); // reset timer 2 to be zero | |
464 | latchTime = rdtsc_64(); // get the time stamp to time | |
465 | latchTime = get_PIT2(&timerValue) - latchTime; // time how long this takes | |
466 | set_PIT2(59658); // set up the timer to count 1/20th a second | |
467 | saveTime = rdtsc_64(); // now time how ling a 20th a second is... | |
468 | get_PIT2(&x); | |
469 | do { get_PIT2(&timerValue); x = timerValue;} while (timerValue > x); | |
470 | do { | |
471 | intermediate = get_PIT2(&timerValue); | |
472 | if (timerValue>x) printf("Hey we are going backwards! %d, %d\n",timerValue,x); | |
473 | x = timerValue; | |
474 | } while ((timerValue != 0) && (timerValue >5)); | |
475 | printf("Timer value:%d\n",timerValue); | |
476 | printf("intermediate 0x%08x:0x%08x\n",intermediate); | |
477 | printf("saveTime 0x%08x:0x%08x\n",saveTime); | |
478 | ||
479 | intermediate = intermediate - saveTime; // raw # of tsc's it takes for about 1/20 second | |
480 | intermediate = intermediate * fact[timerValue]; // actual time spent | |
481 | intermediate = intermediate / 2000000000ll; // rescale so its exactly 1/20 a second | |
482 | intermediate = intermediate + latchTime; // add on our save fudge | |
483 | set_PIT2(0); // reset timer 2 to be zero | |
484 | disable_PIT2(0); // turn off PIT 2 | |
485 | ml_set_interrupts_enabled(int_enabled); | |
486 | return intermediate; | |
487 | } | |
488 | ||
489 | static uint64_t | |
490 | rdtsctime_to_nanoseconds( void ) | |
491 | { | |
492 | uint32_t numer; | |
493 | uint32_t denom; | |
494 | uint64_t abstime; | |
495 | ||
496 | uint32_t intermediate[3]; | |
497 | ||
498 | numer = rtclock.timebase_const.numer; | |
499 | denom = rtclock.timebase_const.denom; | |
500 | abstime = rdtsc_64(); | |
501 | if (denom == 0xFFFFFFFF) { | |
502 | abstime = fast_get_nano_from_abs(abstime, numer); | |
503 | } else { | |
504 | longmul(&abstime, numer, intermediate); | |
505 | abstime = longdiv(intermediate, denom); | |
506 | } | |
507 | return abstime; | |
508 | } | |
509 | ||
510 | inline static mach_timespec_t | |
511 | rdtsc_to_timespec(void) | |
512 | { | |
513 | uint64_t currNanos; | |
514 | currNanos = rdtsctime_to_nanoseconds(); | |
515 | return nanos_to_timespec(currNanos); | |
516 | } | |
517 | ||
1c79356b A |
518 | /* |
519 | * Initialize non-zero clock structure values. | |
520 | */ | |
521 | void | |
522 | rtc_setvals( | |
523 | unsigned int new_clknum, | |
524 | clock_res_t new_ires | |
525 | ) | |
526 | { | |
527 | unsigned int timeperclk; | |
528 | unsigned int scale0; | |
529 | unsigned int scale1; | |
530 | unsigned int res; | |
531 | ||
532 | clknum = new_clknum; | |
533 | rtc_intr_freq = (NSEC_PER_SEC / new_ires); | |
534 | rtc_intr_hertz = rtc_intr_freq / HZ; | |
535 | clks_per_int = (clknum + (rtc_intr_freq / 2)) / rtc_intr_freq; | |
536 | clks_per_int_99 = clks_per_int - clks_per_int/100; | |
537 | ||
538 | /* | |
539 | * The following calculations are done with scaling integer operations | |
540 | * in order that the integer results are accurate to the lsb. | |
541 | */ | |
542 | timeperclk = div_scale(ZANO, clknum, &scale0); /* 838.105647 nsec */ | |
543 | ||
544 | time_per_clk = mul_scale(ZHZ, timeperclk, &scale1); /* 83810 */ | |
545 | if (scale0 > scale1) | |
546 | time_per_clk >>= (scale0 - scale1); | |
547 | else if (scale0 < scale1) | |
548 | panic("rtc_clock: time_per_clk overflow\n"); | |
549 | ||
550 | /* | |
551 | * Notice that rtclock.intr_nsec is signed ==> use unsigned int res | |
552 | */ | |
553 | res = mul_scale(clks_per_int, timeperclk, &scale1); /* 10000276 */ | |
554 | if (scale0 > scale1) | |
555 | rtclock.intr_nsec = res >> (scale0 - scale1); | |
556 | else | |
557 | panic("rtc_clock: rtclock.intr_nsec overflow\n"); | |
558 | ||
559 | rtc_intr_count = 1; | |
560 | RtcDelt = rtclock.intr_nsec/2; | |
561 | } | |
562 | ||
563 | /* | |
564 | * Configure the real-time clock device. Return success (1) | |
565 | * or failure (0). | |
566 | */ | |
567 | ||
568 | int | |
569 | sysclk_config(void) | |
570 | { | |
571 | int RtcFlag; | |
572 | int pic; | |
573 | ||
574 | #if NCPUS > 1 | |
575 | mp_disable_preemption(); | |
576 | if (cpu_number() != master_cpu) { | |
577 | mp_enable_preemption(); | |
578 | return(1); | |
579 | } | |
580 | mp_enable_preemption(); | |
581 | #endif | |
582 | /* | |
583 | * Setup device. | |
584 | */ | |
1c79356b | 585 | pic = 0; /* FIXME .. interrupt registration moved to AppleIntelClock */ |
1c79356b A |
586 | |
587 | ||
588 | /* | |
589 | * We should attempt to test the real-time clock | |
590 | * device here. If it were to fail, we should panic | |
591 | * the system. | |
592 | */ | |
593 | RtcFlag = /* test device */1; | |
594 | printf("realtime clock configured\n"); | |
595 | ||
596 | simple_lock_init(&rtclock.lock, ETAP_NO_TRACE); | |
597 | return (RtcFlag); | |
598 | } | |
599 | ||
600 | /* | |
601 | * Initialize the real-time clock device. Return success (1) | |
602 | * or failure (0). Since the real-time clock is required to | |
603 | * provide canonical mapped time, we allocate a page to keep | |
604 | * the clock time value. In addition, various variables used | |
605 | * to support the clock are initialized. Note: the clock is | |
606 | * not started until rtclock_reset is called. | |
607 | */ | |
608 | int | |
609 | sysclk_init(void) | |
610 | { | |
611 | vm_offset_t *vp; | |
612 | #if NCPUS > 1 | |
613 | mp_disable_preemption(); | |
614 | if (cpu_number() != master_cpu) { | |
615 | mp_enable_preemption(); | |
616 | return(1); | |
617 | } | |
618 | mp_enable_preemption(); | |
619 | #endif | |
620 | ||
621 | RtcTime = &rtclock.time; | |
622 | rtc_setvals( CLKNUM, RTC_MINRES ); /* compute constants */ | |
9bccf70c | 623 | rtc_set_cyc_per_sec(); /* compute number of tsc beats per second */ |
55e303ae | 624 | clock_timebase_init(); |
1c79356b A |
625 | return (1); |
626 | } | |
627 | ||
628 | static volatile unsigned int last_ival = 0; | |
629 | ||
630 | /* | |
631 | * Get the clock device time. This routine is responsible | |
632 | * for converting the device's machine dependent time value | |
633 | * into a canonical mach_timespec_t value. | |
634 | */ | |
635 | kern_return_t | |
636 | sysclk_gettime( | |
637 | mach_timespec_t *cur_time) /* OUT */ | |
638 | { | |
1c79356b A |
639 | if (!RtcTime) { |
640 | /* Uninitialized */ | |
641 | cur_time->tv_nsec = 0; | |
642 | cur_time->tv_sec = 0; | |
643 | return (KERN_SUCCESS); | |
644 | } | |
645 | ||
55e303ae | 646 | *cur_time = rdtsc_to_timespec(); |
1c79356b A |
647 | return (KERN_SUCCESS); |
648 | } | |
649 | ||
650 | kern_return_t | |
651 | sysclk_gettime_internal( | |
652 | mach_timespec_t *cur_time) /* OUT */ | |
653 | { | |
1c79356b A |
654 | if (!RtcTime) { |
655 | /* Uninitialized */ | |
656 | cur_time->tv_nsec = 0; | |
657 | cur_time->tv_sec = 0; | |
658 | return (KERN_SUCCESS); | |
659 | } | |
55e303ae | 660 | *cur_time = rdtsc_to_timespec(); |
1c79356b A |
661 | return (KERN_SUCCESS); |
662 | } | |
663 | ||
664 | /* | |
665 | * Get the clock device time when ALL interrupts are already disabled. | |
666 | * Same as above except for turning interrupts off and on. | |
667 | * This routine is responsible for converting the device's machine dependent | |
668 | * time value into a canonical mach_timespec_t value. | |
669 | */ | |
670 | void | |
671 | sysclk_gettime_interrupts_disabled( | |
672 | mach_timespec_t *cur_time) /* OUT */ | |
673 | { | |
1c79356b A |
674 | if (!RtcTime) { |
675 | /* Uninitialized */ | |
676 | cur_time->tv_nsec = 0; | |
677 | cur_time->tv_sec = 0; | |
678 | return; | |
679 | } | |
55e303ae | 680 | *cur_time = rdtsc_to_timespec(); |
1c79356b A |
681 | } |
682 | ||
9bccf70c A |
683 | // utility routine |
684 | // Code to calculate how many processor cycles are in a second... | |
1c79356b | 685 | |
9bccf70c A |
686 | static void |
687 | rtc_set_cyc_per_sec() | |
688 | { | |
1c79356b | 689 | |
55e303ae A |
690 | uint32_t twen_cycles; |
691 | uint32_t cycles; | |
9bccf70c | 692 | |
55e303ae A |
693 | twen_cycles = timeRDTSC(); |
694 | if (twen_cycles> (1000000000/20)) { | |
695 | // we create this value so that you can use just a "fast" multiply to get nanos | |
696 | rtc_quant_scale = create_mul_quant_GHZ(twen_cycles); | |
697 | rtclock.timebase_const.numer = rtc_quant_scale; // because ctimeRDTSC gives us 1/20 a seconds worth | |
698 | rtclock.timebase_const.denom = 0xffffffff; // so that nanoseconds = (TSC * numer) / denom | |
699 | ||
700 | } else { | |
701 | rtclock.timebase_const.numer = 1000000000/20; // because ctimeRDTSC gives us 1/20 a seconds worth | |
702 | rtclock.timebase_const.denom = twen_cycles; // so that nanoseconds = (TSC * numer) / denom | |
9bccf70c | 703 | } |
55e303ae A |
704 | cycles = twen_cycles; // number of cycles in 1/20th a second |
705 | rtc_cyc_per_sec = cycles*20; // multiply it by 20 and we are done.. BUT we also want to calculate... | |
706 | ||
707 | cycles = ((rtc_cyc_per_sec + UI_CPUFREQ_ROUNDING_FACTOR - 1) / UI_CPUFREQ_ROUNDING_FACTOR) * UI_CPUFREQ_ROUNDING_FACTOR; | |
708 | gPEClockFrequencyInfo.cpu_clock_rate_hz = cycles; | |
709 | DISPLAYVALUE(rtc_set_cyc_per_sec,rtc_cyc_per_sec); | |
710 | DISPLAYEXIT(rtc_set_cyc_per_sec); | |
9bccf70c | 711 | } |
1c79356b | 712 | |
55e303ae A |
713 | void |
714 | clock_get_system_microtime( | |
715 | uint32_t *secs, | |
716 | uint32_t *microsecs) | |
9bccf70c | 717 | { |
55e303ae A |
718 | mach_timespec_t now; |
719 | ||
720 | sysclk_gettime(&now); | |
721 | ||
722 | *secs = now.tv_sec; | |
723 | *microsecs = now.tv_nsec / NSEC_PER_USEC; | |
1c79356b A |
724 | } |
725 | ||
55e303ae A |
726 | void |
727 | clock_get_system_nanotime( | |
728 | uint32_t *secs, | |
729 | uint32_t *nanosecs) | |
730 | { | |
731 | mach_timespec_t now; | |
732 | ||
733 | sysclk_gettime(&now); | |
734 | ||
735 | *secs = now.tv_sec; | |
736 | *nanosecs = now.tv_nsec; | |
737 | } | |
9bccf70c | 738 | |
1c79356b A |
739 | /* |
740 | * Get clock device attributes. | |
741 | */ | |
742 | kern_return_t | |
743 | sysclk_getattr( | |
744 | clock_flavor_t flavor, | |
745 | clock_attr_t attr, /* OUT */ | |
746 | mach_msg_type_number_t *count) /* IN/OUT */ | |
747 | { | |
748 | spl_t s; | |
749 | ||
750 | if (*count != 1) | |
751 | return (KERN_FAILURE); | |
752 | switch (flavor) { | |
753 | ||
754 | case CLOCK_GET_TIME_RES: /* >0 res */ | |
55e303ae | 755 | #if (NCPUS == 1) |
1c79356b A |
756 | LOCK_RTC(s); |
757 | *(clock_res_t *) attr = 1000; | |
758 | UNLOCK_RTC(s); | |
759 | break; | |
55e303ae | 760 | #endif /* (NCPUS == 1) */ |
1c79356b A |
761 | case CLOCK_ALARM_CURRES: /* =0 no alarm */ |
762 | LOCK_RTC(s); | |
763 | *(clock_res_t *) attr = rtclock.intr_nsec; | |
764 | UNLOCK_RTC(s); | |
765 | break; | |
766 | ||
767 | case CLOCK_ALARM_MAXRES: | |
768 | *(clock_res_t *) attr = RTC_MAXRES; | |
769 | break; | |
770 | ||
771 | case CLOCK_ALARM_MINRES: | |
772 | *(clock_res_t *) attr = RTC_MINRES; | |
773 | break; | |
774 | ||
775 | default: | |
776 | return (KERN_INVALID_VALUE); | |
777 | } | |
778 | return (KERN_SUCCESS); | |
779 | } | |
780 | ||
781 | /* | |
782 | * Set clock device attributes. | |
783 | */ | |
784 | kern_return_t | |
785 | sysclk_setattr( | |
786 | clock_flavor_t flavor, | |
787 | clock_attr_t attr, /* IN */ | |
788 | mach_msg_type_number_t count) /* IN */ | |
789 | { | |
790 | spl_t s; | |
791 | int freq; | |
792 | int adj; | |
793 | clock_res_t new_ires; | |
794 | ||
795 | if (count != 1) | |
796 | return (KERN_FAILURE); | |
797 | switch (flavor) { | |
798 | ||
799 | case CLOCK_GET_TIME_RES: | |
800 | case CLOCK_ALARM_MAXRES: | |
801 | case CLOCK_ALARM_MINRES: | |
802 | return (KERN_FAILURE); | |
803 | ||
804 | case CLOCK_ALARM_CURRES: | |
805 | new_ires = *(clock_res_t *) attr; | |
806 | ||
807 | /* | |
808 | * The new resolution must be within the predetermined | |
809 | * range. If the desired resolution cannot be achieved | |
810 | * to within 0.1%, an error is returned. | |
811 | */ | |
812 | if (new_ires < RTC_MAXRES || new_ires > RTC_MINRES) | |
813 | return (KERN_INVALID_VALUE); | |
814 | freq = (NSEC_PER_SEC / new_ires); | |
815 | adj = (((clknum % freq) * new_ires) / clknum); | |
816 | if (adj > (new_ires / 1000)) | |
817 | return (KERN_INVALID_VALUE); | |
818 | /* | |
819 | * Record the new alarm resolution which will take effect | |
820 | * on the next HZ aligned clock tick. | |
821 | */ | |
822 | LOCK_RTC(s); | |
823 | if ( freq != rtc_intr_freq ) { | |
824 | rtclock.new_ires = new_ires; | |
825 | new_clknum = clknum; | |
826 | } | |
827 | UNLOCK_RTC(s); | |
828 | return (KERN_SUCCESS); | |
829 | ||
830 | default: | |
831 | return (KERN_INVALID_VALUE); | |
832 | } | |
833 | } | |
834 | ||
835 | /* | |
836 | * Set next alarm time for the clock device. This call | |
837 | * always resets the time to deliver an alarm for the | |
838 | * clock. | |
839 | */ | |
840 | void | |
841 | sysclk_setalarm( | |
842 | mach_timespec_t *alarm_time) | |
843 | { | |
844 | spl_t s; | |
845 | ||
846 | LOCK_RTC(s); | |
847 | rtclock.alarm_time = *alarm_time; | |
848 | RtcAlrm = &rtclock.alarm_time; | |
849 | UNLOCK_RTC(s); | |
850 | } | |
851 | ||
852 | /* | |
853 | * Configure the calendar clock. | |
854 | */ | |
855 | int | |
856 | calend_config(void) | |
857 | { | |
858 | return bbc_config(); | |
859 | } | |
860 | ||
861 | /* | |
862 | * Initialize calendar clock. | |
863 | */ | |
864 | int | |
865 | calend_init(void) | |
866 | { | |
867 | return (1); | |
868 | } | |
869 | ||
870 | /* | |
871 | * Get the current clock time. | |
872 | */ | |
873 | kern_return_t | |
874 | calend_gettime( | |
875 | mach_timespec_t *cur_time) /* OUT */ | |
876 | { | |
877 | spl_t s; | |
878 | ||
879 | LOCK_RTC(s); | |
880 | if (!rtclock.calend_is_set) { | |
881 | UNLOCK_RTC(s); | |
882 | return (KERN_FAILURE); | |
883 | } | |
884 | ||
885 | (void) sysclk_gettime_internal(cur_time); | |
886 | ADD_MACH_TIMESPEC(cur_time, &rtclock.calend_offset); | |
887 | UNLOCK_RTC(s); | |
888 | ||
889 | return (KERN_SUCCESS); | |
890 | } | |
891 | ||
55e303ae A |
892 | void |
893 | clock_get_calendar_microtime( | |
894 | uint32_t *secs, | |
895 | uint32_t *microsecs) | |
896 | { | |
897 | mach_timespec_t now; | |
898 | ||
899 | calend_gettime(&now); | |
900 | ||
901 | *secs = now.tv_sec; | |
902 | *microsecs = now.tv_nsec / NSEC_PER_USEC; | |
903 | } | |
904 | ||
905 | void | |
906 | clock_get_calendar_nanotime( | |
907 | uint32_t *secs, | |
908 | uint32_t *nanosecs) | |
1c79356b | 909 | { |
55e303ae A |
910 | mach_timespec_t now; |
911 | ||
912 | calend_gettime(&now); | |
913 | ||
914 | *secs = now.tv_sec; | |
915 | *nanosecs = now.tv_nsec; | |
916 | } | |
917 | ||
918 | void | |
919 | clock_set_calendar_microtime( | |
920 | uint32_t secs, | |
921 | uint32_t microsecs) | |
922 | { | |
923 | mach_timespec_t new_time, curr_time; | |
1c79356b A |
924 | spl_t s; |
925 | ||
926 | LOCK_RTC(s); | |
927 | (void) sysclk_gettime_internal(&curr_time); | |
55e303ae A |
928 | rtclock.calend_offset.tv_sec = new_time.tv_sec = secs; |
929 | rtclock.calend_offset.tv_nsec = new_time.tv_nsec = microsecs * NSEC_PER_USEC; | |
1c79356b A |
930 | SUB_MACH_TIMESPEC(&rtclock.calend_offset, &curr_time); |
931 | rtclock.calend_is_set = TRUE; | |
932 | UNLOCK_RTC(s); | |
933 | ||
55e303ae | 934 | (void) bbc_settime(&new_time); |
1c79356b | 935 | |
55e303ae | 936 | host_notify_calendar_change(); |
1c79356b A |
937 | } |
938 | ||
939 | /* | |
940 | * Get clock device attributes. | |
941 | */ | |
942 | kern_return_t | |
943 | calend_getattr( | |
944 | clock_flavor_t flavor, | |
945 | clock_attr_t attr, /* OUT */ | |
946 | mach_msg_type_number_t *count) /* IN/OUT */ | |
947 | { | |
948 | spl_t s; | |
949 | ||
950 | if (*count != 1) | |
951 | return (KERN_FAILURE); | |
952 | switch (flavor) { | |
953 | ||
954 | case CLOCK_GET_TIME_RES: /* >0 res */ | |
55e303ae | 955 | #if (NCPUS == 1) |
1c79356b A |
956 | LOCK_RTC(s); |
957 | *(clock_res_t *) attr = 1000; | |
958 | UNLOCK_RTC(s); | |
959 | break; | |
55e303ae | 960 | #else /* (NCPUS == 1) */ |
1c79356b A |
961 | LOCK_RTC(s); |
962 | *(clock_res_t *) attr = rtclock.intr_nsec; | |
963 | UNLOCK_RTC(s); | |
964 | break; | |
55e303ae | 965 | #endif /* (NCPUS == 1) */ |
1c79356b A |
966 | |
967 | case CLOCK_ALARM_CURRES: /* =0 no alarm */ | |
968 | case CLOCK_ALARM_MINRES: | |
969 | case CLOCK_ALARM_MAXRES: | |
970 | *(clock_res_t *) attr = 0; | |
971 | break; | |
972 | ||
973 | default: | |
974 | return (KERN_INVALID_VALUE); | |
975 | } | |
976 | return (KERN_SUCCESS); | |
977 | } | |
978 | ||
55e303ae A |
979 | #define tickadj (40*NSEC_PER_USEC) /* "standard" skew, ns / tick */ |
980 | #define bigadj (NSEC_PER_SEC) /* use 10x skew above bigadj ns */ | |
981 | ||
982 | uint32_t | |
983 | clock_set_calendar_adjtime( | |
984 | int32_t *secs, | |
985 | int32_t *microsecs) | |
1c79356b | 986 | { |
55e303ae A |
987 | int64_t total, ototal; |
988 | uint32_t interval = 0; | |
989 | spl_t s; | |
990 | ||
991 | total = (int64_t)*secs * NSEC_PER_SEC + *microsecs * NSEC_PER_USEC; | |
1c79356b A |
992 | |
993 | LOCK_RTC(s); | |
55e303ae A |
994 | ototal = rtclock.calend_adjtotal; |
995 | ||
996 | if (total != 0) { | |
997 | int32_t delta = tickadj; | |
998 | ||
999 | if (total > 0) { | |
1000 | if (total > bigadj) | |
1001 | delta *= 10; | |
1002 | if (delta > total) | |
1003 | delta = total; | |
1004 | } | |
1005 | else { | |
1006 | if (total < -bigadj) | |
1007 | delta *= 10; | |
1008 | delta = -delta; | |
1009 | if (delta < total) | |
1010 | delta = total; | |
1011 | } | |
1012 | ||
1013 | rtclock.calend_adjtotal = total; | |
1014 | rtclock.calend_adjdelta = delta; | |
1015 | ||
1016 | interval = (NSEC_PER_SEC / HZ); | |
1017 | } | |
1018 | else | |
1019 | rtclock.calend_adjdelta = rtclock.calend_adjtotal = 0; | |
1020 | ||
1021 | UNLOCK_RTC(s); | |
1022 | ||
1023 | if (ototal == 0) | |
1024 | *secs = *microsecs = 0; | |
1025 | else { | |
1026 | *secs = ototal / NSEC_PER_SEC; | |
1027 | *microsecs = ototal % NSEC_PER_SEC; | |
1028 | } | |
1029 | ||
1030 | return (interval); | |
1031 | } | |
1032 | ||
1033 | uint32_t | |
1034 | clock_adjust_calendar(void) | |
1035 | { | |
1036 | uint32_t interval = 0; | |
1037 | int32_t delta; | |
1038 | spl_t s; | |
1039 | ||
1040 | LOCK_RTC(s); | |
1041 | delta = rtclock.calend_adjdelta; | |
1042 | ADD_MACH_TIMESPEC_NSEC(&rtclock.calend_offset, delta); | |
1043 | ||
1044 | rtclock.calend_adjtotal -= delta; | |
1045 | ||
1046 | if (delta > 0) { | |
1047 | if (delta > rtclock.calend_adjtotal) | |
1048 | rtclock.calend_adjdelta = rtclock.calend_adjtotal; | |
1049 | } | |
1050 | else | |
1051 | if (delta < 0) { | |
1052 | if (delta < rtclock.calend_adjtotal) | |
1053 | rtclock.calend_adjdelta = rtclock.calend_adjtotal; | |
1054 | } | |
1055 | ||
1056 | if (rtclock.calend_adjdelta != 0) | |
1057 | interval = (NSEC_PER_SEC / HZ); | |
1058 | ||
1c79356b | 1059 | UNLOCK_RTC(s); |
55e303ae A |
1060 | |
1061 | return (interval); | |
1c79356b A |
1062 | } |
1063 | ||
1064 | void | |
1065 | clock_initialize_calendar(void) | |
1066 | { | |
1067 | mach_timespec_t bbc_time, curr_time; | |
1068 | spl_t s; | |
1069 | ||
1070 | if (bbc_gettime(&bbc_time) != KERN_SUCCESS) | |
1071 | return; | |
1072 | ||
1073 | LOCK_RTC(s); | |
1074 | if (!rtclock.calend_is_set) { | |
1075 | (void) sysclk_gettime_internal(&curr_time); | |
1076 | rtclock.calend_offset = bbc_time; | |
1077 | SUB_MACH_TIMESPEC(&rtclock.calend_offset, &curr_time); | |
1078 | rtclock.calend_is_set = TRUE; | |
1079 | } | |
1080 | UNLOCK_RTC(s); | |
1c79356b | 1081 | |
55e303ae | 1082 | host_notify_calendar_change(); |
1c79356b A |
1083 | } |
1084 | ||
1085 | void | |
1086 | clock_timebase_info( | |
1087 | mach_timebase_info_t info) | |
1088 | { | |
1089 | spl_t s; | |
1090 | ||
1091 | LOCK_RTC(s); | |
55e303ae A |
1092 | if (rtclock.timebase_const.denom == 0xFFFFFFFF) { |
1093 | info->numer = info->denom = rtc_quant_scale; | |
1094 | } else { | |
1095 | info->numer = info->denom = 1; | |
1096 | } | |
1c79356b A |
1097 | UNLOCK_RTC(s); |
1098 | } | |
1099 | ||
1100 | void | |
1101 | clock_set_timer_deadline( | |
0b4e3aa0 | 1102 | uint64_t deadline) |
1c79356b A |
1103 | { |
1104 | spl_t s; | |
1105 | ||
1106 | LOCK_RTC(s); | |
1107 | rtclock.timer_deadline = deadline; | |
1108 | rtclock.timer_is_set = TRUE; | |
1109 | UNLOCK_RTC(s); | |
1110 | } | |
1111 | ||
1112 | void | |
1113 | clock_set_timer_func( | |
1114 | clock_timer_func_t func) | |
1115 | { | |
1116 | spl_t s; | |
1117 | ||
1118 | LOCK_RTC(s); | |
1119 | if (rtclock.timer_expire == NULL) | |
1120 | rtclock.timer_expire = func; | |
1121 | UNLOCK_RTC(s); | |
1122 | } | |
1123 | ||
1124 | \f | |
1125 | ||
1126 | /* | |
1127 | * Load the count register and start the clock. | |
1128 | */ | |
1129 | #define RTCLOCK_RESET() { \ | |
1130 | outb(PITCTL_PORT, PIT_C0|PIT_NDIVMODE|PIT_READMODE); \ | |
1131 | outb(PITCTR0_PORT, (clks_per_int & 0xff)); \ | |
1132 | outb(PITCTR0_PORT, (clks_per_int >> 8)); \ | |
1133 | } | |
1134 | ||
1135 | /* | |
1136 | * Reset the clock device. This causes the realtime clock | |
1137 | * device to reload its mode and count value (frequency). | |
1138 | * Note: the CPU should be calibrated | |
1139 | * before starting the clock for the first time. | |
1140 | */ | |
1141 | ||
1142 | void | |
1143 | rtclock_reset(void) | |
1144 | { | |
1145 | int s; | |
1146 | ||
55e303ae | 1147 | #if NCPUS > 1 |
1c79356b A |
1148 | mp_disable_preemption(); |
1149 | if (cpu_number() != master_cpu) { | |
1150 | mp_enable_preemption(); | |
1151 | return; | |
1152 | } | |
1153 | mp_enable_preemption(); | |
55e303ae | 1154 | #endif /* NCPUS > 1 */ |
1c79356b A |
1155 | LOCK_RTC(s); |
1156 | RTCLOCK_RESET(); | |
1157 | UNLOCK_RTC(s); | |
1158 | } | |
1159 | ||
1160 | /* | |
1161 | * Real-time clock device interrupt. Called only on the | |
1162 | * master processor. Updates the clock time and upcalls | |
1163 | * into the higher level clock code to deliver alarms. | |
1164 | */ | |
1165 | int | |
55e303ae | 1166 | rtclock_intr(struct i386_interrupt_state *regs) |
1c79356b | 1167 | { |
55e303ae | 1168 | uint64_t abstime; |
1c79356b | 1169 | mach_timespec_t clock_time; |
55e303ae A |
1170 | int i; |
1171 | spl_t s; | |
1172 | boolean_t usermode; | |
1c79356b A |
1173 | |
1174 | /* | |
1175 | * Update clock time. Do the update so that the macro | |
1176 | * MTS_TO_TS() for reading the mapped time works (e.g. | |
1177 | * update in order: mtv_csec, mtv_time.tv_nsec, mtv_time.tv_sec). | |
1178 | */ | |
1179 | LOCK_RTC(s); | |
55e303ae A |
1180 | abstime = rdtsctime_to_nanoseconds(); // get the time as of the TSC |
1181 | clock_time = nanos_to_timespec(abstime); // turn it into a timespec | |
1182 | rtclock.time.tv_nsec = clock_time.tv_nsec; | |
1183 | rtclock.time.tv_sec = clock_time.tv_sec; | |
1184 | rtclock.abstime = abstime; | |
1185 | ||
1c79356b A |
1186 | /* note time now up to date */ |
1187 | last_ival = 0; | |
1188 | ||
55e303ae A |
1189 | /* |
1190 | * On a HZ-tick boundary: return 0 and adjust the clock | |
1191 | * alarm resolution (if requested). Otherwise return a | |
1192 | * non-zero value. | |
1193 | */ | |
1194 | if ((i = --rtc_intr_count) == 0) { | |
1195 | if (rtclock.new_ires) { | |
1196 | rtc_setvals(new_clknum, rtclock.new_ires); | |
1197 | RTCLOCK_RESET(); /* lock clock register */ | |
1198 | rtclock.new_ires = 0; | |
1199 | } | |
1200 | rtc_intr_count = rtc_intr_hertz; | |
1201 | UNLOCK_RTC(s); | |
1202 | usermode = (regs->efl & EFL_VM) || ((regs->cs & 0x03) != 0); | |
1203 | hertz_tick(usermode, regs->eip); | |
1204 | LOCK_RTC(s); | |
1205 | } | |
1206 | ||
0b4e3aa0 A |
1207 | if ( rtclock.timer_is_set && |
1208 | rtclock.timer_deadline <= abstime ) { | |
1c79356b A |
1209 | rtclock.timer_is_set = FALSE; |
1210 | UNLOCK_RTC(s); | |
1211 | ||
1212 | (*rtclock.timer_expire)(abstime); | |
1213 | ||
1214 | LOCK_RTC(s); | |
1215 | } | |
1216 | ||
1217 | /* | |
1218 | * Perform alarm clock processing if needed. The time | |
1219 | * passed up is incremented by a half-interrupt tick | |
1220 | * to trigger alarms closest to their desired times. | |
1221 | * The clock_alarm_intr() routine calls sysclk_setalrm() | |
1222 | * before returning if later alarms are pending. | |
1223 | */ | |
1224 | ||
1225 | if (RtcAlrm && (RtcAlrm->tv_sec < RtcTime->tv_sec || | |
1226 | (RtcAlrm->tv_sec == RtcTime->tv_sec && | |
1227 | RtcDelt >= RtcAlrm->tv_nsec - RtcTime->tv_nsec))) { | |
1228 | clock_time.tv_sec = 0; | |
1229 | clock_time.tv_nsec = RtcDelt; | |
1230 | ADD_MACH_TIMESPEC (&clock_time, RtcTime); | |
1231 | RtcAlrm = 0; | |
1232 | UNLOCK_RTC(s); | |
1233 | /* | |
1234 | * Call clock_alarm_intr() without RTC-lock. | |
1235 | * The lock ordering is always CLOCK-lock | |
1236 | * before RTC-lock. | |
1237 | */ | |
1238 | clock_alarm_intr(SYSTEM_CLOCK, &clock_time); | |
1239 | LOCK_RTC(s); | |
1240 | } | |
1241 | ||
1c79356b A |
1242 | UNLOCK_RTC(s); |
1243 | return (i); | |
1244 | } | |
1245 | ||
1246 | void | |
1247 | clock_get_uptime( | |
0b4e3aa0 | 1248 | uint64_t *result) |
1c79356b | 1249 | { |
55e303ae A |
1250 | *result = rdtsctime_to_nanoseconds(); |
1251 | } | |
1c79356b | 1252 | |
55e303ae A |
1253 | uint64_t |
1254 | mach_absolute_time(void) | |
1255 | { | |
1256 | return rdtsctime_to_nanoseconds(); | |
1c79356b A |
1257 | } |
1258 | ||
1259 | void | |
1260 | clock_interval_to_deadline( | |
0b4e3aa0 A |
1261 | uint32_t interval, |
1262 | uint32_t scale_factor, | |
1263 | uint64_t *result) | |
1c79356b | 1264 | { |
0b4e3aa0 | 1265 | uint64_t abstime; |
1c79356b A |
1266 | |
1267 | clock_get_uptime(result); | |
1268 | ||
1269 | clock_interval_to_absolutetime_interval(interval, scale_factor, &abstime); | |
1270 | ||
0b4e3aa0 | 1271 | *result += abstime; |
1c79356b A |
1272 | } |
1273 | ||
1274 | void | |
1275 | clock_interval_to_absolutetime_interval( | |
0b4e3aa0 A |
1276 | uint32_t interval, |
1277 | uint32_t scale_factor, | |
1278 | uint64_t *result) | |
1c79356b | 1279 | { |
0b4e3aa0 | 1280 | *result = (uint64_t)interval * scale_factor; |
1c79356b A |
1281 | } |
1282 | ||
1283 | void | |
1284 | clock_absolutetime_interval_to_deadline( | |
0b4e3aa0 A |
1285 | uint64_t abstime, |
1286 | uint64_t *result) | |
1c79356b A |
1287 | { |
1288 | clock_get_uptime(result); | |
1289 | ||
0b4e3aa0 | 1290 | *result += abstime; |
1c79356b A |
1291 | } |
1292 | ||
1293 | void | |
1294 | absolutetime_to_nanoseconds( | |
0b4e3aa0 A |
1295 | uint64_t abstime, |
1296 | uint64_t *result) | |
1c79356b | 1297 | { |
0b4e3aa0 | 1298 | *result = abstime; |
1c79356b A |
1299 | } |
1300 | ||
1301 | void | |
1302 | nanoseconds_to_absolutetime( | |
0b4e3aa0 A |
1303 | uint64_t nanoseconds, |
1304 | uint64_t *result) | |
1c79356b | 1305 | { |
0b4e3aa0 | 1306 | *result = nanoseconds; |
1c79356b A |
1307 | } |
1308 | ||
1309 | /* | |
55e303ae | 1310 | * Spin-loop delay primitives. |
1c79356b | 1311 | */ |
55e303ae A |
1312 | void |
1313 | delay_for_interval( | |
1314 | uint32_t interval, | |
1315 | uint32_t scale_factor) | |
1c79356b | 1316 | { |
55e303ae | 1317 | uint64_t now, end; |
1c79356b | 1318 | |
55e303ae | 1319 | clock_interval_to_deadline(interval, scale_factor, &end); |
1c79356b | 1320 | |
55e303ae A |
1321 | do { |
1322 | cpu_pause(); | |
1323 | now = mach_absolute_time(); | |
1324 | } while (now < end); | |
1c79356b A |
1325 | } |
1326 | ||
1c79356b | 1327 | void |
55e303ae A |
1328 | clock_delay_until( |
1329 | uint64_t deadline) | |
1330 | { | |
1331 | uint64_t now; | |
1332 | ||
1333 | do { | |
1334 | cpu_pause(); | |
1335 | now = mach_absolute_time(); | |
1336 | } while (now < deadline); | |
1337 | } | |
1c79356b A |
1338 | |
1339 | void | |
55e303ae A |
1340 | delay( |
1341 | int usec) | |
1c79356b | 1342 | { |
55e303ae | 1343 | delay_for_interval((usec < 0)? -usec: usec, NSEC_PER_USEC); |
1c79356b | 1344 | } |