+
+ /*
+ * Set the new value for the platform clock.
+ */
+ PESetGMTTimeOfDay(newsecs);
+
+ splx(s);
+
+ /*
+ * Send host notifications.
+ */
+ 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)
+ TIME_ADD(sys, 0, microsys, (t32 - delta), USEC_PER_SEC);
+
+ 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;
+
+ TIME_SUB(rtclock_calend.epoch, sys, rtclock_calend.microepoch, microsys, USEC_PER_SEC);
+ }
+
+ 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;
+
+ TIME_ADD(rtclock_calend.epoch, sys, rtclock_calend.microepoch, microsys, USEC_PER_SEC);
+ }
+
+ 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 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) {
+ TIME_ADD(rtclock_calend.epoch, 0, rtclock_calend.microepoch, delta, USEC_PER_SEC);
+
+ 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;
+
+ TIME_ADD(rtclock_calend.epoch, 0, rtclock_calend.microepoch, (t32 + delta), USEC_PER_SEC);
+
+ 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;
+
+ TIME_SUB(rtclock_calend.epoch, sys, rtclock_calend.microepoch, microsys, USEC_PER_SEC);
+
+ rtclock_calend.epoch1 = 0;
+ }
+ }
+
+ if (rtclock_calend.adjdelta != 0)
+ interval = rtclock_tick_interval;
+
+ UNLOCK_RTC(s);
+
+ return (interval);
+}
+
+/*
+ * clock_initialize_calendar:
+ *
+ * Set the calendar and related clocks
+ * from the platform clock at boot or
+ * wake event.
+ */
+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);
+
+ if ((int32_t)secs >= (int32_t)rtclock_boottime) {
+ /*
+ * Initialize the boot time based on the platform clock.
+ */
+ if (rtclock_boottime == 0)
+ rtclock_boottime = secs;
+
+ /*
+ * Calculate the new calendar epoch based
+ * on the platform clock and the system
+ * clock.
+ */
+ clock_get_system_microtime(&sys, µsys);
+ TIME_SUB(secs, sys, microsecs, microsys, USEC_PER_SEC);
+
+ /*
+ * Set the new calendar epoch.
+ */
+ rtclock_calend.epoch = secs;
+ rtclock_calend.microepoch = microsecs;
+
+ /*
+ * Cancel any adjustment in progress.
+ */
+ rtclock_calend.epoch1 = 0;
+ rtclock_calend.adjdelta = rtclock_calend.adjtotal = 0;
+ }
+
+ UNLOCK_RTC(s);
+
+ /*
+ * Send host notifications.
+ */
+ host_notify_calendar_change();
+}
+
+void
+clock_get_boottime_nanotime(
+ uint32_t *secs,
+ uint32_t *nanosecs)
+{
+ *secs = rtclock_boottime;
+ *nanosecs = 0;