Enabled fan control on mainboards by default.
3 This Source Code Form is subject to the terms of the Mozilla Public
4 License, v. 2.0. If a copy of the MPL was not distributed with this
5 file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 Copyright (C) 2009-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
12 using System.Collections.Generic;
13 using System.Diagnostics;
14 using System.Globalization;
16 using System.Runtime.InteropServices;
18 using System.Threading;
20 namespace OpenHardwareMonitor.Hardware.CPU {
22 internal sealed class AMD10CPU : AMDCPU {
24 private readonly Sensor coreTemperature;
25 private readonly Sensor[] coreClocks;
26 private readonly Sensor busClock;
28 private const uint PERF_CTL_0 = 0xC0010000;
29 private const uint PERF_CTR_0 = 0xC0010004;
30 private const uint HWCR = 0xC0010015;
31 private const uint P_STATE_0 = 0xC0010064;
32 private const uint COFVID_STATUS = 0xC0010071;
34 private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
35 private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
36 private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;
37 private const ushort FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
38 private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
39 private const ushort FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1603;
40 private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
41 private const uint CLOCK_POWER_TIMING_CONTROL_0_REGISTER = 0xD4;
43 private readonly uint miscellaneousControlAddress;
44 private readonly ushort miscellaneousControlDeviceId;
46 private readonly FileStream temperatureStream;
48 private readonly double timeStampCounterMultiplier;
49 private readonly bool corePerformanceBoostSupport;
51 public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
52 : base(processorIndex, cpuid, settings)
54 // AMD family 1Xh processors support only one temperature sensor
55 coreTemperature = new Sensor(
56 "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
57 SensorType.Temperature, this, new [] {
58 new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
62 case 0x10: miscellaneousControlDeviceId =
63 FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
64 case 0x11: miscellaneousControlDeviceId =
65 FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
66 case 0x12: miscellaneousControlDeviceId =
67 FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
68 case 0x14: miscellaneousControlDeviceId =
69 FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
70 case 0x15: miscellaneousControlDeviceId =
71 FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
72 default: miscellaneousControlDeviceId = 0; break;
75 // get the pci address for the Miscellaneous Control registers
76 miscellaneousControlAddress = GetPciAddress(
77 MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);
79 busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
80 coreClocks = new Sensor[coreCount];
81 for (int i = 0; i < coreClocks.Length; i++) {
82 coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
84 if (HasTimeStampCounter)
85 ActivateSensor(coreClocks[i]);
88 corePerformanceBoostSupport = (cpuid[0][0].ExtData[7, 3] & (1 << 9)) > 0;
90 // set affinity to the first thread for all frequency estimations
91 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
93 // disable core performance boost
94 uint hwcrEax, hwcrEdx;
95 Ring0.Rdmsr(HWCR, out hwcrEax, out hwcrEdx);
96 if (corePerformanceBoostSupport)
97 Ring0.Wrmsr(HWCR, hwcrEax | (1 << 25), hwcrEdx);
100 Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
102 Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
104 timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
106 // restore the performance counter registers
107 Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
108 Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
110 // restore core performance boost
111 if (corePerformanceBoostSupport)
112 Ring0.Wrmsr(HWCR, hwcrEax, hwcrEdx);
114 // restore the thread affinity.
115 ThreadAffinity.Set(mask);
117 // the file reader for lm-sensors support on Linux
118 temperatureStream = null;
119 int p = (int)Environment.OSVersion.Platform;
120 if ((p == 4) || (p == 128)) {
121 string[] devicePaths = Directory.GetDirectories("/sys/class/hwmon/");
122 foreach (string path in devicePaths) {
125 using (StreamReader reader = new StreamReader(path + "/device/name"))
126 name = reader.ReadLine();
127 } catch (IOException) { }
130 temperatureStream = new FileStream(path + "/device/temp1_input",
131 FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
140 private double estimateTimeStampCounterMultiplier() {
141 // preload the function
142 estimateTimeStampCounterMultiplier(0);
143 estimateTimeStampCounterMultiplier(0);
145 // estimate the multiplier
146 List<double> estimate = new List<double>(3);
147 for (int i = 0; i < 3; i++)
148 estimate.Add(estimateTimeStampCounterMultiplier(0.025));
153 private double estimateTimeStampCounterMultiplier(double timeWindow) {
156 // select event "076h CPU Clocks not Halted" and enable the counter
157 Ring0.Wrmsr(PERF_CTL_0,
158 (1 << 22) | // enable performance counter
159 (1 << 17) | // count events in user mode
160 (1 << 16) | // count events in operating-system mode
163 // set the counter to 0
164 Ring0.Wrmsr(PERF_CTR_0, 0, 0);
166 long ticks = (long)(timeWindow * Stopwatch.Frequency);
167 uint lsbBegin, msbBegin, lsbEnd, msbEnd;
169 long timeBegin = Stopwatch.GetTimestamp() +
170 (long)Math.Ceiling(0.001 * ticks);
171 long timeEnd = timeBegin + ticks;
172 while (Stopwatch.GetTimestamp() < timeBegin) { }
173 Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
175 while (Stopwatch.GetTimestamp() < timeEnd) { }
176 Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
177 Ring0.Rdmsr(COFVID_STATUS, out eax, out edx);
178 double coreMultiplier = GetCoreMultiplier(eax);
180 ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
181 ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
183 double coreFrequency = 1e-6 *
184 (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
185 (timeEnd - timeBegin);
187 double busFrequency = coreFrequency / coreMultiplier;
189 return 0.25 * Math.Round(4 * TimeStampCounterFrequency / busFrequency);
192 protected override uint[] GetMSRs() {
193 return new uint[] { PERF_CTL_0, PERF_CTR_0, HWCR, P_STATE_0,
197 public override string GetReport() {
198 StringBuilder r = new StringBuilder();
199 r.Append(base.GetReport());
201 r.Append("Miscellaneous Control Address: 0x");
202 r.AppendLine((miscellaneousControlAddress).ToString("X",
203 CultureInfo.InvariantCulture));
204 r.Append("Time Stamp Counter Multiplier: ");
205 r.AppendLine(timeStampCounterMultiplier.ToString(
206 CultureInfo.InvariantCulture));
207 if (family == 0x14) {
209 Ring0.ReadPciConfig(miscellaneousControlAddress,
210 CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
211 r.Append("PCI Register D18F3xD4: ");
212 r.AppendLine(value.ToString("X8", CultureInfo.InvariantCulture));
219 private double GetCoreMultiplier(uint cofvidEax) {
224 // 8:6 CpuDid: current core divisor ID
225 // 5:0 CpuFid: current core frequency ID
226 uint cpuDid = (cofvidEax >> 6) & 7;
227 uint cpuFid = cofvidEax & 0x1F;
228 return 0.5 * (cpuFid + 0x10) / (1 << (int)cpuDid);
231 // 8:4 CpuFid: current CPU core frequency ID
232 // 3:0 CpuDid: current CPU core divisor ID
233 uint cpuFid = (cofvidEax >> 4) & 0x1F;
234 uint cpuDid = cofvidEax & 0xF;
237 case 0: divisor = 1; break;
238 case 1: divisor = 1.5; break;
239 case 2: divisor = 2; break;
240 case 3: divisor = 3; break;
241 case 4: divisor = 4; break;
242 case 5: divisor = 6; break;
243 case 6: divisor = 8; break;
244 case 7: divisor = 12; break;
245 case 8: divisor = 16; break;
246 default: divisor = 1; break;
248 return (cpuFid + 0x10) / divisor;
251 // 8:4: current CPU core divisor ID most significant digit
252 // 3:0: current CPU core divisor ID least significant digit
253 uint divisorIdMSD = (cofvidEax >> 4) & 0x1F;
254 uint divisorIdLSD = cofvidEax & 0xF;
256 Ring0.ReadPciConfig(miscellaneousControlAddress,
257 CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
258 uint frequencyId = value & 0x1F;
259 return (frequencyId + 0x10) /
260 (divisorIdMSD + (divisorIdLSD * 0.25) + 1);
267 private string ReadFirstLine(Stream stream) {
268 StringBuilder sb = new StringBuilder();
270 stream.Seek(0, SeekOrigin.Begin);
271 int b = stream.ReadByte();
272 while (b != -1 && b != 10) {
274 b = stream.ReadByte();
277 return sb.ToString();
280 public override void Update() {
283 if (temperatureStream == null) {
284 if (miscellaneousControlAddress != Ring0.InvalidPciAddress) {
286 if (Ring0.ReadPciConfig(miscellaneousControlAddress,
287 REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
288 if (family == 0x15 && (value & 0x30000) == 0x30000) {
289 coreTemperature.Value = ((value >> 21) & 0x7FC) / 8.0f +
290 coreTemperature.Parameters[0].Value - 49;
292 coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
293 coreTemperature.Parameters[0].Value;
295 ActivateSensor(coreTemperature);
297 DeactivateSensor(coreTemperature);
301 string s = ReadFirstLine(temperatureStream);
303 coreTemperature.Value = 0.001f *
304 long.Parse(s, CultureInfo.InvariantCulture);
305 ActivateSensor(coreTemperature);
307 DeactivateSensor(coreTemperature);
311 if (HasTimeStampCounter) {
312 double newBusClock = 0;
314 for (int i = 0; i < coreClocks.Length; i++) {
318 if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
319 1UL << cpuid[i][0].Thread))
322 multiplier = GetCoreMultiplier(curEax);
324 coreClocks[i].Value =
325 (float)(multiplier * TimeStampCounterFrequency /
326 timeStampCounterMultiplier);
328 (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
330 coreClocks[i].Value = (float)TimeStampCounterFrequency;
334 if (newBusClock > 0) {
335 this.busClock.Value = (float)newBusClock;
336 ActivateSensor(this.busClock);
341 public override void Close() {
342 if (temperatureStream != null) {
343 temperatureStream.Close();