1/* 2 * Copyright (c) 2000-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * @OSF_COPYRIGHT@ 30 */ 31 32#include <platforms.h> 33 34#include <mach/mach_types.h> 35 36#include <architecture/i386/pio.h> 37#include <i386/machine_cpu.h> 38#include <i386/cpuid.h> 39#include <i386/cpu_threads.h> 40#include <i386/mp.h> 41#include <i386/machine_routines.h> 42#include <i386/pal_routines.h> 43#include <i386/proc_reg.h> 44#include <i386/misc_protos.h> 45#include <i386/lapic.h> 46#include <pexpert/pexpert.h> 47#include <machine/limits.h> 48#include <sys/kdebug.h> 49#include <i386/tsc.h> 50#include <i386/rtclock_protos.h> 51#include <i386/pal_routines.h> 52#include <kern/etimer.h> 53 54static uint64_t rtc_decrementer_min; 55static uint64_t rtc_decrementer_max; 56 57static uint64_t 58deadline_to_decrementer( 59 uint64_t deadline, 60 uint64_t now) 61{ 62 uint64_t delta; 63 64 if (deadline <= now) 65 return rtc_decrementer_min; 66 else { 67 delta = deadline - now; 68 return MIN(MAX(rtc_decrementer_min,delta),rtc_decrementer_max); 69 } 70} 71 72 73/* 74 * Regular local APIC timer case: 75 */ 76static void 77rtc_lapic_config_timer(void) 78{ 79 lapic_config_timer(TRUE, one_shot, divide_by_1); 80} 81static uint64_t 82rtc_lapic_set_timer(uint64_t deadline, uint64_t now) 83{ 84 uint64_t count; 85 uint64_t set = 0; 86 87 if (deadline > 0) { 88 /* 89 * Convert delta to bus ticks 90 * - time now is not relevant 91 */ 92 count = deadline_to_decrementer(deadline, now); 93 set = now + count; 94 lapic_set_timer_fast((uint32_t) tmrCvt(count, busFCvtn2t)); 95 } else { 96 lapic_set_timer(FALSE, one_shot, divide_by_1, 0); 97 } 98 return set; 99} 100 101/* 102 * TSC-deadline timer case: 103 */ 104static void 105rtc_lapic_config_tsc_deadline_timer(void) 106{ 107 lapic_config_tsc_deadline_timer(); 108} 109static uint64_t 110rtc_lapic_set_tsc_deadline_timer(uint64_t deadline, uint64_t now) 111{ 112 uint64_t delta; 113 uint64_t delta_tsc; 114 uint64_t tsc = rdtsc64(); 115 uint64_t set = 0; 116 117 if (deadline > 0) { 118 /* 119 * Convert to TSC 120 */ 121 delta = deadline_to_decrementer(deadline, now); 122 set = now + delta; 123 delta_tsc = tmrCvt(delta, tscFCvtn2t); 124 lapic_set_tsc_deadline_timer(tsc + delta_tsc); 125 } else { 126 lapic_set_tsc_deadline_timer(0); 127 } 128 129 KERNEL_DEBUG_CONSTANT( 130 DECR_SET_TSC_DEADLINE | DBG_FUNC_NONE, 131 now, deadline, 132 tsc, lapic_get_tsc_deadline_timer(), 133 0); 134 135 return set; 136} 137 138/* 139 * Definitions for timer operations table 140 */ 141 142rtc_timer_t rtc_timer_lapic = { 143 rtc_lapic_config_timer, 144 rtc_lapic_set_timer 145}; 146 147rtc_timer_t rtc_timer_tsc_deadline = { 148 rtc_lapic_config_tsc_deadline_timer, 149 rtc_lapic_set_tsc_deadline_timer 150}; 151 152rtc_timer_t *rtc_timer = &rtc_timer_lapic; /* defaults to LAPIC timer */ 153 154/* 155 * rtc_timer_init() is called at startup on the boot processor only. 156 */ 157void 158rtc_timer_init(void) 159{ 160 int TSC_deadline_timer = 0; 161 162 /* See whether we can use the local apic in TSC-deadline mode */ 163 if ((cpuid_features() & CPUID_FEATURE_TSCTMR)) { 164 TSC_deadline_timer = 1; 165 PE_parse_boot_argn("TSC_deadline_timer", &TSC_deadline_timer, 166 sizeof(TSC_deadline_timer)); 167 printf("TSC Deadline Timer supported %s enabled\n", 168 TSC_deadline_timer ? "and" : "but not"); 169 } 170 171 if (TSC_deadline_timer) { 172 rtc_timer = &rtc_timer_tsc_deadline; 173 rtc_decrementer_max = UINT64_MAX; /* effectively none */ 174 /* 175 * The min could be as low as 1nsec, 176 * but we're being conservative for now and making it the same 177 * as for the local apic timer. 178 */ 179 rtc_decrementer_min = 1*NSEC_PER_USEC; /* 1 usec */ 180 } else { 181 /* 182 * Compute the longest interval using LAPIC timer. 183 */ 184 rtc_decrementer_max = tmrCvt(0x7fffffffULL, busFCvtt2n); 185 kprintf("maxDec: %lld\n", rtc_decrementer_max); 186 rtc_decrementer_min = 1*NSEC_PER_USEC; /* 1 usec */ 187 } 188 189 /* Point LAPIC interrupts to hardclock() */ 190 lapic_set_timer_func((i386_intr_func_t) rtclock_intr); 191} 192