From c7e36ad96cfae3650917005ccc6be60ab294005a Mon Sep 17 00:00:00 2001 From: Alcolawl Date: Thu, 19 Dec 2024 21:50:23 -0500 Subject: [PATCH] Add register arrays and fan control logic for Nuvoton NCT6687D-R Note that this controller exists, but is strictly in use for the MSI X870 Tomahawk WiFi at the time of this commit --- .../Hardware/Motherboard/Lpc/Nct677X.cs | 218 +++++++++++++----- 1 file changed, 160 insertions(+), 58 deletions(-) diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/Nct677X.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/Nct677X.cs index ee0204dd0..1c4c78e84 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/Nct677X.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/Nct677X.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; +//using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; @@ -76,6 +77,13 @@ public Nct677X(Chip chip, byte revision, ushort port, LpcPort lpcPort) 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 if (chip is Chip.NCT6687DR) // MSI MAG X870 Tomahawk WiFi + { + FAN_PWM_OUT_REG = new ushort[] { 0x160, 0x161, 0xE05, 0xE04, 0xE03, 0xE02, 0xE01, 0xE00 }; // PWM Signal % Sensors CONFIRMED + FAN_PWM_COMMAND_REG = new ushort[] { 0xA28, 0xA29, 0xC70, 0xC58, 0xC40, 0xC28, 0xC10, 0xBF8 }; // Control Registers for CPU/Pump, Initial Fan Curve Registers for System Fans + 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; @@ -333,6 +341,75 @@ public Nct677X(Chip chip, byte revision, ushort port, LpcPort lpcPort) WriteByte(0x1BE, 0x64); WriteByte(0x1BF, 0x65); break; + + case Chip.NCT6687DR: + 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 0x140 + // PUMP Fan 0x142 + // SYS Fan 1 0x15E + // SYS Fan 2 0x15C + // SYS Fan 3 0x15A + // SYS Fan 4 0x158 + // SYS Fan 5 0x156 + // SYS Fan 6 0x154 + _fanRpmRegister = new ushort[] { 0x140, 0x142, 0x15E, 0x15C, 0x15A, 0x158, 0x156, 0x154 }; //RPM reporting good + + _restoreDefaultFanControlRequired = new bool[_fanRpmRegister.Length]; + _initialFanControlMode = new byte[_fanRpmRegister.Length]; + _initialFanPwmCommand = new byte[_fanRpmRegister.Length]; + + // initialize + const ushort initRegisterNct6687DR = 0x180; + byte dataNct6687DR = ReadByte(initRegisterNct6687DR); + if ((dataNct6687DR & 0x80) == 0) + { + WriteByte(initRegisterNct6687DR, (byte)(dataNct6687DR | 0x80)); + } + + // enable SIO voltage -- address, value + WriteByte(0x1BB, 0x61); + WriteByte(0x1BC, 0x62); + WriteByte(0x1BD, 0x63); + WriteByte(0x1BE, 0x64); + WriteByte(0x1BF, 0x65); + break; } } @@ -369,7 +446,7 @@ public void SetControl(int index, byte? value) { SaveDefaultFanControl(index); - if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D and not Chip.NCT6687DR) { // set manual mode WriteByte(FAN_CONTROL_MODE_REG[index], 0); @@ -394,11 +471,35 @@ public void SetControl(int index, byte? value) mode = (byte)(mode | bitMask); WriteByte(FAN_CONTROL_MODE_REG[index], mode); - WriteByte(FAN_PWM_REQUEST_REG[index], 0x80); + WriteByte(FAN_PWM_REQUEST_REG[index], 0x80); // Requests for PWM signal change Thread.Sleep(50); + + if (Chip is Chip.NCT6687DR){ // for MSI MAG X870 Tomahawk WiFi testing + if (index > 1){ // System Fan Control + + int initFanCurveReg = FAN_PWM_COMMAND_REG[index]; // Initial Register Address for the Fan Curve + int targetFanCurveAddr = initFanCurveReg; // Current Fan Curve Register Address we're writing to + ushort targetFanCurveReg = 0; // Integer value of the current fan curve register address, not the value within + int count = 0; // Countin variable because I'm dumb and can't get FOR loops to work + + // Write fan curve + do{ + targetFanCurveAddr = initFanCurveReg+count; + targetFanCurveReg = Convert.ToUInt16(targetFanCurveAddr); + WriteByte(targetFanCurveReg, value.Value); + count = count+2; + } + while (count < 14); + } + else{ // Control CPU and Pump Fan normally + WriteByte(FAN_PWM_COMMAND_REG[index], value.Value); + } + } + else{ // All other motherboards that use NCT6683/6686/6687 + WriteByte(FAN_PWM_COMMAND_REG[index], value.Value); + } - WriteByte(FAN_PWM_COMMAND_REG[index], value.Value); - WriteByte(FAN_PWM_REQUEST_REG[index], 0x40); + WriteByte(FAN_PWM_REQUEST_REG[index], 0x40); // Finished with request Thread.Sleep(50); } } @@ -422,7 +523,7 @@ public void Update() for (int i = 0; i < Voltages.Length; i++) { - if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D and not Chip.NCT6687DR) { float value = 0.008f * ReadByte(_voltageRegisters[i]); bool valid = value > 0; @@ -462,6 +563,7 @@ public void Update() switch (Chip) { case Chip.NCT6687D: + case Chip.NCT6687DR: case Chip.NCT6686D: case Chip.NCT6683D: value = (sbyte)ReadByte(ts.Register); @@ -581,7 +683,7 @@ public void Update() for (int i = 0; i < Fans.Length; i++) { - if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D and not Chip.NCT6687DR) { if (_fanCountRegister != null) { @@ -622,7 +724,7 @@ public void Update() for (int i = 0; i < Controls.Length; i++) { - if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6686D and not Chip.NCT6687DR) { int value = ReadByte(FAN_PWM_OUT_REG[i]); Controls[i] = value / 2.55f; @@ -763,7 +865,7 @@ public string GetReport() 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.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D and not Chip.NCT6687DR) { foreach (ushort address in addresses) { @@ -806,7 +908,7 @@ public string GetReport() private byte ReadByte(ushort address) { - if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D and not Chip.NCT6687DR) { byte bank = (byte)(address >> 8); byte register = (byte)(address & 0xFF); @@ -817,33 +919,33 @@ private byte ReadByte(ushort address) } byte page = (byte)(address >> 8); - byte index = (byte)(address & 0xFF); - - //wait for access, access == EC_SPACE_PAGE_SELECT - //timeout: after 500ms, abort and force access - byte access; - - DateTime timeout = DateTime.UtcNow.AddMilliseconds(500); - while (true) - { - access = Ring0.ReadIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET); - if (access == EC_SPACE_PAGE_SELECT || DateTime.UtcNow > timeout) - break; - - System.Threading.Thread.Sleep(1); - } - - if (access != EC_SPACE_PAGE_SELECT) - { - // Failed to gain access: force register access - Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, EC_SPACE_PAGE_SELECT); - } - + byte index = (byte)(address & 0xFF); + + //wait for access, access == EC_SPACE_PAGE_SELECT + //timeout: after 500ms, abort and force access + byte access; + + DateTime timeout = DateTime.UtcNow.AddMilliseconds(500); + while (true) + { + access = Ring0.ReadIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET); + if (access == EC_SPACE_PAGE_SELECT || DateTime.UtcNow > timeout) + break; + + System.Threading.Thread.Sleep(1); + } + + if (access != EC_SPACE_PAGE_SELECT) + { + // Failed to gain access: force register access + 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); - byte result = Ring0.ReadIoPort(_port + EC_SPACE_DATA_REGISTER_OFFSET); - - //free access for other instances + byte result = Ring0.ReadIoPort(_port + EC_SPACE_DATA_REGISTER_OFFSET); + + //free access for other instances Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, EC_SPACE_PAGE_SELECT); return result; @@ -851,7 +953,7 @@ private byte ReadByte(ushort address) private void WriteByte(ushort address, byte value) { - if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D and not Chip.NCT6687DR) { byte bank = (byte)(address >> 8); byte register = (byte)(address & 0xFF); @@ -863,26 +965,26 @@ private void WriteByte(ushort address, byte value) else { byte page = (byte)(address >> 8); - byte index = (byte)(address & 0xFF); - - //wait for access, access == EC_SPACE_PAGE_SELECT - //timeout: after 500ms, abort and force access - byte access; - - DateTime timeout = DateTime.UtcNow.AddMilliseconds(500); - while (true) - { - access = Ring0.ReadIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET); - if (access == EC_SPACE_PAGE_SELECT || DateTime.UtcNow > timeout) - break; - - System.Threading.Thread.Sleep(1); - } - - if (access != EC_SPACE_PAGE_SELECT) - { - // Failed to gain access: force register access - Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, EC_SPACE_PAGE_SELECT); + byte index = (byte)(address & 0xFF); + + //wait for access, access == EC_SPACE_PAGE_SELECT + //timeout: after 500ms, abort and force access + byte access; + + DateTime timeout = DateTime.UtcNow.AddMilliseconds(500); + while (true) + { + access = Ring0.ReadIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET); + if (access == EC_SPACE_PAGE_SELECT || DateTime.UtcNow > timeout) + break; + + System.Threading.Thread.Sleep(1); + } + + if (access != EC_SPACE_PAGE_SELECT) + { + // Failed to gain access: force register access + Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, EC_SPACE_PAGE_SELECT); } Ring0.WriteIoPort(_port + EC_SPACE_PAGE_REGISTER_OFFSET, page); @@ -896,14 +998,14 @@ private void WriteByte(ushort address, byte value) private bool IsNuvotonVendor() { - return Chip is Chip.NCT6683D or Chip.NCT6686D or Chip.NCT6687D || ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) | ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID; + return Chip is Chip.NCT6683D or Chip.NCT6686D or Chip.NCT6687D or Chip.NCT6687DR || ((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.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D and not Chip.NCT6687DR) { _initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]); } @@ -923,7 +1025,7 @@ private void RestoreDefaultFanControl(int index) { if (_restoreDefaultFanControlRequired[index]) { - if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D) + if (Chip is not Chip.NCT6683D and not Chip.NCT6686D and not Chip.NCT6687D and not Chip.NCT6687DR) { WriteByte(FAN_CONTROL_MODE_REG[index], _initialFanControlMode[index]); WriteByte(FAN_PWM_COMMAND_REG[index], _initialFanPwmCommand[index]);