Added experimental support for the Nuvoton NCT6791D super I/O chip.
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-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
12 using System.Collections.Generic;
13 using System.Diagnostics;
14 using System.Globalization;
17 using System.Threading;
19 namespace OpenHardwareMonitor.Hardware.CPU {
21 internal sealed class AMD10CPU : AMDCPU {
23 private readonly Sensor coreTemperature;
24 private readonly Sensor[] coreClocks;
25 private readonly Sensor busClock;
27 private const uint PERF_CTL_0 = 0xC0010000;
28 private const uint PERF_CTR_0 = 0xC0010004;
29 private const uint HWCR = 0xC0010015;
30 private const uint P_STATE_0 = 0xC0010064;
31 private const uint COFVID_STATUS = 0xC0010071;
33 private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
34 private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
35 private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;
36 private const ushort FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
37 private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
38 private const ushort FAMILY_15H_MODEL_00_MISC_CONTROL_DEVICE_ID = 0x1603;
39 private const ushort FAMILY_15H_MODEL_10_MISC_CONTROL_DEVICE_ID = 0x1403;
40 private const ushort FAMILY_16H_MODEL_00_MISC_CONTROL_DEVICE_ID = 0x1533;
42 private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
43 private const uint CLOCK_POWER_TIMING_CONTROL_0_REGISTER = 0xD4;
45 private readonly uint miscellaneousControlAddress;
46 private readonly ushort miscellaneousControlDeviceId;
48 private readonly FileStream temperatureStream;
50 private readonly double timeStampCounterMultiplier;
51 private readonly bool corePerformanceBoostSupport;
53 public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
54 : base(processorIndex, cpuid, settings)
56 // AMD family 1Xh processors support only one temperature sensor
57 coreTemperature = new Sensor(
58 "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
59 SensorType.Temperature, this, new [] {
60 new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
64 case 0x10: miscellaneousControlDeviceId =
65 FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
66 case 0x11: miscellaneousControlDeviceId =
67 FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
68 case 0x12: miscellaneousControlDeviceId =
69 FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
70 case 0x14: miscellaneousControlDeviceId =
71 FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
73 switch (model & 0xF0) {
74 case 0x00: miscellaneousControlDeviceId =
75 FAMILY_15H_MODEL_00_MISC_CONTROL_DEVICE_ID; break;
76 case 0x10: miscellaneousControlDeviceId =
77 FAMILY_15H_MODEL_10_MISC_CONTROL_DEVICE_ID; break;
78 default: miscellaneousControlDeviceId = 0; break;
81 switch (model & 0xF0) {
82 case 0x00: miscellaneousControlDeviceId =
83 FAMILY_16H_MODEL_00_MISC_CONTROL_DEVICE_ID; break;
84 default: miscellaneousControlDeviceId = 0; break;
86 default: miscellaneousControlDeviceId = 0; break;
89 // get the pci address for the Miscellaneous Control registers
90 miscellaneousControlAddress = GetPciAddress(
91 MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);
93 busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
94 coreClocks = new Sensor[coreCount];
95 for (int i = 0; i < coreClocks.Length; i++) {
96 coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
98 if (HasTimeStampCounter)
99 ActivateSensor(coreClocks[i]);
102 corePerformanceBoostSupport = (cpuid[0][0].ExtData[7, 3] & (1 << 9)) > 0;
104 // set affinity to the first thread for all frequency estimations
105 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
107 // disable core performance boost
108 uint hwcrEax, hwcrEdx;
109 Ring0.Rdmsr(HWCR, out hwcrEax, out hwcrEdx);
110 if (corePerformanceBoostSupport)
111 Ring0.Wrmsr(HWCR, hwcrEax | (1 << 25), hwcrEdx);
114 Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
116 Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
118 timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
120 // restore the performance counter registers
121 Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
122 Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
124 // restore core performance boost
125 if (corePerformanceBoostSupport)
126 Ring0.Wrmsr(HWCR, hwcrEax, hwcrEdx);
128 // restore the thread affinity.
129 ThreadAffinity.Set(mask);
131 // the file reader for lm-sensors support on Linux
132 temperatureStream = null;
133 int p = (int)Environment.OSVersion.Platform;
134 if ((p == 4) || (p == 128)) {
135 string[] devicePaths = Directory.GetDirectories("/sys/class/hwmon/");
136 foreach (string path in devicePaths) {
139 using (StreamReader reader = new StreamReader(path + "/device/name"))
140 name = reader.ReadLine();
141 } catch (IOException) { }
144 temperatureStream = new FileStream(path + "/device/temp1_input",
145 FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
154 private double estimateTimeStampCounterMultiplier() {
155 // preload the function
156 estimateTimeStampCounterMultiplier(0);
157 estimateTimeStampCounterMultiplier(0);
159 // estimate the multiplier
160 List<double> estimate = new List<double>(3);
161 for (int i = 0; i < 3; i++)
162 estimate.Add(estimateTimeStampCounterMultiplier(0.025));
167 private double estimateTimeStampCounterMultiplier(double timeWindow) {
170 // select event "076h CPU Clocks not Halted" and enable the counter
171 Ring0.Wrmsr(PERF_CTL_0,
172 (1 << 22) | // enable performance counter
173 (1 << 17) | // count events in user mode
174 (1 << 16) | // count events in operating-system mode
177 // set the counter to 0
178 Ring0.Wrmsr(PERF_CTR_0, 0, 0);
180 long ticks = (long)(timeWindow * Stopwatch.Frequency);
181 uint lsbBegin, msbBegin, lsbEnd, msbEnd;
183 long timeBegin = Stopwatch.GetTimestamp() +
184 (long)Math.Ceiling(0.001 * ticks);
185 long timeEnd = timeBegin + ticks;
186 while (Stopwatch.GetTimestamp() < timeBegin) { }
187 Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
189 while (Stopwatch.GetTimestamp() < timeEnd) { }
190 Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
191 Ring0.Rdmsr(COFVID_STATUS, out eax, out edx);
192 double coreMultiplier = GetCoreMultiplier(eax);
194 ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
195 ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
197 double coreFrequency = 1e-6 *
198 (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
199 (timeEnd - timeBegin);
201 double busFrequency = coreFrequency / coreMultiplier;
203 return 0.25 * Math.Round(4 * TimeStampCounterFrequency / busFrequency);
206 protected override uint[] GetMSRs() {
207 return new uint[] { PERF_CTL_0, PERF_CTR_0, HWCR, P_STATE_0,
211 public override string GetReport() {
212 StringBuilder r = new StringBuilder();
213 r.Append(base.GetReport());
215 r.Append("Miscellaneous Control Address: 0x");
216 r.AppendLine((miscellaneousControlAddress).ToString("X",
217 CultureInfo.InvariantCulture));
218 r.Append("Time Stamp Counter Multiplier: ");
219 r.AppendLine(timeStampCounterMultiplier.ToString(
220 CultureInfo.InvariantCulture));
221 if (family == 0x14) {
223 Ring0.ReadPciConfig(miscellaneousControlAddress,
224 CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
225 r.Append("PCI Register D18F3xD4: ");
226 r.AppendLine(value.ToString("X8", CultureInfo.InvariantCulture));
233 private double GetCoreMultiplier(uint cofvidEax) {
239 // 8:6 CpuDid: current core divisor ID
240 // 5:0 CpuFid: current core frequency ID
241 uint cpuDid = (cofvidEax >> 6) & 7;
242 uint cpuFid = cofvidEax & 0x1F;
243 return 0.5 * (cpuFid + 0x10) / (1 << (int)cpuDid);
246 // 8:4 CpuFid: current CPU core frequency ID
247 // 3:0 CpuDid: current CPU core divisor ID
248 uint cpuFid = (cofvidEax >> 4) & 0x1F;
249 uint cpuDid = cofvidEax & 0xF;
252 case 0: divisor = 1; break;
253 case 1: divisor = 1.5; break;
254 case 2: divisor = 2; break;
255 case 3: divisor = 3; break;
256 case 4: divisor = 4; break;
257 case 5: divisor = 6; break;
258 case 6: divisor = 8; break;
259 case 7: divisor = 12; break;
260 case 8: divisor = 16; break;
261 default: divisor = 1; break;
263 return (cpuFid + 0x10) / divisor;
266 // 8:4: current CPU core divisor ID most significant digit
267 // 3:0: current CPU core divisor ID least significant digit
268 uint divisorIdMSD = (cofvidEax >> 4) & 0x1F;
269 uint divisorIdLSD = cofvidEax & 0xF;
271 Ring0.ReadPciConfig(miscellaneousControlAddress,
272 CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
273 uint frequencyId = value & 0x1F;
274 return (frequencyId + 0x10) /
275 (divisorIdMSD + (divisorIdLSD * 0.25) + 1);
282 private string ReadFirstLine(Stream stream) {
283 StringBuilder sb = new StringBuilder();
285 stream.Seek(0, SeekOrigin.Begin);
286 int b = stream.ReadByte();
287 while (b != -1 && b != 10) {
289 b = stream.ReadByte();
292 return sb.ToString();
295 public override void Update() {
298 if (temperatureStream == null) {
299 if (miscellaneousControlAddress != Ring0.InvalidPciAddress) {
301 if (Ring0.ReadPciConfig(miscellaneousControlAddress,
302 REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
303 if (family == 0x15 && (value & 0x30000) == 0x30000) {
304 if ((model & 0xF0) == 0x00) {
305 coreTemperature.Value = ((value >> 21) & 0x7FC) / 8.0f +
306 coreTemperature.Parameters[0].Value - 49;
308 coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
309 coreTemperature.Parameters[0].Value - 49;
311 } else if (family == 0x16 &&
312 ((value & 0x30000) == 0x30000 || (value & 0x80000) == 0x80000)) {
313 coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
314 coreTemperature.Parameters[0].Value - 49;
316 coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
317 coreTemperature.Parameters[0].Value;
319 ActivateSensor(coreTemperature);
321 DeactivateSensor(coreTemperature);
325 string s = ReadFirstLine(temperatureStream);
327 coreTemperature.Value = 0.001f *
328 long.Parse(s, CultureInfo.InvariantCulture);
329 ActivateSensor(coreTemperature);
331 DeactivateSensor(coreTemperature);
335 if (HasTimeStampCounter) {
336 double newBusClock = 0;
338 for (int i = 0; i < coreClocks.Length; i++) {
342 if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
343 1UL << cpuid[i][0].Thread))
346 multiplier = GetCoreMultiplier(curEax);
348 coreClocks[i].Value =
349 (float)(multiplier * TimeStampCounterFrequency /
350 timeStampCounterMultiplier);
352 (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
354 coreClocks[i].Value = (float)TimeStampCounterFrequency;
358 if (newBusClock > 0) {
359 this.busClock.Value = (float)newBusClock;
360 ActivateSensor(this.busClock);
365 public override void Close() {
366 if (temperatureStream != null) {
367 temperatureStream.Close();