1/* 2 * Copyright 2013, winocm. <winocm@icloud.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright notice, this 12 * list of conditions and the following disclaimer in the documentation and/or 13 * other materials provided with the distribution. 14 * 15 * If you are going to use this software in any form that does not involve 16 * releasing the source to this project or improving it, let me know beforehand. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30/* 31 * ARM realtime clock. 32 */ 33 34#include <platforms.h> 35 36#include <mach/mach_types.h> 37 38#include <kern/cpu_data.h> 39#include <kern/cpu_number.h> 40#include <kern/clock.h> 41#include <kern/host_notify.h> 42#include <kern/macro_help.h> 43#include <kern/misc_protos.h> 44#include <kern/spl.h> 45#include <kern/assert.h> 46#include <kern/etimer.h> 47#include <mach/vm_prot.h> 48#include <vm/pmap.h> 49#include <vm/vm_kern.h> /* for kernel_map */ 50#include <pexpert/pexpert.h> 51#include <machine/limits.h> 52#include <machine/commpage.h> 53#include <sys/kdebug.h> 54 55#include <arm/machine_cpu.h> 56 57#include <pexpert/pexpert.h> 58#include <pexpert/arm/boot.h> 59#include <pexpert/arm/protos.h> 60 61#include <libkern/OSBase.h> 62 63static uint32_t rtclock_sec_divisor; 64static uint64_t rtclock_scaler = 0; 65 66static uint64_t rtc_decrementer_min; 67static uint64_t rtc_decrementer_max; 68 69extern uint64_t clock_decrementer; 70 71#define UI_CPUFREQ_ROUNDING_FACTOR 10000000 72 73/* Decimal powers: */ 74#define kilo (1000ULL) 75#define Mega (kilo * kilo) 76#define Giga (kilo * Mega) 77#define Tera (kilo * Giga) 78#define Peta (kilo * Tera) 79 80uint64_t tscFreq = 0, tscFCvtt2n, tscFCvtn2t; 81 82uint64_t rtclock_timer_scale_convert(void) 83{ 84 uint64_t ticks; 85 uint64_t nsecs = pe_arm_get_timebase(NULL); 86 87 /* nanoseconds = (((rdtsc - rnt_tsc_base) * 10e9) / tscFreq) - rnt_ns_base; */ 88 if(!tscFreq) 89 return 0; 90 91 ticks = (nsecs * NSEC_PER_SEC / tscFreq); 92 return ticks; 93} 94 95static uint64_t deadline_to_decrementer(uint64_t deadline, uint64_t now) 96{ 97 uint64_t delta; 98 if (deadline <= now) 99 return rtc_decrementer_min; 100 else { 101 delta = deadline - now; 102 return MIN(MAX(rtc_decrementer_min,delta),rtc_decrementer_max); 103 } 104} 105 106/* 107 * The units are a 64-bit fixed point integer which contains a high 32-bit integer part 108 * and a low 32-bit fractional part. 109 */ 110uint64_t tmrCvt(uint64_t time, uint64_t unit) 111{ 112 return ((time * unit) >> 32); 113} 114 115static uint64_t rtclock_internal_arm_timer(uint64_t deadline, uint64_t now) 116{ 117 uint64_t current = pe_arm_get_timebase(NULL); 118 uint64_t set = 0; 119 120 if(deadline) { 121 uint64_t delta = deadline_to_decrementer(deadline, now); 122 uint64_t delta_abs; 123 124 set = now + delta; 125 126 /* Convert to Absolute frequency. */ 127 delta_abs = tmrCvt(delta, tscFCvtn2t); 128 129#if CONFIG_TICKLESS 130 /* Set internal decrementer and rearm. */ 131 clock_decrementer = delta_abs; 132#endif 133 134 } else { 135#if CONFIG_TICKLESS 136 printf("disarming timer...\n"); 137 clock_decrementer = EndOfAllTime; 138#endif 139 } 140 141#if CONFIG_TICKLESS 142 /* Rearm decrementer by disable-reenable. This resets the timer. */ 143 pe_arm_set_timer_enabled(FALSE); 144 pe_arm_set_timer_enabled(TRUE); 145#endif 146 147 return set; 148} 149 150int setPop(uint64_t time) 151{ 152 uint64_t now; 153 uint64_t pop; 154 155 /* 156 * 0 and EndOfAllTime are special-cases for "clear the timer" 157 */ 158 if (time == 0 || time == EndOfAllTime) { 159 time = EndOfAllTime; 160 now = 0; 161 pop = rtclock_internal_arm_timer(0, 0); 162 } else { 163 now = mach_absolute_time(); /* The time in nanoseconds */ 164 pop = rtclock_internal_arm_timer(time, now); 165 } 166 167 /* 168 * Record requested and actual deadlines set 169 */ 170 current_cpu_datap()->rtcPop = pop; 171 172 return (pop - now); 173} 174 175void rtclock_intr(arm_saved_state_t * regs) 176{ 177 /* 178 * Interrupts must be enabled. 179 */ 180 181 spl_t x = splclock(); 182 etimer_intr(0, 0); 183 splx(x); 184 185 return; 186} 187 188uint64_t mach_absolute_time(void) 189{ 190 if (!rtclock_sec_divisor) 191 return 0; 192 return rtclock_timer_scale_convert(); 193} 194 195void machine_delay_until(uint64_t interval, uint64_t deadline) 196{ 197 uint64_t now; 198 199 do { 200 cpu_pause(); 201 now = mach_absolute_time(); 202 } while (now < deadline); 203} 204 205static inline uint32_t 206_absolutetime_to_microtime(uint64_t abstime, clock_sec_t *secs, clock_usec_t *microsecs) 207{ 208 uint32_t remain; 209 *secs = abstime / (uint64_t)NSEC_PER_SEC; 210 remain = (uint32_t)(abstime % (uint64_t)NSEC_PER_SEC); 211 *microsecs = remain / NSEC_PER_USEC; 212 return remain; 213} 214 215static inline void 216_absolutetime_to_nanotime(uint64_t abstime, clock_sec_t *secs, clock_usec_t *nanosecs) 217{ 218 *secs = abstime / (uint64_t)NSEC_PER_SEC; 219 *nanosecs = (clock_usec_t)(abstime % (uint64_t)NSEC_PER_SEC); 220} 221 222void 223clock_interval_to_absolutetime_interval(uint32_t interval, uint32_t scale_factor, uint64_t* result) 224{ 225 *result = (uint64_t)interval * scale_factor; 226} 227 228void 229absolutetime_to_microtime(uint64_t abstime, clock_sec_t *secs, clock_usec_t *microsecs) 230{ 231 _absolutetime_to_microtime(abstime, secs, microsecs); 232} 233 234void 235absolutetime_to_nanotime(uint64_t abstime, clock_sec_t* secs, clock_nsec_t *nanosecs) 236{ 237 _absolutetime_to_nanotime(abstime, secs, nanosecs); 238} 239 240void 241nanotime_to_absolutetime(clock_sec_t secs, clock_nsec_t nanosecs, uint64_t* result) 242{ 243 *result = ((uint64_t)secs * NSEC_PER_SEC) + nanosecs; 244} 245 246void 247absolutetime_to_nanoseconds(uint64_t abstime, uint64_t* result) 248{ 249 *result = abstime; 250} 251 252void 253nanoseconds_to_absolutetime(uint64_t nanoseconds, uint64_t* result) 254{ 255 *result = nanoseconds; 256} 257 258void clock_get_system_nanotime(clock_sec_t * secs, clock_nsec_t * nanosecs) 259{ 260 uint64_t now = pe_arm_get_timebase(NULL); 261 262 _absolutetime_to_nanotime(now, secs, nanosecs); 263} 264 265void clock_get_system_microtime(clock_sec_t * secs, clock_usec_t * microsecs) 266{ 267 uint64_t now = mach_absolute_time(); 268 _absolutetime_to_microtime(now, secs, microsecs); 269} 270 271int rtclock_config(void) 272{ 273 return (1); 274} 275 276void rtc_configure(uint64_t hz) 277{ 278 rtclock_sec_divisor = hz; 279 return; 280} 281 282void rtclock_bus_configure(void) 283{ 284 uint64_t cycles; 285 kprintf("rtclock_bus_configure: Configuring RTCLOCK...\n"); 286 287 /* We need a Hz, this will be specified in ...Hz. */ 288 if(!gPEClockFrequencyInfo.timebase_frequency_hz) 289 panic("rtclock_bus_configure"); 290 291 if(!rtclock_sec_divisor) 292 panic("rtclock_bus_configure 2"); 293 294 cycles = gPEClockFrequencyInfo.timebase_frequency_hz; 295 296 /* Calculate 'TSC' frequency. */ 297 tscFreq = cycles; 298 tscFCvtt2n = ((1 * Giga) << 32) / tscFreq; 299 tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; 300 301 kprintf("[RTCLOCK] Frequency = %6d.%06dMHz, " 302 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n", 303 (uint32_t)(tscFreq / Mega), 304 (uint32_t)(tscFreq % Mega), 305 (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, 306 (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t); 307 308 rtc_decrementer_max = UINT64_MAX; /* No limit. */ 309 rtc_decrementer_min = 1*NSEC_PER_USEC; /* 1 usec */ 310} 311 312int rtclock_init(void) 313{ 314 uint64_t cycles; 315 assert(!ml_get_interrupts_enabled()); 316 317 if(cpu_number() == master_cpu) { 318 pe_arm_init_timebase(NULL); 319 rtclock_bus_configure(); 320 321 current_cpu_datap()->rtcPop = 0; 322 clock_timebase_init(); 323 } 324 325 ml_init_lock_timeout(); 326 327 return 1; 328} 329 330void clock_timebase_info(mach_timebase_info_t info) 331{ 332 info->numer = info->denom = 1; 333} 334 335 336void clock_gettimeofday_set_commpage(uint64_t abstime, uint64_t epoch, 337 uint64_t offset, clock_sec_t * secs, 338 clock_usec_t * microsecs) 339{ 340 uint64_t now = abstime + offset; 341 uint32_t remain; 342 343 remain = _absolutetime_to_microtime(now, secs, microsecs); 344 *secs += (clock_sec_t)epoch; 345 commpage_set_timestamp(abstime - remain, *secs, 0); 346} 347 348 349