/* * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ */ /* * File: i386/tsc.c * Purpose: Initializes the TSC and the various conversion * factors needed by other parts of the system. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for kernel_map */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include uint64_t busFCvtt2n = 0; uint64_t busFCvtn2t = 0; uint64_t tscFreq = 0; uint64_t tscFCvtt2n = 0; uint64_t tscFCvtn2t = 0; uint64_t tscGranularity = 0; uint64_t bus2tsc = 0; uint64_t busFreq = 0; uint32_t flex_ratio = 0; uint32_t flex_ratio_min = 0; uint32_t flex_ratio_max = 0; #define bit(n) (1ULL << (n)) #define bitmask(h,l) ((bit(h)|(bit(h)-1)) & ~(bit(l)-1)) #define bitfield(x,h,l) (((x) & bitmask(h,l)) >> l) /* Decimal powers: */ #define kilo (1000ULL) #define Mega (kilo * kilo) #define Giga (kilo * Mega) #define Tera (kilo * Giga) #define Peta (kilo * Tera) #define CPU_FAMILY_PENTIUM_M (0x6) static const char FSB_Frequency_prop[] = "FSBFrequency"; /* * This routine extracts the bus frequency in Hz from the device tree. */ static uint64_t EFI_FSB_frequency(void) { uint64_t frequency = 0; DTEntry entry; void *value; unsigned int size; if (DTLookupEntry(0, "/efi/platform", &entry) != kSuccess) { kprintf("EFI_FSB_frequency: didn't find /efi/platform\n"); return 0; } if (DTGetProperty(entry,FSB_Frequency_prop,&value,&size) != kSuccess) { kprintf("EFI_FSB_frequency: property %s not found\n", FSB_Frequency_prop); return 0; } if (size == sizeof(uint64_t)) { frequency = *(uint64_t *) value; kprintf("EFI_FSB_frequency: read %s value: %llu\n", FSB_Frequency_prop, frequency); if (!(90*Mega < frequency && frequency < 10*Giga)) { kprintf("EFI_FSB_frequency: value out of range\n"); frequency = 0; } } else { kprintf("EFI_FSB_frequency: unexpected size %d\n", size); } return frequency; } /* * Initialize the various conversion factors needed by code referencing * the TSC. */ void tsc_init(void) { uint64_t busFCvtInt = 0; boolean_t N_by_2_bus_ratio = FALSE; /* * Get the FSB frequency and conversion factors from EFI. */ busFreq = EFI_FSB_frequency(); if (cpuid_info()->cpuid_family != CPU_FAMILY_PENTIUM_M) { panic("tsc_init: unknown CPU family: 0x%X\n", cpuid_info()->cpuid_family); } switch (cpuid_info()->cpuid_model) { case CPUID_MODEL_NEHALEM: { uint64_t cpu_mhz; uint64_t msr_flex_ratio; uint64_t msr_platform_info; /* See if FLEX_RATIO is being used */ msr_flex_ratio = rdmsr64(MSR_FLEX_RATIO); msr_platform_info = rdmsr64(MSR_PLATFORM_INFO); flex_ratio_min = (uint32_t)bitfield(msr_platform_info, 47, 40); flex_ratio_max = (uint32_t)bitfield(msr_platform_info, 15, 8); /* No BIOS-programed flex ratio. Use hardware max as default */ tscGranularity = flex_ratio_max; if (msr_flex_ratio & bit(16)) { /* Flex Enabled: Use this MSR if less than max */ flex_ratio = (uint32_t)bitfield(msr_flex_ratio, 15, 8); if (flex_ratio < flex_ratio_max) tscGranularity = flex_ratio; } /* If EFI isn't configured correctly, use a constant * value. See 6036811. */ if (busFreq == 0) busFreq = BASE_NHM_CLOCK_SOURCE; cpu_mhz = tscGranularity * BASE_NHM_CLOCK_SOURCE; kprintf("[NHM] Maximum Non-Turbo Ratio = [%d]\n", (uint32_t)tscGranularity); kprintf("[NHM] CPU: Frequency = %6d.%04dMhz\n", (uint32_t)(cpu_mhz / Mega), (uint32_t)(cpu_mhz % Mega)); break; } default: { uint64_t prfsts; prfsts = rdmsr64(IA32_PERF_STS); tscGranularity = (uint32_t)bitfield(prfsts, 44, 40); N_by_2_bus_ratio = (prfsts & bit(46)) != 0; } } if (busFreq != 0) { busFCvtt2n = ((1 * Giga) << 32) / busFreq; busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; busFCvtInt = tmrCvt(1 * Peta, 0xFFFFFFFFFFFFFFFFULL / busFreq); } else { panic("tsc_init: EFI not supported!\n"); } kprintf(" BUS: Frequency = %6d.%04dMHz, " "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, " "cvtInt = %08X.%08X\n", (uint32_t)(busFreq / Mega), (uint32_t)(busFreq % Mega), (uint32_t)(busFCvtt2n >> 32), (uint32_t)busFCvtt2n, (uint32_t)(busFCvtn2t >> 32), (uint32_t)busFCvtn2t, (uint32_t)(busFCvtInt >> 32), (uint32_t)busFCvtInt); /* * Get the TSC increment. The TSC is incremented by this * on every bus tick. Calculate the TSC conversion factors * to and from nano-seconds. * The tsc granularity is also called the "bus ratio". If the N/2 bit * is set this indicates the bus ration is 0.5 more than this - i.e. * that the true bus ratio is (2*tscGranularity + 1)/2. */ if (N_by_2_bus_ratio) tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity); else tscFCvtt2n = busFCvtt2n / tscGranularity; tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; kprintf(" TSC: Frequency = %6d.%04dMHz, " "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %lld%s\n", (uint32_t)(tscFreq / Mega), (uint32_t)(tscFreq % Mega), (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t, tscGranularity, N_by_2_bus_ratio ? " (N/2)" : ""); /* * Calculate conversion from BUS to TSC */ bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); } void tsc_get_info(tscInfo_t *info) { info->busFCvtt2n = busFCvtt2n; info->busFCvtn2t = busFCvtn2t; info->tscFreq = tscFreq; info->tscFCvtt2n = tscFCvtt2n; info->tscFCvtn2t = tscFCvtn2t; info->tscGranularity = tscGranularity; info->bus2tsc = bus2tsc; info->busFreq = busFreq; info->flex_ratio = flex_ratio; info->flex_ratio_min = flex_ratio_min; info->flex_ratio_max = flex_ratio_max; }