/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ */ /* * @APPLE_FREE_COPYRIGHT@ */ /* * File: rtclock.c * Purpose: Routines for handling the machine dependent * real-time clock. */ #include #include #include #include #include #include #include /* HZ */ #include #include #include #include #include #include int sysclk_config(void); int sysclk_init(void); void treqs(uint32_t dec); kern_return_t sysclk_gettime( mach_timespec_t *cur_time); kern_return_t sysclk_getattr( clock_flavor_t flavor, clock_attr_t attr, mach_msg_type_number_t *count); void sysclk_setalarm( mach_timespec_t *deadline); struct clock_ops sysclk_ops = { sysclk_config, sysclk_init, sysclk_gettime, 0, sysclk_getattr, 0, sysclk_setalarm, }; int calend_config(void); int calend_init(void); kern_return_t calend_gettime( mach_timespec_t *cur_time); kern_return_t calend_getattr( clock_flavor_t flavor, clock_attr_t attr, mach_msg_type_number_t *count); struct clock_ops calend_ops = { calend_config, calend_init, calend_gettime, 0, calend_getattr, 0, 0, }; /* local data declarations */ static struct rtclock_calend { uint32_t epoch; uint32_t microepoch; uint64_t epoch1; int64_t adjtotal; int32_t adjdelta; } rtclock_calend; static boolean_t rtclock_initialized; static uint64_t rtclock_tick_deadline[NCPUS]; #define NSEC_PER_HZ (NSEC_PER_SEC / HZ) static uint32_t rtclock_tick_interval; static uint32_t rtclock_sec_divisor; static mach_timebase_info_data_t rtclock_timebase_const; static boolean_t rtclock_timebase_initialized; static struct rtclock_timer { uint64_t deadline; uint32_t /*boolean_t*/ is_set:1, has_expired:1, :0; } rtclock_timer[NCPUS]; static clock_timer_func_t rtclock_timer_expire; static timer_call_data_t rtclock_alarm_timer; static void timespec_to_absolutetime( mach_timespec_t *ts, uint64_t *result); static int deadline_to_decrementer( uint64_t deadline, uint64_t now); static void rtclock_alarm_expire( timer_call_param_t p0, timer_call_param_t p1); /* global data declarations */ #define DECREMENTER_MAX 0x7FFFFFFFUL #define DECREMENTER_MIN 0xAUL natural_t rtclock_decrementer_min; decl_simple_lock_data(static,rtclock_lock) /* * Macros to lock/unlock real-time clock device. */ #define LOCK_RTC(s) \ MACRO_BEGIN \ (s) = splclock(); \ simple_lock(&rtclock_lock); \ MACRO_END #define UNLOCK_RTC(s) \ MACRO_BEGIN \ simple_unlock(&rtclock_lock); \ splx(s); \ MACRO_END static void timebase_callback( struct timebase_freq_t *freq) { uint32_t numer, denom; uint64_t abstime; spl_t s; if ( freq->timebase_den < 1 || freq->timebase_den > 4 || freq->timebase_num < freq->timebase_den ) panic("rtclock timebase_callback: invalid constant %d / %d", freq->timebase_num, freq->timebase_den); denom = freq->timebase_num; numer = freq->timebase_den * NSEC_PER_SEC; LOCK_RTC(s); if (!rtclock_timebase_initialized) { commpage_set_timestamp(0,0,0,0); rtclock_timebase_const.numer = numer; rtclock_timebase_const.denom = denom; rtclock_sec_divisor = freq->timebase_num / freq->timebase_den; nanoseconds_to_absolutetime(NSEC_PER_HZ, &abstime); rtclock_tick_interval = abstime; ml_init_lock_timeout(); } else { UNLOCK_RTC(s); printf("rtclock timebase_callback: late old %d / %d new %d / %d", rtclock_timebase_const.numer, rtclock_timebase_const.denom, numer, denom); return; } UNLOCK_RTC(s); clock_timebase_init(); } /* * Configure the real-time clock device. */ int sysclk_config(void) { if (cpu_number() != master_cpu) return(1); timer_call_setup(&rtclock_alarm_timer, rtclock_alarm_expire, NULL); simple_lock_init(&rtclock_lock, ETAP_MISC_RT_CLOCK); PE_register_timebase_callback(timebase_callback); return (1); } /* * Initialize the system clock device. */ int sysclk_init(void) { uint64_t abstime; int decr, mycpu = cpu_number(); if (mycpu != master_cpu) { if (rtclock_initialized == FALSE) { panic("sysclk_init on cpu %d, rtc not initialized\n", mycpu); } /* Set decrementer and hence our next tick due */ abstime = mach_absolute_time(); rtclock_tick_deadline[mycpu] = abstime; rtclock_tick_deadline[mycpu] += rtclock_tick_interval; decr = deadline_to_decrementer(rtclock_tick_deadline[mycpu], abstime); treqs(decr); return(1); } /* Set decrementer and our next tick due */ abstime = mach_absolute_time(); rtclock_tick_deadline[mycpu] = abstime; rtclock_tick_deadline[mycpu] += rtclock_tick_interval; decr = deadline_to_decrementer(rtclock_tick_deadline[mycpu], abstime); treqs(decr); rtclock_initialized = TRUE; return (1); } kern_return_t sysclk_gettime( mach_timespec_t *time) /* OUT */ { uint64_t now, t64; uint32_t divisor; now = mach_absolute_time(); time->tv_sec = t64 = now / (divisor = rtclock_sec_divisor); now -= (t64 * divisor); time->tv_nsec = (now * NSEC_PER_SEC) / divisor; return (KERN_SUCCESS); } void clock_get_system_microtime( uint32_t *secs, uint32_t *microsecs) { uint64_t now, t64; uint32_t divisor; now = mach_absolute_time(); *secs = t64 = now / (divisor = rtclock_sec_divisor); now -= (t64 * divisor); *microsecs = (now * USEC_PER_SEC) / divisor; } void clock_get_system_nanotime( uint32_t *secs, uint32_t *nanosecs) { uint64_t now, t64; uint32_t divisor; now = mach_absolute_time(); *secs = t64 = now / (divisor = rtclock_sec_divisor); now -= (t64 * divisor); *nanosecs = (now * NSEC_PER_SEC) / divisor; } /* * Get clock device attributes. */ kern_return_t sysclk_getattr( clock_flavor_t flavor, clock_attr_t attr, /* OUT */ mach_msg_type_number_t *count) /* IN/OUT */ { spl_t s; if (*count != 1) return (KERN_FAILURE); switch (flavor) { case CLOCK_GET_TIME_RES: /* >0 res */ case CLOCK_ALARM_CURRES: /* =0 no alarm */ case CLOCK_ALARM_MINRES: case CLOCK_ALARM_MAXRES: LOCK_RTC(s); *(clock_res_t *) attr = NSEC_PER_HZ; UNLOCK_RTC(s); break; default: return (KERN_INVALID_VALUE); } return (KERN_SUCCESS); } /* * Set deadline for the next alarm on the clock device. This call * always resets the time to deliver an alarm for the clock. */ void sysclk_setalarm( mach_timespec_t *deadline) { uint64_t abstime; timespec_to_absolutetime(deadline, &abstime); timer_call_enter(&rtclock_alarm_timer, abstime); } /* * Configure the calendar clock. */ int calend_config(void) { return (1); } /* * Initialize the calendar clock. */ int calend_init(void) { if (cpu_number() != master_cpu) return(1); return (1); } /* * Get the current clock time. */ kern_return_t calend_gettime( mach_timespec_t *time) /* OUT */ { clock_get_calendar_nanotime( &time->tv_sec, &time->tv_nsec); return (KERN_SUCCESS); } /* * Get clock device attributes. */ kern_return_t calend_getattr( clock_flavor_t flavor, clock_attr_t attr, /* OUT */ mach_msg_type_number_t *count) /* IN/OUT */ { spl_t s; if (*count != 1) return (KERN_FAILURE); switch (flavor) { case CLOCK_GET_TIME_RES: /* >0 res */ LOCK_RTC(s); *(clock_res_t *) attr = NSEC_PER_HZ; UNLOCK_RTC(s); break; case CLOCK_ALARM_CURRES: /* =0 no alarm */ case CLOCK_ALARM_MINRES: case CLOCK_ALARM_MAXRES: *(clock_res_t *) attr = 0; break; default: return (KERN_INVALID_VALUE); } return (KERN_SUCCESS); } void clock_get_calendar_microtime( uint32_t *secs, uint32_t *microsecs) { uint32_t epoch, microepoch; uint64_t now, t64; spl_t s = splclock(); simple_lock(&rtclock_lock); if (rtclock_calend.adjdelta >= 0) { uint32_t divisor; now = mach_absolute_time(); epoch = rtclock_calend.epoch; microepoch = rtclock_calend.microepoch; simple_unlock(&rtclock_lock); *secs = t64 = now / (divisor = rtclock_sec_divisor); now -= (t64 * divisor); *microsecs = (now * USEC_PER_SEC) / divisor; if ((*microsecs += microepoch) >= USEC_PER_SEC) { *microsecs -= USEC_PER_SEC; epoch += 1; } *secs += epoch; } else { uint32_t delta, t32; delta = -rtclock_calend.adjdelta; now = mach_absolute_time(); *secs = rtclock_calend.epoch; *microsecs = rtclock_calend.microepoch; if (now > rtclock_calend.epoch1) { t64 = now - rtclock_calend.epoch1; t32 = (t64 * USEC_PER_SEC) / rtclock_sec_divisor; if (t32 > delta) *microsecs += (t32 - delta); if (*microsecs >= USEC_PER_SEC) { *microsecs -= USEC_PER_SEC; *secs += 1; } } simple_unlock(&rtclock_lock); } splx(s); } /* This is only called from the gettimeofday() syscall. As a side * effect, it updates the commpage timestamp. Otherwise it is * identical to clock_get_calendar_microtime(). Because most * gettimeofday() calls are handled by the commpage in user mode, * this routine should be infrequently used except when slowing down * the clock. */ void clock_gettimeofday( uint32_t *secs_p, uint32_t *microsecs_p) { uint32_t epoch, microepoch; uint32_t secs, microsecs; uint64_t now, t64, secs_64, usec_64; spl_t s = splclock(); simple_lock(&rtclock_lock); if (rtclock_calend.adjdelta >= 0) { now = mach_absolute_time(); epoch = rtclock_calend.epoch; microepoch = rtclock_calend.microepoch; secs = secs_64 = now / rtclock_sec_divisor; t64 = now - (secs_64 * rtclock_sec_divisor); microsecs = usec_64 = (t64 * USEC_PER_SEC) / rtclock_sec_divisor; if ((microsecs += microepoch) >= USEC_PER_SEC) { microsecs -= USEC_PER_SEC; epoch += 1; } secs += epoch; /* adjust "now" to be absolute time at _start_ of usecond */ now -= t64 - ((usec_64 * rtclock_sec_divisor) / USEC_PER_SEC); commpage_set_timestamp(now,secs,microsecs,rtclock_sec_divisor); } else { uint32_t delta, t32; delta = -rtclock_calend.adjdelta; now = mach_absolute_time(); secs = rtclock_calend.epoch; microsecs = rtclock_calend.microepoch; if (now > rtclock_calend.epoch1) { t64 = now - rtclock_calend.epoch1; t32 = (t64 * USEC_PER_SEC) / rtclock_sec_divisor; if (t32 > delta) microsecs += (t32 - delta); if (microsecs >= USEC_PER_SEC) { microsecs -= USEC_PER_SEC; secs += 1; } } /* no need to disable timestamp, it is already off */ } simple_unlock(&rtclock_lock); splx(s); *secs_p = secs; *microsecs_p = microsecs; } void clock_get_calendar_nanotime( uint32_t *secs, uint32_t *nanosecs) { uint32_t epoch, nanoepoch; uint64_t now, t64; spl_t s = splclock(); simple_lock(&rtclock_lock); if (rtclock_calend.adjdelta >= 0) { uint32_t divisor; now = mach_absolute_time(); epoch = rtclock_calend.epoch; nanoepoch = rtclock_calend.microepoch * NSEC_PER_USEC; simple_unlock(&rtclock_lock); *secs = t64 = now / (divisor = rtclock_sec_divisor); now -= (t64 * divisor); *nanosecs = ((now * USEC_PER_SEC) / divisor) * NSEC_PER_USEC; if ((*nanosecs += nanoepoch) >= NSEC_PER_SEC) { *nanosecs -= NSEC_PER_SEC; epoch += 1; } *secs += epoch; } else { uint32_t delta, t32; delta = -rtclock_calend.adjdelta; now = mach_absolute_time(); *secs = rtclock_calend.epoch; *nanosecs = rtclock_calend.microepoch * NSEC_PER_USEC; if (now > rtclock_calend.epoch1) { t64 = now - rtclock_calend.epoch1; t32 = (t64 * USEC_PER_SEC) / rtclock_sec_divisor; if (t32 > delta) *nanosecs += ((t32 - delta) * NSEC_PER_USEC); if (*nanosecs >= NSEC_PER_SEC) { *nanosecs -= NSEC_PER_SEC; *secs += 1; } } simple_unlock(&rtclock_lock); } splx(s); } void clock_set_calendar_microtime( uint32_t secs, uint32_t microsecs) { uint32_t sys, microsys; uint32_t newsecs; spl_t s; newsecs = (microsecs < 500*USEC_PER_SEC)? secs: secs + 1; LOCK_RTC(s); commpage_set_timestamp(0,0,0,0); clock_get_system_microtime(&sys, µsys); if ((int32_t)(microsecs -= microsys) < 0) { microsecs += USEC_PER_SEC; secs -= 1; } secs -= sys; rtclock_calend.epoch = secs; rtclock_calend.microepoch = microsecs; rtclock_calend.epoch1 = 0; rtclock_calend.adjdelta = rtclock_calend.adjtotal = 0; UNLOCK_RTC(s); PESetGMTTimeOfDay(newsecs); host_notify_calendar_change(); } #define tickadj (40) /* "standard" skew, us / tick */ #define bigadj (USEC_PER_SEC) /* use 10x skew above bigadj us */ uint32_t clock_set_calendar_adjtime( int32_t *secs, int32_t *microsecs) { int64_t total, ototal; uint32_t interval = 0; spl_t s; total = (int64_t)*secs * USEC_PER_SEC + *microsecs; LOCK_RTC(s); commpage_set_timestamp(0,0,0,0); ototal = rtclock_calend.adjtotal; if (rtclock_calend.adjdelta < 0) { uint64_t now, t64; uint32_t delta, t32; uint32_t sys, microsys; delta = -rtclock_calend.adjdelta; sys = rtclock_calend.epoch; microsys = rtclock_calend.microepoch; now = mach_absolute_time(); if (now > rtclock_calend.epoch1) t64 = now - rtclock_calend.epoch1; else t64 = 0; t32 = (t64 * USEC_PER_SEC) / rtclock_sec_divisor; if (t32 > delta) microsys += (t32 - delta); if (microsys >= USEC_PER_SEC) { microsys -= USEC_PER_SEC; sys += 1; } rtclock_calend.epoch = sys; rtclock_calend.microepoch = microsys; sys = t64 = now / rtclock_sec_divisor; now -= (t64 * rtclock_sec_divisor); microsys = (now * USEC_PER_SEC) / rtclock_sec_divisor; if ((int32_t)(rtclock_calend.microepoch -= microsys) < 0) { rtclock_calend.microepoch += USEC_PER_SEC; sys += 1; } rtclock_calend.epoch -= sys; } if (total != 0) { int32_t delta = tickadj; if (total > 0) { if (total > bigadj) delta *= 10; if (delta > total) delta = total; rtclock_calend.epoch1 = 0; } else { uint64_t now, t64; uint32_t sys, microsys; if (total < -bigadj) delta *= 10; delta = -delta; if (delta < total) delta = total; rtclock_calend.epoch1 = now = mach_absolute_time(); sys = t64 = now / rtclock_sec_divisor; now -= (t64 * rtclock_sec_divisor); microsys = (now * USEC_PER_SEC) / rtclock_sec_divisor; if ((rtclock_calend.microepoch += microsys) >= USEC_PER_SEC) { rtclock_calend.microepoch -= USEC_PER_SEC; sys += 1; } rtclock_calend.epoch += sys; } rtclock_calend.adjtotal = total; rtclock_calend.adjdelta = delta; interval = rtclock_tick_interval; } else { rtclock_calend.epoch1 = 0; rtclock_calend.adjdelta = rtclock_calend.adjtotal = 0; } UNLOCK_RTC(s); if (ototal == 0) *secs = *microsecs = 0; else { *secs = ototal / USEC_PER_SEC; *microsecs = ototal % USEC_PER_SEC; } return (interval); } uint32_t clock_adjust_calendar(void) { uint32_t micronew, interval = 0; int32_t delta; spl_t s; LOCK_RTC(s); commpage_set_timestamp(0,0,0,0); delta = rtclock_calend.adjdelta; if (delta > 0) { micronew = rtclock_calend.microepoch + delta; if (micronew >= USEC_PER_SEC) { micronew -= USEC_PER_SEC; rtclock_calend.epoch += 1; } rtclock_calend.microepoch = micronew; rtclock_calend.adjtotal -= delta; if (delta > rtclock_calend.adjtotal) rtclock_calend.adjdelta = rtclock_calend.adjtotal; } else if (delta < 0) { uint64_t now, t64; uint32_t t32; now = mach_absolute_time(); if (now > rtclock_calend.epoch1) t64 = now - rtclock_calend.epoch1; else t64 = 0; rtclock_calend.epoch1 = now; t32 = (t64 * USEC_PER_SEC) / rtclock_sec_divisor; micronew = rtclock_calend.microepoch + t32 + delta; if (micronew >= USEC_PER_SEC) { micronew -= USEC_PER_SEC; rtclock_calend.epoch += 1; } rtclock_calend.microepoch = micronew; rtclock_calend.adjtotal -= delta; if (delta < rtclock_calend.adjtotal) rtclock_calend.adjdelta = rtclock_calend.adjtotal; if (rtclock_calend.adjdelta == 0) { uint32_t sys, microsys; sys = t64 = now / rtclock_sec_divisor; now -= (t64 * rtclock_sec_divisor); microsys = (now * USEC_PER_SEC) / rtclock_sec_divisor; if ((int32_t)(rtclock_calend.microepoch -= microsys) < 0) { rtclock_calend.microepoch += USEC_PER_SEC; sys += 1; } rtclock_calend.epoch -= sys; rtclock_calend.epoch1 = 0; } } if (rtclock_calend.adjdelta != 0) interval = rtclock_tick_interval; UNLOCK_RTC(s); return (interval); } void clock_initialize_calendar(void) { uint32_t sys, microsys; uint32_t microsecs = 0, secs = PEGetGMTTimeOfDay(); spl_t s; LOCK_RTC(s); commpage_set_timestamp(0,0,0,0); clock_get_system_microtime(&sys, µsys); if ((int32_t)(microsecs -= microsys) < 0) { microsecs += USEC_PER_SEC; secs -= 1; } secs -= sys; rtclock_calend.epoch = secs; rtclock_calend.microepoch = microsecs; rtclock_calend.epoch1 = 0; rtclock_calend.adjdelta = rtclock_calend.adjtotal = 0; UNLOCK_RTC(s); host_notify_calendar_change(); } void clock_timebase_info( mach_timebase_info_t info) { spl_t s; LOCK_RTC(s); rtclock_timebase_initialized = TRUE; *info = rtclock_timebase_const; UNLOCK_RTC(s); } void clock_set_timer_deadline( uint64_t deadline) { uint64_t abstime; int decr, mycpu; struct rtclock_timer *mytimer; spl_t s; s = splclock(); mycpu = cpu_number(); mytimer = &rtclock_timer[mycpu]; mytimer->deadline = deadline; mytimer->is_set = TRUE; if (!mytimer->has_expired) { abstime = mach_absolute_time(); if ( mytimer->deadline < rtclock_tick_deadline[mycpu] ) { decr = deadline_to_decrementer(mytimer->deadline, abstime); if ( rtclock_decrementer_min != 0 && rtclock_decrementer_min < (natural_t)decr ) decr = rtclock_decrementer_min; treqs(decr); KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_EXCP_DECI, 1) | DBG_FUNC_NONE, decr, 2, 0, 0, 0); } } splx(s); } void clock_set_timer_func( clock_timer_func_t func) { spl_t s; LOCK_RTC(s); if (rtclock_timer_expire == NULL) rtclock_timer_expire = func; UNLOCK_RTC(s); } /* * Reset the clock device. This causes the realtime clock * device to reload its mode and count value (frequency). */ void rtclock_reset(void) { return; } /* * Real-time clock device interrupt. */ void rtclock_intr( int device, struct savearea *ssp, spl_t old_spl) { uint64_t abstime; int decr1, decr2, mycpu = cpu_number(); struct rtclock_timer *mytimer = &rtclock_timer[mycpu]; /* * We may receive interrupts too early, we must reject them. */ if (rtclock_initialized == FALSE) { treqs(DECREMENTER_MAX); /* Max the decrementer if not init */ return; } decr1 = decr2 = DECREMENTER_MAX; abstime = mach_absolute_time(); if ( rtclock_tick_deadline[mycpu] <= abstime ) { clock_deadline_for_periodic_event(rtclock_tick_interval, abstime, &rtclock_tick_deadline[mycpu]); hertz_tick(USER_MODE(ssp->save_srr1), ssp->save_srr0); } abstime = mach_absolute_time(); if ( mytimer->is_set && mytimer->deadline <= abstime ) { mytimer->has_expired = TRUE; mytimer->is_set = FALSE; (*rtclock_timer_expire)(abstime); mytimer->has_expired = FALSE; } abstime = mach_absolute_time(); decr1 = deadline_to_decrementer(rtclock_tick_deadline[mycpu], abstime); if (mytimer->is_set) decr2 = deadline_to_decrementer(mytimer->deadline, abstime); if (decr1 > decr2) decr1 = decr2; if ( rtclock_decrementer_min != 0 && rtclock_decrementer_min < (natural_t)decr1 ) decr1 = rtclock_decrementer_min; treqs(decr1); KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_EXCP_DECI, 1) | DBG_FUNC_NONE, decr1, 3, 0, 0, 0); } static void rtclock_alarm_expire( timer_call_param_t p0, timer_call_param_t p1) { mach_timespec_t timestamp; (void) sysclk_gettime(×tamp); clock_alarm_intr(SYSTEM_CLOCK, ×tamp); } static int deadline_to_decrementer( uint64_t deadline, uint64_t now) { uint64_t delt; if (deadline <= now) return DECREMENTER_MIN; else { delt = deadline - now; return (delt >= (DECREMENTER_MAX + 1))? DECREMENTER_MAX: ((delt >= (DECREMENTER_MIN + 1))? (delt - 1): DECREMENTER_MIN); } } static void timespec_to_absolutetime( mach_timespec_t *ts, uint64_t *result) { uint32_t divisor; *result = ((uint64_t)ts->tv_sec * (divisor = rtclock_sec_divisor)) + ((uint64_t)ts->tv_nsec * divisor) / NSEC_PER_SEC; } void clock_interval_to_deadline( uint32_t interval, uint32_t scale_factor, uint64_t *result) { uint64_t abstime; clock_get_uptime(result); clock_interval_to_absolutetime_interval(interval, scale_factor, &abstime); *result += abstime; } void clock_interval_to_absolutetime_interval( uint32_t interval, uint32_t scale_factor, uint64_t *result) { uint64_t nanosecs = (uint64_t)interval * scale_factor; uint64_t t64; uint32_t divisor; *result = (t64 = nanosecs / NSEC_PER_SEC) * (divisor = rtclock_sec_divisor); nanosecs -= (t64 * NSEC_PER_SEC); *result += (nanosecs * divisor) / NSEC_PER_SEC; } void clock_absolutetime_interval_to_deadline( uint64_t abstime, uint64_t *result) { clock_get_uptime(result); *result += abstime; } void absolutetime_to_nanoseconds( uint64_t abstime, uint64_t *result) { uint64_t t64; uint32_t divisor; *result = (t64 = abstime / (divisor = rtclock_sec_divisor)) * NSEC_PER_SEC; abstime -= (t64 * divisor); *result += (abstime * NSEC_PER_SEC) / divisor; } void nanoseconds_to_absolutetime( uint64_t nanosecs, uint64_t *result) { uint64_t t64; uint32_t divisor; *result = (t64 = nanosecs / NSEC_PER_SEC) * (divisor = rtclock_sec_divisor); nanosecs -= (t64 * NSEC_PER_SEC); *result += (nanosecs * divisor) / NSEC_PER_SEC; } /* * Spin-loop delay primitives. */ void delay_for_interval( uint32_t interval, uint32_t scale_factor) { uint64_t now, end; clock_interval_to_deadline(interval, scale_factor, &end); do { now = mach_absolute_time(); } while (now < end); } void clock_delay_until( uint64_t deadline) { uint64_t now; do { now = mach_absolute_time(); } while (now < deadline); } void delay( int usec) { delay_for_interval((usec < 0)? -usec: usec, NSEC_PER_USEC); } /* * Request a decrementer pop * */ void treqs(uint32_t dec) { struct per_proc_info *pp; uint64_t nowtime, newtime; nowtime = mach_absolute_time(); /* What time is it? */ pp = getPerProc(); /* Get our processor block */ newtime = nowtime + (uint64_t)dec; /* Get requested pop time */ pp->rtcPop = newtime; /* Copy it */ mtdec((uint32_t)(newtime - nowtime)); /* Set decrementer */ return; }