// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright (C) LibreHardwareMonitor and Contributors. // Partial Copyright (C) Michael Möller and Contributors. // All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using System.Threading; namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc { internal class Nct677X : ISuperIO { private readonly struct TemperatureSourceData { public TemperatureSourceData(Enum source, ushort register, ushort halfRegister = 0, int halfBit = -1, ushort sourceRegister = 0, ushort? alternateRegister = null) { Source = source; Register = register; HalfRegister = halfRegister; HalfBit = halfBit; SourceRegister = sourceRegister; AlternateRegister = alternateRegister; } public readonly Enum Source; public readonly ushort Register; public readonly ushort HalfRegister; public readonly int HalfBit; public readonly ushort SourceRegister; public readonly ushort? AlternateRegister; } private readonly ushort[] _fanCountRegister; private readonly ushort[] _fanRpmRegister; private readonly byte[] _initialFanControlMode = new byte[7]; private readonly byte[] _initialFanPwmCommand = new byte[7]; private readonly bool _isNuvotonVendor; private readonly LpcPort _lpcPort; private readonly int _maxFanCount; private readonly int _minFanCount; private readonly int _minFanRpm; private readonly ushort _port; private readonly bool[] _restoreDefaultFanControlRequired = new bool[7]; private readonly byte _revision; private readonly TemperatureSourceData[] _temperaturesSource; private readonly ushort _vBatMonitorControlRegister; private readonly ushort[] _voltageRegisters; private readonly ushort _voltageVBatRegister; public Nct677X(Chip chip, byte revision, ushort port, LpcPort lpcPort) { Chip = chip; _revision = revision; _port = port; _lpcPort = lpcPort; if (chip == Chip.NCT610XD) { VENDOR_ID_HIGH_REGISTER = 0x80FE; VENDOR_ID_LOW_REGISTER = 0x00FE; FAN_PWM_OUT_REG = new ushort[] { 0x04A, 0x04B, 0x04C }; FAN_PWM_COMMAND_REG = new ushort[] { 0x119, 0x129, 0x139 }; FAN_CONTROL_MODE_REG = new ushort[] { 0x113, 0x123, 0x133 }; _vBatMonitorControlRegister = 0x0318; } else if (chip is Chip.NCT6687D or Chip.NCT6683D) { FAN_PWM_OUT_REG = new ushort[] { 0x160, 0x161, 0x162, 0x163, 0x164, 0x165, 0x166, 0x167 }; FAN_PWM_COMMAND_REG = new ushort[] { 0xA28, 0xA29, 0xA2A, 0xA2B, 0xA2C, 0xA2D, 0xA2E, 0xA2F }; FAN_CONTROL_MODE_REG = new ushort[] { 0xA00, 0xA00, 0xA00, 0xA00, 0xA00, 0xA00, 0xA00, 0xA00 }; FAN_PWM_REQUEST_REG = new ushort[] { 0xA01, 0xA01, 0xA01, 0xA01, 0xA01, 0xA01, 0xA01, 0xA01 }; } else { VENDOR_ID_HIGH_REGISTER = 0x804F; VENDOR_ID_LOW_REGISTER = 0x004F; FAN_PWM_OUT_REG = chip is Chip.NCT6797D or Chip.NCT6798D ? new ushort[] { 0x001, 0x003, 0x011, 0x013, 0x015, 0xA09, 0xB09 } : new ushort[] { 0x001, 0x003, 0x011, 0x013, 0x015, 0x017, 0x029 }; FAN_PWM_COMMAND_REG = new ushort[] { 0x109, 0x209, 0x309, 0x809, 0x909, 0xA09, 0xB09 }; FAN_CONTROL_MODE_REG = new ushort[] { 0x102, 0x202, 0x302, 0x802, 0x902, 0xA02, 0xB02 }; _vBatMonitorControlRegister = 0x005D; } _isNuvotonVendor = IsNuvotonVendor(); if (!_isNuvotonVendor) return; switch (chip) { case Chip.NCT6771F: case Chip.NCT6776F: if (chip == Chip.NCT6771F) { Fans = new float?[4]; // min value RPM value with 16-bit fan counter _minFanRpm = (int)(1.35e6 / 0xFFFF); } else { Fans = new float?[5]; // min value RPM value with 13-bit fan counter _minFanRpm = (int)(1.35e6 / 0x1FFF); } _fanRpmRegister = new ushort[5]; for (int i = 0; i < _fanRpmRegister.Length; i++) _fanRpmRegister[i] = (ushort)(0x656 + (i << 1)); Controls = new float?[3]; Voltages = new float?[9]; _voltageRegisters = new ushort[] { 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x550, 0x551 }; _voltageVBatRegister = 0x551; _temperaturesSource = new TemperatureSourceData[] { new(chip == Chip.NCT6771F ? SourceNct6771F.PECI_0 : SourceNct6776F.PECI_0, 0x027, 0, -1, 0x621), new(chip == Chip.NCT6771F ? SourceNct6771F.CPUTIN : SourceNct6776F.CPUTIN, 0x073, 0x074, 7, 0x100), new(chip == Chip.NCT6771F ? SourceNct6771F.AUXTIN : SourceNct6776F.AUXTIN, 0x075, 0x076, 7, 0x200), new(chip == Chip.NCT6771F ? SourceNct6771F.SYSTIN : SourceNct6776F.SYSTIN, 0x077, 0x078, 7, 0x300), new(null, 0x150, 0x151, 7, 0x622), new(null, 0x250, 0x251, 7, 0x623), new(null, 0x62B, 0x62E, 0, 0x624), new(null, 0x62C, 0x62E, 1, 0x625), new(null, 0x62D, 0x62E, 2, 0x626) }; Temperatures = new float?[4]; break; case Chip.NCT6779D: case Chip.NCT6791D: case Chip.NCT6792D: case Chip.NCT6792DA: case Chip.NCT6793D: case Chip.NCT6795D: case Chip.NCT6796D: case Chip.NCT6796DR: case Chip.NCT6797D: case Chip.NCT6798D: switch (chip) { case Chip.NCT6779D: Fans = new float?[5]; Controls = new float?[5]; break; case Chip.NCT6796DR: case Chip.NCT6797D: case Chip.NCT6798D: Fans = new float?[7]; Controls = new float?[7]; break; default: Fans = new float?[6]; Controls = new float?[6]; break; } _fanCountRegister = new ushort[] { 0x4B0, 0x4B2, 0x4B4, 0x4B6, 0x4B8, 0x4BA, 0x4CC }; // max value for 13-bit fan counter _maxFanCount = 0x1FFF; // min value that could be transferred to 16-bit RPM registers _minFanCount = 0x15; Voltages = new float?[15]; _voltageRegisters = new ushort[] { 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488, 0x489, 0x48A, 0x48B, 0x48C, 0x48D, 0x48E }; _voltageVBatRegister = 0x488; var temperaturesSources = new List(); switch (chip) { case Chip.NCT6796D: case Chip.NCT6796DR: case Chip.NCT6797D: case Chip.NCT6798D: temperaturesSources.AddRange(new TemperatureSourceData[] { new(SourceNct67Xxd.PECI_0, 0x073, 0x074, 7, 0x100), new(SourceNct67Xxd.CPUTIN, 0x075, 0x076, 7, 0x200, 0x491), new(SourceNct67Xxd.SYSTIN, 0x077, 0x078, 7, 0x300, 0x490), new(SourceNct67Xxd.AUXTIN0, 0x079, 0x07A, 7, 0x800, 0x492), new(SourceNct67Xxd.AUXTIN1, 0x07B, 0x07C, 7, 0x900, 0x493), new(SourceNct67Xxd.AUXTIN2, 0x07D, 0x07E, 7, 0xA00, 0x494), new(SourceNct67Xxd.AUXTIN3, 0x4A0, 0x49E, 6, 0xB00, 0x495), new(SourceNct67Xxd.AUXTIN4, 0x027, 0, -1, 0x621), new(SourceNct67Xxd.SMBUSMASTER0, 0x150, 0x151, 7, 0x622), new(SourceNct67Xxd.SMBUSMASTER1, 0x670, 0, -1, 0xC26), new(SourceNct67Xxd.PECI_1, 0x672, 0, -1, 0xC27), new(SourceNct67Xxd.PCH_CHIP_CPU_MAX_TEMP, 0x674, 0, -1, 0xC28, 0x400), new(SourceNct67Xxd.PCH_CHIP_TEMP, 0x676, 0, -1, 0xC29, 0x401), new(SourceNct67Xxd.PCH_CPU_TEMP, 0x678, 0, -1, 0xC2A, 0x402), new(SourceNct67Xxd.PCH_MCH_TEMP, 0x67A, 0, -1, 0xC2B, 0x404), new(SourceNct67Xxd.AGENT0_DIMM0, 0), new(SourceNct67Xxd.AGENT0_DIMM1, 0), new(SourceNct67Xxd.AGENT1_DIMM0, 0), new(SourceNct67Xxd.AGENT1_DIMM1, 0), new(SourceNct67Xxd.BYTE_TEMP0, 0), new(SourceNct67Xxd.BYTE_TEMP1, 0), new(SourceNct67Xxd.PECI_0_CAL, 0), new(SourceNct67Xxd.PECI_1_CAL, 0), new(SourceNct67Xxd.VIRTUAL_TEMP, 0) }); break; default: temperaturesSources.AddRange(new TemperatureSourceData[] { new(SourceNct67Xxd.PECI_0, 0x027, 0, -1, 0x621), new(SourceNct67Xxd.CPUTIN, 0x073, 0x074, 7, 0x100, 0x491), new(SourceNct67Xxd.SYSTIN, 0x075, 0x076, 7, 0x200, 0x490), new(SourceNct67Xxd.AUXTIN0, 0x077, 0x078, 7, 0x300, 0x492), new(SourceNct67Xxd.AUXTIN1, 0x079, 0x07A, 7, 0x800, 0x493), new(SourceNct67Xxd.AUXTIN2, 0x07B, 0x07C, 7, 0x900, 0x494), new(SourceNct67Xxd.AUXTIN3, 0x150, 0x151, 7, 0x622, 0x495) }); break; } _temperaturesSource = temperaturesSources.ToArray(); Temperatures = new float?[_temperaturesSource.Length]; break; case Chip.NCT610XD: Fans = new float?[3]; Controls = new float?[3]; _fanRpmRegister = new ushort[3]; for (int i = 0; i < _fanRpmRegister.Length; i++) _fanRpmRegister[i] = (ushort)(0x030 + (i << 1)); // min value RPM value with 13-bit fan counter _minFanRpm = (int)(1.35e6 / 0x1FFF); Voltages = new float?[9]; _voltageRegisters = new ushort[] { 0x300, 0x301, 0x302, 0x303, 0x304, 0x305, 0x307, 0x308, 0x309 }; _voltageVBatRegister = 0x308; Temperatures = new float?[4]; _temperaturesSource = new TemperatureSourceData[] { new(SourceNct610X.PECI_0, 0x027, 0, -1, 0x621), new(SourceNct610X.SYSTIN, 0x018, 0x01B, 7, 0x100, 0x018), new(SourceNct610X.CPUTIN, 0x019, 0x11B, 7, 0x200, 0x019), new(SourceNct610X.AUXTIN, 0x01A, 0x21B, 7, 0x300, 0x01A) }; break; case Chip.NCT6683D: case Chip.NCT6687D: Fans = new float?[8]; Controls = new float?[8]; Voltages = new float?[14]; Temperatures = new float?[7]; // CPU // System // MOS // PCH // CPU Socket // PCIE_1 // M2_1 _temperaturesSource = new TemperatureSourceData[] { new(null, 0x100), new(null, 0x102), new(null, 0x104), new(null, 0x106), new(null, 0x108), new(null, 0x10A), new(null, 0x10C) }; // VIN0 +12V // VIN1 +5V // VIN2 VCore // VIN3 SIO // VIN4 DRAM // VIN5 CPU IO // VIN6 CPU SA // VIN7 SIO // 3VCC I/O +3.3 // SIO VTT // SIO VREF // SIO VSB // SIO AVSB // SIO VBAT _voltageRegisters = new ushort[] { 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x13A, 0x13E, 0x136, 0x138, 0x13C }; // CPU Fan // PUMP Fan // SYS Fan 1 // SYS Fan 2 // SYS Fan 3 // SYS Fan 4 // SYS Fan 5 // SYS Fan 6 _fanRpmRegister = new ushort[] { 0x140, 0x142, 0x144, 0x146, 0x148, 0x14A, 0x14C, 0x14E }; _restoreDefaultFanControlRequired = new bool[_fanRpmRegister.Length]; _initialFanControlMode = new byte[_fanRpmRegister.Length]; _initialFanPwmCommand = new byte[_fanRpmRegister.Length]; // initialize const ushort initRegister = 0x180; byte data = ReadByte(initRegister); if ((data & 0x80) == 0) { WriteByte(initRegister, (byte)(data | 0x80)); } // enable SIO voltage WriteByte(0x1BB, 0x61); WriteByte(0x1BC, 0x62); WriteByte(0x1BD, 0x63); WriteByte(0x1BE, 0x64); WriteByte(0x1BF, 0x65); break; } } public Chip Chip { get; } public float?[] Controls { get; } = Array.Empty(); public float?[] Fans { get; } = Array.Empty(); public float?[] Temperatures { get; } = Array.Empty(); public float?[] Voltages { get; } = Array.Empty(); public byte? ReadGpio(int index) { return null; } public void WriteGpio(int index, byte value) { } public void SetControl(int index, byte? value) { if (!_isNuvotonVendor) return; if (index < 0 || index >= Controls.Length) throw new ArgumentOutOfRangeException(nameof(index)); if (!Ring0.WaitIsaBusMutex(10)) return; if (value.HasValue) { SaveDefaultFanControl(index); if (Chip is not Chip.NCT6687D and not Chip.NCT6683D) { // set manual mode WriteByte(FAN_CONTROL_MODE_REG[index], 0); // set output value WriteByte(FAN_PWM_COMMAND_REG[index], value.Value); } else { // Manual mode, bit(1 : set, 0 : unset) // bit 0 : CPU Fan // bit 1 : PUMP Fan // bit 2 : SYS Fan 1 // bit 3 : SYS Fan 2 // bit 4 : SYS Fan 3 // bit 5 : SYS Fan 4 // bit 6 : SYS Fan 5 // bit 7 : SYS Fan 6 byte mode = ReadByte(FAN_CONTROL_MODE_REG[index]); byte bitMask = (byte)(0x01 << index); mode = (byte)(mode | bitMask); WriteByte(FAN_CONTROL_MODE_REG[index], mode); WriteByte(FAN_PWM_REQUEST_REG[index], 0x80); Thread.Sleep(50); WriteByte(FAN_PWM_COMMAND_REG[index], value.Value); WriteByte(FAN_PWM_REQUEST_REG[index], 0x40); Thread.Sleep(50); } } else { RestoreDefaultFanControl(index); } Ring0.ReleaseIsaBusMutex(); } public void Update() { if (!_isNuvotonVendor) return; if (!Ring0.WaitIsaBusMutex(10)) return; DisableIOSpaceLock(); for (int i = 0; i < Voltages.Length; i++) { if (Chip != Chip.NCT6687D) { float value = 0.008f * ReadByte(_voltageRegisters[i]); bool valid = value > 0; // check if battery voltage monitor is enabled if (valid && _voltageRegisters[i] == _voltageVBatRegister) valid = (ReadByte(_vBatMonitorControlRegister) & 0x01) > 0; Voltages[i] = valid ? value : null; } else { float value = 0.001f * ((16 * ReadByte(_voltageRegisters[i])) + (ReadByte((ushort)(_voltageRegisters[i] + 1)) >> 4)); Voltages[i] = i switch { // 12V 0 => value * 12.0f, // 5V 1 => value * 5.0f, // DRAM 4 => value * 2.0f, _ => value }; } } System.Diagnostics.Debug.WriteLine("Updating temperatures."); long temperatureSourceMask = 0; for (int i = 0; i < _temperaturesSource.Length; i++) { TemperatureSourceData ts = _temperaturesSource[i]; int value; SourceNct67Xxd source; float? temperature; switch (Chip) { case Chip.NCT6687D: case Chip.NCT6683D: value = (sbyte)ReadByte(ts.Register); int half = (ReadByte((ushort)(ts.Register + 1)) >> 7) & 0x1; Temperatures[i] = value + (0.5f * half); break; case Chip.NCT6796D: case Chip.NCT6796DR: case Chip.NCT6797D: case Chip.NCT6798D: if (_temperaturesSource[i].Register == 0) { System.Diagnostics.Debug.WriteLine("Temperature register {0} skipped, address 0.", i); continue; } value = (sbyte)ReadByte(_temperaturesSource[i].Register) << 1; System.Diagnostics.Debug.WriteLine("Temperature register {0} at 0x{1:X3} value (integer): {2}/2", i, ts.Register, value); if (_temperaturesSource[i].HalfBit > 0) { value |= (ReadByte(_temperaturesSource[i].HalfRegister) >> ts.HalfBit) & 0x1; System.Diagnostics.Debug.WriteLine("Temperature register {0} value updated from 0x{1:X3} (fractional): {2}/2", i, ts.HalfRegister, value); } if (ts.SourceRegister > 0) { source = (SourceNct67Xxd)(ReadByte(ts.SourceRegister) & 0x1F); System.Diagnostics.Debug.WriteLine("Temperature register {0} source at 0x{1:X3}: {2:G} ({2:D})", i, ts.SourceRegister, source); } else { source = (SourceNct67Xxd)ts.Source; System.Diagnostics.Debug.WriteLine("Temperature register {0} source register is 0, source set to: {1:G} ({1:D})", i, source); } // Skip reading when already filled, because later values are without fractional if ((temperatureSourceMask & (1L << (byte)source)) > 0) { System.Diagnostics.Debug.WriteLine("Temperature register {0} discarded, because source seen before.", i); continue; } temperature = 0.5f * value; System.Diagnostics.Debug.WriteLine("Temperature register {0} final temperature: {1}.", i, temperature); if (temperature is > 125 or < -55) { temperature = null; System.Diagnostics.Debug.WriteLine("Temperature register {0} discarded: Out of range.", i); } else { temperatureSourceMask |= 1L << (byte)source; System.Diagnostics.Debug.WriteLine("Temperature register {0} accepted.", i); } for (int j = 0; j < Temperatures.Length; j++) { if ((SourceNct67Xxd)_temperaturesSource[j].Source == source) { Temperatures[j] = temperature; System.Diagnostics.Debug.WriteLine("Temperature register {0}, value from source {1:G} ({1:D}), written at position {2}.", i, _temperaturesSource[j].Source, j); } } break; default: value = (sbyte)ReadByte(ts.Register) << 1; if (ts.HalfBit > 0) { value |= (ReadByte(ts.HalfRegister) >> ts.HalfBit) & 0x1; } source = (SourceNct67Xxd)ReadByte(ts.SourceRegister); temperatureSourceMask |= 1L << (byte)source; temperature = 0.5f * value; if (temperature is > 125 or < -55) temperature = null; for (int j = 0; j < Temperatures.Length; j++) { if ((SourceNct67Xxd)_temperaturesSource[j].Source == source) Temperatures[j] = temperature; } break; } } for (int i = 0; i < _temperaturesSource.Length; i++) { TemperatureSourceData ts = _temperaturesSource[i]; if (!ts.AlternateRegister.HasValue) { System.Diagnostics.Debug.WriteLine("Alternate temperature register for temperature {0}, {1:G} ({1:D}), skipped, because address is null.", i, ts.Source); continue; } if ((temperatureSourceMask & (1L << (byte)(SourceNct67Xxd)ts.Source)) > 0) { System.Diagnostics.Debug.WriteLine("Alternate temperature register for temperature {0}, {1:G} ({1:D}), at 0x{2:X3} skipped, because value already set.", i, ts.Source, ts.AlternateRegister.Value); continue; } float? temperature = (sbyte)ReadByte(ts.AlternateRegister.Value); System.Diagnostics.Debug.WriteLine("Alternate temperature register for temperature {0}, {1:G} ({1:D}), at 0x{2:X3} final temperature: {3}.", i, ts.Source, ts.AlternateRegister.Value, temperature); if (temperature is > 125 or <= 0) { temperature = null; System.Diagnostics.Debug.WriteLine("Alternate Temperature register for temperature {0}, {1:G} ({1:D}), discarded: Out of range.", i, ts.Source); } Temperatures[i] = temperature; } for (int i = 0; i < Fans.Length; i++) { if (Chip is not Chip.NCT6687D and not Chip.NCT6683D) { if (_fanCountRegister != null) { byte high = ReadByte(_fanCountRegister[i]); byte low = ReadByte((ushort)(_fanCountRegister[i] + 1)); int count = (high << 5) | (low & 0x1F); if (count < _maxFanCount) { if (count >= _minFanCount) { Fans[i] = 1.35e6f / count; } else { Fans[i] = null; } } else { Fans[i] = 0; } } else { byte high = ReadByte(_fanRpmRegister[i]); byte low = ReadByte((ushort)(_fanRpmRegister[i] + 1)); int value = (high << 8) | low; Fans[i] = value > _minFanRpm ? value : 0; } } else { Fans[i] = (ReadByte(_fanRpmRegister[i]) << 8) | ReadByte((ushort)(_fanRpmRegister[i] + 1)); } } for (int i = 0; i < Controls.Length; i++) { if (Chip is not Chip.NCT6687D and not Chip.NCT6683D) { int value = ReadByte(FAN_PWM_OUT_REG[i]); Controls[i] = value / 2.55f; } else { int value = ReadByte(FAN_PWM_OUT_REG[i]); Controls[i] = (float)Math.Round(value / 2.55f); } } Ring0.ReleaseIsaBusMutex(); } public string GetReport() { StringBuilder r = new(); r.AppendLine("LPC " + GetType().Name); r.AppendLine(); r.Append("Chip Id: 0x"); r.AppendLine(Chip.ToString("X")); r.Append("Chip Revision: 0x"); r.AppendLine(_revision.ToString("X", CultureInfo.InvariantCulture)); r.Append("Base Address: 0x"); r.AppendLine(_port.ToString("X4", CultureInfo.InvariantCulture)); r.AppendLine(); if (!Ring0.WaitIsaBusMutex(100)) return r.ToString(); ushort[] addresses = { 0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070, 0x0F0, 0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 0x200, 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x300, 0x320, 0x330, 0x340, 0x360, 0x400, 0x410, 0x420, 0x440, 0x450, 0x460, 0x480, 0x490, 0x4B0, 0x4C0, 0x4F0, 0x500, 0x550, 0x560, 0x600, 0x610, 0x620, 0x630, 0x640, 0x650, 0x660, 0x670, 0x700, 0x710, 0x720, 0x730, 0x800, 0x820, 0x830, 0x840, 0x900, 0x920, 0x930, 0x940, 0x960, 0xA00, 0xA10, 0xA20, 0xA30, 0xA40, 0xA50, 0xA60, 0xA70, 0xB00, 0xB10, 0xB20, 0xB30, 0xB50, 0xB60, 0xB70, 0xC00, 0xC10, 0xC20, 0xC30, 0xC50, 0xC60, 0xC70, 0xD00, 0xD10, 0xD20, 0xD30, 0xD50, 0xD60, 0xE00, 0xE10, 0xE20, 0xE30, 0xF00, 0xF10, 0xF20, 0xF30, 0x8040, 0x80F0 }; r.AppendLine("Hardware Monitor Registers"); r.AppendLine(); r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"); r.AppendLine(); if (Chip is not Chip.NCT6687D and not Chip.NCT6683D) { foreach (ushort address in addresses) { r.Append(" "); r.Append(address.ToString("X4", CultureInfo.InvariantCulture)); r.Append(" "); for (ushort j = 0; j <= 0xF; j++) { r.Append(" "); r.Append(ReadByte((ushort)(address | j)).ToString("X2", CultureInfo.InvariantCulture)); } r.AppendLine(); } } else { for (int i = 0; i <= 0xFF; i++) { r.Append(" "); r.Append((i << 4).ToString("X4", CultureInfo.InvariantCulture)); r.Append(" "); for (int j = 0; j <= 0xF; j++) { ushort address = (ushort)(i << 4 | j); r.Append(" "); r.Append(ReadByte(address).ToString("X2", CultureInfo.InvariantCulture)); } r.AppendLine(); } } r.AppendLine(); Ring0.ReleaseIsaBusMutex(); return r.ToString(); } private byte ReadByte(ushort address) { if (Chip is not Chip.NCT6687D and not Chip.NCT6683D) { byte bank = (byte)(address >> 8); byte register = (byte)(address & 0xFF); Ring0.WriteIoPort(_port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER); Ring0.WriteIoPort(_port + DATA_REGISTER_OFFSET, bank); Ring0.WriteIoPort(_port + ADDRESS_REGISTER_OFFSET, register); return Ring0.ReadIoPort(_port + DATA_REGISTER_OFFSET); } byte page = (byte)(address >> 8); byte index = (byte)(address & 0xFF); Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, EC_SPACE_PAGE_SELECT); Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, page); Ring0.WriteIoPort(_port + EC_SPACE_INDEX_REGISTER_OFFSET, index); return Ring0.ReadIoPort(_port + EC_SPACE_DATA_REGISTER_OFFSET); } private void WriteByte(ushort address, byte value) { if (Chip is not Chip.NCT6687D and not Chip.NCT6683D) { byte bank = (byte)(address >> 8); byte register = (byte)(address & 0xFF); Ring0.WriteIoPort(_port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER); Ring0.WriteIoPort(_port + DATA_REGISTER_OFFSET, bank); Ring0.WriteIoPort(_port + ADDRESS_REGISTER_OFFSET, register); Ring0.WriteIoPort(_port + DATA_REGISTER_OFFSET, value); } else { byte page = (byte)(address >> 8); byte index = (byte)(address & 0xFF); Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, EC_SPACE_PAGE_SELECT); Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, page); Ring0.WriteIoPort(_port + EC_SPACE_INDEX_REGISTER_OFFSET, index); Ring0.WriteIoPort(_port + EC_SPACE_DATA_REGISTER_OFFSET, value); } } private bool IsNuvotonVendor() { return Chip == Chip.NCT6687D || Chip == Chip.NCT6683D || ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) | ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID; } private void SaveDefaultFanControl(int index) { if (!_restoreDefaultFanControlRequired[index]) { if (Chip is not Chip.NCT6687D and not Chip.NCT6683D) { _initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]); } else { byte mode = ReadByte(FAN_CONTROL_MODE_REG[index]); byte bitMask = (byte)(0x01 << index); _initialFanControlMode[index] = (byte)(mode & bitMask); } _initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]); _restoreDefaultFanControlRequired[index] = true; } } private void RestoreDefaultFanControl(int index) { if (_restoreDefaultFanControlRequired[index]) { if (Chip is not Chip.NCT6687D and not Chip.NCT6683D) { WriteByte(FAN_CONTROL_MODE_REG[index], _initialFanControlMode[index]); WriteByte(FAN_PWM_COMMAND_REG[index], _initialFanPwmCommand[index]); } else { byte mode = ReadByte(FAN_CONTROL_MODE_REG[index]); mode = (byte)(mode & ~_initialFanControlMode[index]); WriteByte(FAN_CONTROL_MODE_REG[index], mode); WriteByte(FAN_PWM_REQUEST_REG[index], 0x80); Thread.Sleep(50); WriteByte(FAN_PWM_COMMAND_REG[index], _initialFanPwmCommand[index]); WriteByte(FAN_PWM_REQUEST_REG[index], 0x40); Thread.Sleep(50); } _restoreDefaultFanControlRequired[index] = false; } } private void DisableIOSpaceLock() { if (Chip is not Chip.NCT6791D and not Chip.NCT6792D and not Chip.NCT6792DA and not Chip.NCT6793D and not Chip.NCT6795D and not Chip.NCT6796D and not Chip.NCT6796DR and not Chip.NCT6797D and not Chip.NCT6798D) { return; } // the lock is disabled already if the vendor ID can be read if (IsNuvotonVendor()) return; _lpcPort.WinbondNuvotonFintekEnter(); _lpcPort.NuvotonDisableIOSpaceLock(); _lpcPort.WinbondNuvotonFintekExit(); } [SuppressMessage("ReSharper", "InconsistentNaming")] private enum SourceNct6771F : byte { SYSTIN = 1, CPUTIN = 2, AUXTIN = 3, PECI_0 = 5 } [SuppressMessage("ReSharper", "InconsistentNaming")] private enum SourceNct6776F : byte { SYSTIN = 1, CPUTIN = 2, AUXTIN = 3, PECI_0 = 12 } [SuppressMessage("ReSharper", "InconsistentNaming")] private enum SourceNct67Xxd : byte { SYSTIN = 1, CPUTIN = 2, AUXTIN0 = 3, AUXTIN1 = 4, AUXTIN2 = 5, AUXTIN3 = 6, AUXTIN4 = 7, SMBUSMASTER0 = 8, SMBUSMASTER1 = 9, PECI_0 = 16, PECI_1 = 17, PCH_CHIP_CPU_MAX_TEMP = 18, PCH_CHIP_TEMP = 19, PCH_CPU_TEMP = 20, PCH_MCH_TEMP = 21, AGENT0_DIMM0 = 22, AGENT0_DIMM1 = 23, AGENT1_DIMM0 = 24, AGENT1_DIMM1 = 25, BYTE_TEMP0 = 26, BYTE_TEMP1 = 27, PECI_0_CAL = 28, PECI_1_CAL = 29, VIRTUAL_TEMP = 31 } [SuppressMessage("ReSharper", "InconsistentNaming")] private enum SourceNct610X : byte { SYSTIN = 1, CPUTIN = 2, AUXTIN = 3, PECI_0 = 12 } // ReSharper disable InconsistentNaming private const uint ADDRESS_REGISTER_OFFSET = 0x05; private const byte BANK_SELECT_REGISTER = 0x4E; private const uint DATA_REGISTER_OFFSET = 0x06; // NCT668X private const uint EC_SPACE_PAGE_REGISTER_OFFSET = 0x04; private const uint EC_SPACE_INDEX_REGISTER_OFFSET = 0x05; private const uint EC_SPACE_DATA_REGISTER_OFFSET = 0x06; private const byte EC_SPACE_PAGE_SELECT = 0xFF; private const ushort NUVOTON_VENDOR_ID = 0x5CA3; private readonly ushort[] FAN_CONTROL_MODE_REG; private readonly ushort[] FAN_PWM_COMMAND_REG; private readonly ushort[] FAN_PWM_OUT_REG; private readonly ushort[] FAN_PWM_REQUEST_REG; private readonly ushort VENDOR_ID_HIGH_REGISTER; private readonly ushort VENDOR_ID_LOW_REGISTER; // ReSharper restore InconsistentNaming } }