Added a mainboard specific configuration for the Asus P8Z77-V (Issue 347) and added an additional temperature for the Gigabyte GA-MA78LM-S2H (Issue 358).
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-2012 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 float?[] voltages;
24 private readonly float?[] temperatures;
25 private readonly float?[] fans ;
26 private readonly float?[] controls;
29 private const uint ADDRESS_REGISTER_OFFSET = 0x05;
30 private const uint DATA_REGISTER_OFFSET = 0x06;
31 private const byte BANK_SELECT_REGISTER = 0x4E;
33 private byte ReadByte(ushort address) {
34 byte bank = (byte)(address >> 8);
35 byte register = (byte)(address & 0xFF);
36 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
37 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
38 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
39 return Ring0.ReadIoPort(port + DATA_REGISTER_OFFSET);
42 private void WriteByte(ushort address, byte value) {
43 byte bank = (byte)(address >> 8);
44 byte register = (byte)(address & 0xFF);
45 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
46 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
47 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
48 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, value);
52 private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
54 // Hardware Monitor Registers
55 private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
56 private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;
58 private readonly ushort[] FAN_PWM_OUT_REG =
59 { 0x001, 0x003, 0x011, 0x013, 0x015 };
60 private readonly ushort[] FAN_PWM_COMMAND_REG =
61 { 0x109, 0x209, 0x309, 0x809, 0x909 };
62 private readonly ushort[] FAN_CONTROL_MODE_REG =
63 { 0x102, 0x202, 0x302, 0x802, 0x902 };
65 private readonly ushort fanRpmBaseRegister;
66 private readonly int minFanRPM;
68 private bool[] restoreDefaultFanControlRequired = new bool[5];
69 private byte[] initialFanControlMode = new byte[5];
70 private byte[] initialFanPwmCommand = new byte[5];
72 private readonly ushort[] voltageRegisters;
73 private readonly ushort voltageVBatRegister;
75 private readonly byte[] temperaturesSource;
77 private readonly ushort[] temperatureRegister;
78 private readonly ushort[] temperatureHalfRegister;
79 private readonly int[] temperatureHalfBit;
80 private readonly ushort[] temperatureSourceRegister;
82 private readonly ushort?[] alternateTemperatureRegister;
84 private enum SourceNCT6771F : byte {
97 PCH_CHIP_CPU_MAX_TEMP = 13,
107 private enum SourceNCT6776F : byte {
121 PCH_CHIP_CPU_MAX_TEMP = 14,
132 private enum SourceNCT6779D : byte {
149 PCH_CHIP_CPU_MAX_TEMP = 18,
160 public NCT677X(Chip chip, byte revision, ushort port) {
162 this.revision = revision;
165 if (!IsNuvotonVendor())
171 if (chip == Chip.NCT6771F) {
172 fans = new float?[4];
174 // min value RPM value with 16-bit fan counter
175 minFanRPM = (int)(1.35e6 / 0xFFFF);
177 temperaturesSource = new byte[] {
178 (byte)SourceNCT6771F.PECI_0,
179 (byte)SourceNCT6771F.CPUTIN,
180 (byte)SourceNCT6771F.AUXTIN,
181 (byte)SourceNCT6771F.SYSTIN
184 fans = new float?[5];
186 // min value RPM value with 13-bit fan counter
187 minFanRPM = (int)(1.35e6 / 0x1FFF);
189 temperaturesSource = new byte[] {
190 (byte)SourceNCT6776F.PECI_0,
191 (byte)SourceNCT6776F.CPUTIN,
192 (byte)SourceNCT6776F.AUXTIN,
193 (byte)SourceNCT6776F.SYSTIN
196 fanRpmBaseRegister = 0x656;
198 controls = new float?[3];
200 voltages = new float?[9];
201 voltageRegisters = new ushort[]
202 { 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x550, 0x551 };
203 voltageVBatRegister = 0x551;
205 temperatures = new float?[4];
206 temperatureRegister = new ushort[]
207 { 0x027, 0x073, 0x075, 0x077, 0x150, 0x250, 0x62B, 0x62C, 0x62D };
208 temperatureHalfRegister = new ushort[]
209 { 0, 0x074, 0x076, 0x078, 0x151, 0x251, 0x62E, 0x62E, 0x62E };
210 temperatureHalfBit = new int[]
211 { -1, 7, 7, 7, 7, 7, 0, 1, 2 };
212 temperatureSourceRegister = new ushort[]
213 { 0x621, 0x100, 0x200, 0x300, 0x622, 0x623, 0x624, 0x625, 0x626 };
215 alternateTemperatureRegister = new ushort?[]
216 { null, null, null, null };
220 fans = new float?[5];
221 fanRpmBaseRegister = 0x4C0;
223 // min value RPM value with 13-bit fan counter
224 minFanRPM = (int)(1.35e6 / 0x1FFF);
226 controls = new float?[5];
228 voltages = new float?[15];
229 voltageRegisters = new ushort[]
230 { 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488,
231 0x489, 0x48A, 0x48B, 0x48C, 0x48D, 0x48E };
232 voltageVBatRegister = 0x488;
234 temperatures = new float?[7];
235 temperaturesSource = new byte[] {
236 (byte)SourceNCT6779D.PECI_0,
237 (byte)SourceNCT6779D.CPUTIN,
238 (byte)SourceNCT6779D.SYSTIN,
239 (byte)SourceNCT6779D.AUXTIN0,
240 (byte)SourceNCT6779D.AUXTIN1,
241 (byte)SourceNCT6779D.AUXTIN2,
242 (byte)SourceNCT6779D.AUXTIN3
245 temperatureRegister = new ushort[]
246 { 0x027, 0x073, 0x075, 0x077, 0x079, 0x07B, 0x150 };
247 temperatureHalfRegister = new ushort[]
248 { 0, 0x074, 0x076, 0x078, 0x07A, 0x07C, 0x151 };
249 temperatureHalfBit = new int[]
250 { -1, 7, 7, 7, 7, 7, 7 };
251 temperatureSourceRegister = new ushort[]
252 { 0x621, 0x100, 0x200, 0x300, 0x800, 0x900, 0x622 };
254 alternateTemperatureRegister = new ushort?[]
255 {null, 0x491, 0x490, 0x492, 0x493, 0x494, 0x495 };
261 private bool IsNuvotonVendor() {
262 return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
263 ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
266 public byte? ReadGPIO(int index) {
270 public void WriteGPIO(int index, byte value) { }
273 private void SaveDefaultFanControl(int index) {
274 if (!restoreDefaultFanControlRequired[index]) {
275 initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
276 initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
277 restoreDefaultFanControlRequired[index] = true;
281 private void RestoreDefaultFanControl(int index) {
282 if (restoreDefaultFanControlRequired[index]) {
283 WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
284 WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
285 restoreDefaultFanControlRequired[index] = false;
289 public void SetControl(int index, byte? value) {
290 if (index < 0 || index >= controls.Length)
291 throw new ArgumentOutOfRangeException("index");
293 if (!Ring0.WaitIsaBusMutex(10))
296 if (value.HasValue) {
297 SaveDefaultFanControl(index);
300 WriteByte(FAN_CONTROL_MODE_REG[index], 0);
303 WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);
305 RestoreDefaultFanControl(index);
308 Ring0.ReleaseIsaBusMutex();
311 public Chip Chip { get { return chip; } }
312 public float?[] Voltages { get { return voltages; } }
313 public float?[] Temperatures { get { return temperatures; } }
314 public float?[] Fans { get { return fans; } }
315 public float?[] Controls { get { return controls; } }
317 public void Update() {
318 if (!Ring0.WaitIsaBusMutex(10))
321 for (int i = 0; i < voltages.Length; i++) {
322 float value = 0.008f * ReadByte(voltageRegisters[i]);
323 bool valid = value > 0;
325 // check if battery voltage monitor is enabled
326 if (valid && voltageRegisters[i] == voltageVBatRegister)
327 valid = (ReadByte(0x005D) & 0x01) > 0;
329 voltages[i] = valid ? value : (float?)null;
332 int temperatureSourceMask = 0;
333 for (int i = temperatureRegister.Length - 1; i >= 0 ; i--) {
334 int value = ((sbyte)ReadByte(temperatureRegister[i])) << 1;
335 if (temperatureHalfBit[i] > 0) {
336 value |= ((ReadByte(temperatureHalfRegister[i]) >>
337 temperatureHalfBit[i]) & 0x1);
340 byte source = ReadByte(temperatureSourceRegister[i]);
341 temperatureSourceMask |= 1 << source;
343 float? temperature = 0.5f * value;
344 if (temperature > 125 || temperature < -55)
347 for (int j = 0; j < temperatures.Length; j++)
348 if (temperaturesSource[j] == source)
349 temperatures[j] = temperature;
351 for (int i = 0; i < alternateTemperatureRegister.Length; i++) {
352 if (!alternateTemperatureRegister[i].HasValue)
355 if ((temperatureSourceMask & (1 << temperaturesSource[i])) > 0)
358 float? temperature = (sbyte)
359 ReadByte(alternateTemperatureRegister[i].Value);
361 if (temperature > 125 || temperature < -55)
364 temperatures[i] = temperature;
367 for (int i = 0; i < fans.Length; i++) {
368 byte high = ReadByte((ushort)(fanRpmBaseRegister + (i << 1)));
369 byte low = ReadByte((ushort)(fanRpmBaseRegister + (i << 1) + 1));
370 int value = (high << 8) | low;
372 fans[i] = value > minFanRPM ? value : 0;
375 for (int i = 0; i < controls.Length; i++) {
376 int value = ReadByte(FAN_PWM_OUT_REG[i]);
377 controls[i] = value / 2.55f;
380 Ring0.ReleaseIsaBusMutex();
383 public string GetReport() {
384 StringBuilder r = new StringBuilder();
386 r.AppendLine("LPC " + this.GetType().Name);
388 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
389 r.Append("Chip revision: 0x");
390 r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
391 r.Append("Base Adress: 0x");
392 r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
395 if (!Ring0.WaitIsaBusMutex(100))
398 ushort[] addresses = new ushort[] {
399 0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
400 0x100, 0x110, 0x120, 0x130, 0x140, 0x150,
401 0x200, 0x220, 0x230, 0x240, 0x250,
402 0x300, 0x320, 0x330, 0x340,
403 0x400, 0x410, 0x420, 0x440, 0x450, 0x460, 0x480, 0x490, 0x4C0,
405 0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670,
408 0xA00, 0xA10, 0xA20, 0xA30, 0xA50, 0xA60, 0xA70,
409 0xB00, 0xB10, 0xB20, 0xB30, 0xB50, 0xB60, 0xB70,
410 0xC00, 0xC10, 0xC20, 0xC30, 0xC50, 0xC60, 0xC70,
411 0xD00, 0xD10, 0xD20, 0xD30, 0xD50, 0xD60,
412 0xE00, 0xE10, 0xE20, 0xE30,
413 0xF00, 0xF10, 0xF20, 0xF30};
415 r.AppendLine("Hardware Monitor Registers");
417 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
419 foreach (ushort address in addresses) {
421 r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
423 for (ushort j = 0; j <= 0xF; j++) {
425 r.Append(ReadByte((ushort)(address | j)).ToString(
426 "X2", CultureInfo.InvariantCulture));
432 Ring0.ReleaseIsaBusMutex();