1/* 2 * Copyright (c) 2005-2007 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/* 33 * File: i386/tsc.c 34 * Purpose: Initializes the TSC and the various conversion 35 * factors needed by other parts of the system. 36 */ 37 38#include <platforms.h> 39#include <mach_kdb.h> 40 41#include <mach/mach_types.h> 42 43#include <kern/cpu_data.h> 44#include <kern/cpu_number.h> 45#include <kern/clock.h> 46#include <kern/host_notify.h> 47#include <kern/macro_help.h> 48#include <kern/misc_protos.h> 49#include <kern/spl.h> 50#include <kern/assert.h> 51#include <mach/vm_prot.h> 52#include <vm/pmap.h> 53#include <vm/vm_kern.h> /* for kernel_map */ 54#include <i386/ipl.h> 55#include <architecture/i386/pio.h> 56#include <i386/misc_protos.h> 57#include <i386/proc_reg.h> 58#include <i386/machine_cpu.h> 59#include <i386/mp.h> 60#include <i386/cpu_data.h> 61#include <i386/cpuid.h> 62#include <i386/machine_routines.h> 63#include <pexpert/pexpert.h> 64#include <machine/limits.h> 65#include <machine/commpage.h> 66#include <sys/kdebug.h> 67#include <pexpert/device_tree.h> 68#include <i386/tsc.h> 69 70uint64_t busFCvtt2n = 0; 71uint64_t busFCvtn2t = 0; 72uint64_t tscFreq = 0; 73uint64_t tscFCvtt2n = 0; 74uint64_t tscFCvtn2t = 0; 75uint64_t tscGranularity = 0; 76uint64_t bus2tsc = 0; 77uint64_t busFreq = 0; 78uint32_t flex_ratio = 0; 79uint32_t flex_ratio_min = 0; 80uint32_t flex_ratio_max = 0; 81 82 83#define bit(n) (1ULL << (n)) 84#define bitmask(h,l) ((bit(h)|(bit(h)-1)) & ~(bit(l)-1)) 85#define bitfield(x,h,l) (((x) & bitmask(h,l)) >> l) 86 87/* Decimal powers: */ 88#define kilo (1000ULL) 89#define Mega (kilo * kilo) 90#define Giga (kilo * Mega) 91#define Tera (kilo * Giga) 92#define Peta (kilo * Tera) 93 94#define CPU_FAMILY_PENTIUM_M (0x6) 95 96static const char FSB_Frequency_prop[] = "FSBFrequency"; 97/* 98 * This routine extracts the bus frequency in Hz from the device tree. 99 */ 100static uint64_t 101EFI_FSB_frequency(void) 102{ 103 uint64_t frequency = 0; 104 DTEntry entry; 105 void *value; 106 unsigned int size; 107 108 if (DTLookupEntry(0, "/efi/platform", &entry) != kSuccess) { 109 kprintf("EFI_FSB_frequency: didn't find /efi/platform\n"); 110 return 0; 111 } 112 if (DTGetProperty(entry,FSB_Frequency_prop,&value,&size) != kSuccess) { 113 kprintf("EFI_FSB_frequency: property %s not found\n", 114 FSB_Frequency_prop); 115 return 0; 116 } 117 if (size == sizeof(uint64_t)) { 118 frequency = *(uint64_t *) value; 119 kprintf("EFI_FSB_frequency: read %s value: %llu\n", 120 FSB_Frequency_prop, frequency); 121 if (!(90*Mega < frequency && frequency < 10*Giga)) { 122 kprintf("EFI_FSB_frequency: value out of range\n"); 123 frequency = 0; 124 } 125 } else { 126 kprintf("EFI_FSB_frequency: unexpected size %d\n", size); 127 } 128 return frequency; 129} 130 131/* 132 * Initialize the various conversion factors needed by code referencing 133 * the TSC. 134 */ 135void 136tsc_init(void) 137{ 138 uint64_t busFCvtInt = 0; 139 boolean_t N_by_2_bus_ratio = FALSE; 140 141 /* 142 * Get the FSB frequency and conversion factors from EFI. 143 */ 144 busFreq = EFI_FSB_frequency(); 145 146 if (cpuid_info()->cpuid_family != CPU_FAMILY_PENTIUM_M) { 147 panic("tsc_init: unknown CPU family: 0x%X\n", 148 cpuid_info()->cpuid_family); 149 } 150 151 switch (cpuid_info()->cpuid_model) { 152 case CPUID_MODEL_NEHALEM: { 153 uint64_t cpu_mhz; 154 uint64_t msr_flex_ratio; 155 uint64_t msr_platform_info; 156 157 /* See if FLEX_RATIO is being used */ 158 msr_flex_ratio = rdmsr64(MSR_FLEX_RATIO); 159 msr_platform_info = rdmsr64(MSR_PLATFORM_INFO); 160 flex_ratio_min = (uint32_t)bitfield(msr_platform_info, 47, 40); 161 flex_ratio_max = (uint32_t)bitfield(msr_platform_info, 15, 8); 162 /* No BIOS-programed flex ratio. Use hardware max as default */ 163 tscGranularity = flex_ratio_max; 164 if (msr_flex_ratio & bit(16)) { 165 /* Flex Enabled: Use this MSR if less than max */ 166 flex_ratio = (uint32_t)bitfield(msr_flex_ratio, 15, 8); 167 if (flex_ratio < flex_ratio_max) 168 tscGranularity = flex_ratio; 169 } 170 171 /* If EFI isn't configured correctly, use a constant 172 * value. See 6036811. 173 */ 174 if (busFreq == 0) 175 busFreq = BASE_NHM_CLOCK_SOURCE; 176 177 cpu_mhz = tscGranularity * BASE_NHM_CLOCK_SOURCE; 178 179 kprintf("[NHM] Maximum Non-Turbo Ratio = [%d]\n", 180 (uint32_t)tscGranularity); 181 kprintf("[NHM] CPU: Frequency = %6d.%04dMhz\n", 182 (uint32_t)(cpu_mhz / Mega), (uint32_t)(cpu_mhz % Mega)); 183 break; 184 } 185 default: { 186 uint64_t prfsts; 187 188 prfsts = rdmsr64(IA32_PERF_STS); 189 tscGranularity = (uint32_t)bitfield(prfsts, 44, 40); 190 N_by_2_bus_ratio = (prfsts & bit(46)) != 0; 191 } 192 } 193 194 if (busFreq != 0) { 195 busFCvtt2n = ((1 * Giga) << 32) / busFreq; 196 busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; 197 busFCvtInt = tmrCvt(1 * Peta, 0xFFFFFFFFFFFFFFFFULL / busFreq); 198 } else { 199 panic("tsc_init: EFI not supported!\n"); 200 } 201 202 kprintf(" BUS: Frequency = %6d.%04dMHz, " 203 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, " 204 "cvtInt = %08X.%08X\n", 205 (uint32_t)(busFreq / Mega), 206 (uint32_t)(busFreq % Mega), 207 (uint32_t)(busFCvtt2n >> 32), (uint32_t)busFCvtt2n, 208 (uint32_t)(busFCvtn2t >> 32), (uint32_t)busFCvtn2t, 209 (uint32_t)(busFCvtInt >> 32), (uint32_t)busFCvtInt); 210 211 /* 212 * Get the TSC increment. The TSC is incremented by this 213 * on every bus tick. Calculate the TSC conversion factors 214 * to and from nano-seconds. 215 * The tsc granularity is also called the "bus ratio". If the N/2 bit 216 * is set this indicates the bus ration is 0.5 more than this - i.e. 217 * that the true bus ratio is (2*tscGranularity + 1)/2. 218 */ 219 if (N_by_2_bus_ratio) 220 tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity); 221 else 222 tscFCvtt2n = busFCvtt2n / tscGranularity; 223 224 tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; 225 tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; 226 227 kprintf(" TSC: Frequency = %6d.%04dMHz, " 228 "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %lld%s\n", 229 (uint32_t)(tscFreq / Mega), 230 (uint32_t)(tscFreq % Mega), 231 (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, 232 (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t, 233 tscGranularity, N_by_2_bus_ratio ? " (N/2)" : ""); 234 235 /* 236 * Calculate conversion from BUS to TSC 237 */ 238 bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); 239} 240 241void 242tsc_get_info(tscInfo_t *info) 243{ 244 info->busFCvtt2n = busFCvtt2n; 245 info->busFCvtn2t = busFCvtn2t; 246 info->tscFreq = tscFreq; 247 info->tscFCvtt2n = tscFCvtt2n; 248 info->tscFCvtn2t = tscFCvtn2t; 249 info->tscGranularity = tscGranularity; 250 info->bus2tsc = bus2tsc; 251 info->busFreq = busFreq; 252 info->flex_ratio = flex_ratio; 253 info->flex_ratio_min = flex_ratio_min; 254 info->flex_ratio_max = flex_ratio_max; 255} 256