Fixed an issue with the Nuvoton NCT6791D (hardware monitor i/o space lock wasn't disabled before attempting to read data).
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) 2010-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
12 using System.Globalization;
15 namespace OpenHardwareMonitor.Hardware.LPC {
16 internal class NCT677X : ISuperIO {
18 private readonly ushort port;
19 private readonly byte revision;
21 private readonly Chip chip;
23 private readonly bool isNuvotonVendor;
25 private readonly float?[] voltages = new float?[0];
26 private readonly float?[] temperatures = new float?[0];
27 private readonly float?[] fans = new float?[0];
28 private readonly float?[] controls = new float?[0];
31 private const uint ADDRESS_REGISTER_OFFSET = 0x05;
32 private const uint DATA_REGISTER_OFFSET = 0x06;
33 private const byte BANK_SELECT_REGISTER = 0x4E;
35 private byte ReadByte(ushort address) {
36 byte bank = (byte)(address >> 8);
37 byte register = (byte)(address & 0xFF);
38 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
39 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
40 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
41 return Ring0.ReadIoPort(port + DATA_REGISTER_OFFSET);
44 private void WriteByte(ushort address, byte value) {
45 byte bank = (byte)(address >> 8);
46 byte register = (byte)(address & 0xFF);
47 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
48 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
49 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
50 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, value);
54 private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
56 // Hardware Monitor Registers
57 private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
58 private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;
60 private readonly ushort[] FAN_PWM_OUT_REG =
61 { 0x001, 0x003, 0x011, 0x013, 0x015, 0x017 };
62 private readonly ushort[] FAN_PWM_COMMAND_REG =
63 { 0x109, 0x209, 0x309, 0x809, 0x909, 0xA09 };
64 private readonly ushort[] FAN_CONTROL_MODE_REG =
65 { 0x102, 0x202, 0x302, 0x802, 0x902, 0xA02 };
67 private readonly ushort fanRpmBaseRegister;
68 private readonly int minFanRPM;
70 private bool[] restoreDefaultFanControlRequired = new bool[6];
71 private byte[] initialFanControlMode = new byte[6];
72 private byte[] initialFanPwmCommand = new byte[6];
74 private readonly ushort[] voltageRegisters;
75 private readonly ushort voltageVBatRegister;
77 private readonly byte[] temperaturesSource;
79 private readonly ushort[] temperatureRegister;
80 private readonly ushort[] temperatureHalfRegister;
81 private readonly int[] temperatureHalfBit;
82 private readonly ushort[] temperatureSourceRegister;
84 private readonly ushort?[] alternateTemperatureRegister;
86 private enum SourceNCT6771F : byte {
99 PCH_CHIP_CPU_MAX_TEMP = 13,
109 private enum SourceNCT6776F : byte {
123 PCH_CHIP_CPU_MAX_TEMP = 14,
134 private enum SourceNCT67XXD : byte {
151 PCH_CHIP_CPU_MAX_TEMP = 18,
162 public NCT677X(Chip chip, byte revision, ushort port) {
164 this.revision = revision;
168 this.isNuvotonVendor = IsNuvotonVendor();
170 if (!isNuvotonVendor)
176 if (chip == Chip.NCT6771F) {
177 fans = new float?[4];
179 // min value RPM value with 16-bit fan counter
180 minFanRPM = (int)(1.35e6 / 0xFFFF);
182 temperaturesSource = new byte[] {
183 (byte)SourceNCT6771F.PECI_0,
184 (byte)SourceNCT6771F.CPUTIN,
185 (byte)SourceNCT6771F.AUXTIN,
186 (byte)SourceNCT6771F.SYSTIN
189 fans = new float?[5];
191 // min value RPM value with 13-bit fan counter
192 minFanRPM = (int)(1.35e6 / 0x1FFF);
194 temperaturesSource = new byte[] {
195 (byte)SourceNCT6776F.PECI_0,
196 (byte)SourceNCT6776F.CPUTIN,
197 (byte)SourceNCT6776F.AUXTIN,
198 (byte)SourceNCT6776F.SYSTIN
201 fanRpmBaseRegister = 0x656;
203 controls = new float?[3];
205 voltages = new float?[9];
206 voltageRegisters = new ushort[]
207 { 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x550, 0x551 };
208 voltageVBatRegister = 0x551;
210 temperatures = new float?[4];
211 temperatureRegister = new ushort[]
212 { 0x027, 0x073, 0x075, 0x077, 0x150, 0x250, 0x62B, 0x62C, 0x62D };
213 temperatureHalfRegister = new ushort[]
214 { 0, 0x074, 0x076, 0x078, 0x151, 0x251, 0x62E, 0x62E, 0x62E };
215 temperatureHalfBit = new int[]
216 { -1, 7, 7, 7, 7, 7, 0, 1, 2 };
217 temperatureSourceRegister = new ushort[]
218 { 0x621, 0x100, 0x200, 0x300, 0x622, 0x623, 0x624, 0x625, 0x626 };
220 alternateTemperatureRegister = new ushort?[]
221 { null, null, null, null };
226 if (chip == Chip.NCT6779D) {
227 fans = new float?[5];
228 controls = new float?[5];
230 fans = new float?[6];
231 controls = new float?[6];
234 fanRpmBaseRegister = 0x4C0;
236 // min value RPM value with 13-bit fan counter
237 minFanRPM = (int)(1.35e6 / 0x1FFF);
239 voltages = new float?[15];
240 voltageRegisters = new ushort[]
241 { 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488,
242 0x489, 0x48A, 0x48B, 0x48C, 0x48D, 0x48E };
243 voltageVBatRegister = 0x488;
245 temperatures = new float?[7];
246 temperaturesSource = new byte[] {
247 (byte)SourceNCT67XXD.PECI_0,
248 (byte)SourceNCT67XXD.CPUTIN,
249 (byte)SourceNCT67XXD.SYSTIN,
250 (byte)SourceNCT67XXD.AUXTIN0,
251 (byte)SourceNCT67XXD.AUXTIN1,
252 (byte)SourceNCT67XXD.AUXTIN2,
253 (byte)SourceNCT67XXD.AUXTIN3
256 temperatureRegister = new ushort[]
257 { 0x027, 0x073, 0x075, 0x077, 0x079, 0x07B, 0x150 };
258 temperatureHalfRegister = new ushort[]
259 { 0, 0x074, 0x076, 0x078, 0x07A, 0x07C, 0x151 };
260 temperatureHalfBit = new int[]
261 { -1, 7, 7, 7, 7, 7, 7 };
262 temperatureSourceRegister = new ushort[]
263 { 0x621, 0x100, 0x200, 0x300, 0x800, 0x900, 0x622 };
265 alternateTemperatureRegister = new ushort?[]
266 {null, 0x491, 0x490, 0x492, 0x493, 0x494, 0x495 };
272 private bool IsNuvotonVendor() {
273 return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
274 ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
277 public byte? ReadGPIO(int index) {
281 public void WriteGPIO(int index, byte value) { }
284 private void SaveDefaultFanControl(int index) {
285 if (!restoreDefaultFanControlRequired[index]) {
286 initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
287 initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
288 restoreDefaultFanControlRequired[index] = true;
292 private void RestoreDefaultFanControl(int index) {
293 if (restoreDefaultFanControlRequired[index]) {
294 WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
295 WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
296 restoreDefaultFanControlRequired[index] = false;
300 public void SetControl(int index, byte? value) {
301 if (!isNuvotonVendor)
304 if (index < 0 || index >= controls.Length)
305 throw new ArgumentOutOfRangeException("index");
307 if (!Ring0.WaitIsaBusMutex(10))
310 if (value.HasValue) {
311 SaveDefaultFanControl(index);
314 WriteByte(FAN_CONTROL_MODE_REG[index], 0);
317 WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);
319 RestoreDefaultFanControl(index);
322 Ring0.ReleaseIsaBusMutex();
325 public Chip Chip { get { return chip; } }
326 public float?[] Voltages { get { return voltages; } }
327 public float?[] Temperatures { get { return temperatures; } }
328 public float?[] Fans { get { return fans; } }
329 public float?[] Controls { get { return controls; } }
331 public void Update() {
332 if (!isNuvotonVendor)
335 if (!Ring0.WaitIsaBusMutex(10))
338 for (int i = 0; i < voltages.Length; i++) {
339 float value = 0.008f * ReadByte(voltageRegisters[i]);
340 bool valid = value > 0;
342 // check if battery voltage monitor is enabled
343 if (valid && voltageRegisters[i] == voltageVBatRegister)
344 valid = (ReadByte(0x005D) & 0x01) > 0;
346 voltages[i] = valid ? value : (float?)null;
349 int temperatureSourceMask = 0;
350 for (int i = temperatureRegister.Length - 1; i >= 0 ; i--) {
351 int value = ((sbyte)ReadByte(temperatureRegister[i])) << 1;
352 if (temperatureHalfBit[i] > 0) {
353 value |= ((ReadByte(temperatureHalfRegister[i]) >>
354 temperatureHalfBit[i]) & 0x1);
357 byte source = ReadByte(temperatureSourceRegister[i]);
358 temperatureSourceMask |= 1 << source;
360 float? temperature = 0.5f * value;
361 if (temperature > 125 || temperature < -55)
364 for (int j = 0; j < temperatures.Length; j++)
365 if (temperaturesSource[j] == source)
366 temperatures[j] = temperature;
368 for (int i = 0; i < alternateTemperatureRegister.Length; i++) {
369 if (!alternateTemperatureRegister[i].HasValue)
372 if ((temperatureSourceMask & (1 << temperaturesSource[i])) > 0)
375 float? temperature = (sbyte)
376 ReadByte(alternateTemperatureRegister[i].Value);
378 if (temperature > 125 || temperature < -55)
381 temperatures[i] = temperature;
384 for (int i = 0; i < fans.Length; i++) {
385 byte high = ReadByte((ushort)(fanRpmBaseRegister + (i << 1)));
386 byte low = ReadByte((ushort)(fanRpmBaseRegister + (i << 1) + 1));
387 int value = (high << 8) | low;
389 fans[i] = value > minFanRPM ? value : 0;
392 for (int i = 0; i < controls.Length; i++) {
393 int value = ReadByte(FAN_PWM_OUT_REG[i]);
394 controls[i] = value / 2.55f;
397 Ring0.ReleaseIsaBusMutex();
400 public string GetReport() {
401 StringBuilder r = new StringBuilder();
403 r.AppendLine("LPC " + this.GetType().Name);
405 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
406 r.Append("Chip revision: 0x");
407 r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
408 r.Append("Base Adress: 0x");
409 r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
412 if (!Ring0.WaitIsaBusMutex(100))
415 ushort[] addresses = new ushort[] {
416 0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
417 0x100, 0x110, 0x120, 0x130, 0x140, 0x150,
418 0x200, 0x220, 0x230, 0x240, 0x250, 0x260,
419 0x300, 0x320, 0x330, 0x340, 0x360,
420 0x400, 0x410, 0x420, 0x440, 0x450, 0x460, 0x480, 0x490, 0x4B0,
423 0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670,
424 0x700, 0x710, 0x720, 0x730,
425 0x800, 0x820, 0x830, 0x840,
426 0x900, 0x920, 0x930, 0x940, 0x960,
427 0xA00, 0xA10, 0xA20, 0xA30, 0xA40, 0xA50, 0xA60, 0xA70,
428 0xB00, 0xB10, 0xB20, 0xB30, 0xB50, 0xB60, 0xB70,
429 0xC00, 0xC10, 0xC20, 0xC30, 0xC50, 0xC60, 0xC70,
430 0xD00, 0xD10, 0xD20, 0xD30, 0xD50, 0xD60,
431 0xE00, 0xE10, 0xE20, 0xE30,
432 0xF00, 0xF10, 0xF20, 0xF30,
435 r.AppendLine("Hardware Monitor Registers");
437 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
439 foreach (ushort address in addresses) {
441 r.Append(address.ToString("X4", CultureInfo.InvariantCulture));
443 for (ushort j = 0; j <= 0xF; j++) {
445 r.Append(ReadByte((ushort)(address | j)).ToString(
446 "X2", CultureInfo.InvariantCulture));
452 Ring0.ReleaseIsaBusMutex();