1/* 2 * Copyright 2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Clemens Zeidler, haiku@clemens-zeidler.de 7 */ 8 9 10#include "frequency.h" 11 12#include <arch/x86/arch_cpu.h> 13 14#include <stdio.h> 15#include <stdlib.h> 16#include <string.h> 17 18 19void 20est_get_id16(uint16* _id16) 21{ 22 *_id16 = x86_read_msr(MSR_GET_FREQ_STATE) & 0xffff; 23} 24 25 26status_t 27est_set_id16(uint16 id16, bool needCheck) 28{ 29 uint64 msr; 30 31 // Read the current register, mask out the old, set the new id. 32 msr = x86_read_msr(MSR_GET_FREQ_STATE); 33 msr = (msr & ~0xffff) | id16; 34 x86_write_msr(MSR_SET_FREQ_STATE, msr); 35 36 if (needCheck) { 37 // Wait a short while for the new setting. XXX Is this necessary? 38 snooze(EST_TRANS_LAT); 39 40 uint16 newID16; 41 est_get_id16(&newID16); 42 if (newID16 != id16) 43 return B_ERROR; 44 TRACE("EST: set frequency ok, id %i\n", id16); 45 } 46 return B_OK; 47} 48 49 50freq_info* 51est_get_current(freq_info* list) 52{ 53 // Try a few times to get a valid value. Sometimes, if the CPU 54 // is in the middle of an asynchronous transition (i.e., P4TCC), 55 // we get a temporary invalid result. 56 for (uint32 i = 0; i < 5; i++) { 57 uint16 id16; 58 est_get_id16(&id16); 59 60 for (freq_info* info = list; info->id != 0; info++) { 61 if (info->id == id16) 62 return info; 63 } 64 65 snooze(100); 66 } 67 return NULL; 68} 69 70 71status_t 72est_get_info(freq_info** _frequencyInfos) 73{ 74 uint64 msr = x86_read_msr(MSR_GET_FREQ_STATE); 75 status_t status = est_table_info(msr, _frequencyInfos); 76 if (status != B_OK) { 77 TRACE("EST: Get frequency table from model specific register\n"); 78 status = est_msr_info(msr, _frequencyInfos); 79 } 80 81 if (status != B_OK) { 82 TRACE("est: CPU supports Enhanced Speedstep, but is not recognized.\n"); 83 return status; 84 } 85 86 return B_OK; 87} 88 89 90status_t 91est_table_info(uint64 msr, freq_info** _frequencyInfos) 92{ 93 // Find a table which matches (vendor, id32). 94 system_info info; 95 if (get_system_info(&info) != B_OK) 96 return B_ERROR; 97 98 ss_cpu_info* cpuInfo; 99 uint32 id = msr >> 32; 100 for (cpuInfo = ESTprocs; cpuInfo->id32 != 0; cpuInfo++) { 101 if (cpuInfo->vendor_id == uint32(info.cpu_type & B_CPU_x86_VENDOR_MASK) 102 && cpuInfo->id32 == id) 103 break; 104 } 105 if (cpuInfo->id32 == 0) 106 return B_ERROR; 107 108 // Make sure the current setpoint is valid. 109 if (est_get_current(cpuInfo->freqtab) == NULL) { 110 TRACE("current setting not found in table\n"); 111 return B_ERROR; 112 } 113 114 *_frequencyInfos = cpuInfo->freqtab; 115 return B_OK; 116} 117 118 119bool 120bus_speed_ok(int bus) 121{ 122 switch (bus) { 123 case 100: 124 case 133: 125 case 166: 126 case 333: 127 return true; 128 default: 129 return false; 130 } 131} 132 133 134/*! Flesh out a simple rate table containing the high and low frequencies 135 based on the current clock speed and the upper 32 bits of the MSR. 136*/ 137status_t 138est_msr_info(uint64 msr, freq_info** _frequencyInfos) 139{ 140 // Figure out the bus clock. 141 system_info info; 142 if (get_system_info(&info) != B_OK) 143 return B_ERROR; 144 145 int32 freq = info.cpu_clock_speed / 1000000; 146 uint16 id = msr >> 32; 147 int32 bus = 0; 148 if (id >> 8) 149 bus = freq / (id >> 8); 150 151 TRACE("est: Guessed bus clock (high) of %d MHz\n", int(bus)); 152 if (!bus_speed_ok(bus)) { 153 // We may be running on the low frequency. 154 id = msr >> 48; 155 if (id >> 8) 156 bus = freq / (id >> 8); 157 TRACE("est: Guessed bus clock (low) of %d MHz\n", int(bus)); 158 if (!bus_speed_ok(bus)) 159 return B_ERROR; 160 161 // Calculate high frequency. 162 id = msr >> 32; 163 freq = ((id >> 8) & 0xff) * bus; 164 } 165 166 // Fill out a new freq table containing just the high and low freqs. 167 freq_info* frequencyInfo = (freq_info*)malloc(sizeof(freq_info) * 3); 168 if (frequencyInfo == NULL) 169 return B_NO_MEMORY; 170 171 memset(frequencyInfo, 0, sizeof(freq_info) * 3); 172 173 // First, the high frequency. 174 int32 volts = id & 0xff; 175 if (volts != 0) { 176 volts <<= 4; 177 volts += 700; 178 } 179 frequencyInfo[0].frequency = freq; 180 frequencyInfo[0].volts = volts; 181 frequencyInfo[0].id = id; 182 frequencyInfo[0].power = CPUFREQ_VAL_UNKNOWN; 183 TRACE("Guessed high setting of %d MHz @ %d Mv\n", int(freq), int(volts)); 184 185 // Second, the low frequency. 186 id = msr >> 48; 187 freq = ((id >> 8) & 0xff) * bus; 188 volts = id & 0xff; 189 if (volts != 0) { 190 volts <<= 4; 191 volts += 700; 192 } 193 frequencyInfo[1].frequency = freq; 194 frequencyInfo[1].volts = volts; 195 frequencyInfo[1].id = id; 196 frequencyInfo[1].power = CPUFREQ_VAL_UNKNOWN; 197 TRACE("Guessed low setting of %d MHz @ %d Mv\n", int(freq), int(volts)); 198 199 // Table is already terminated due to M_ZERO. 200 *_frequencyInfos = frequencyInfo; 201 return B_OK; 202} 203