Skip to content

Commit

Permalink
Add register arrays and fan control logic for Nuvoton NCT6687D-R
Browse files Browse the repository at this point in the history
Note that this controller exists, but is strictly in use for the MSI X870 Tomahawk WiFi at the time of this commit
  • Loading branch information
Alcolawl committed Dec 20, 2024
1 parent ed6c8e4 commit c7e36ad
Showing 1 changed file with 160 additions and 58 deletions.
218 changes: 160 additions & 58 deletions LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/Nct677X.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
}
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand All @@ -817,41 +919,41 @@ 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;
}

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);
Expand All @@ -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);
Expand All @@ -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]);
}
Expand All @@ -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]);
Expand Down

0 comments on commit c7e36ad

Please sign in to comment.