Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing view for IT8792E and allow fan control for IT8792E on some Gigabyte AMD boards #1313

Merged
merged 12 commits into from
Jun 11, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.

using System;
using System.Threading;

namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc;

internal class EcioPortGigabyteController : IGigabyteController
{
private const ushort ControllerVersionOffset = 0x00;
private const ushort ControllerEnableRegister = 0x47;
private const ushort ControllerFanControlArea = 0x900;

private const ushort EcioRegisterPort = 0x3F4;
private const ushort EcioValuePort = 0x3F0;

private readonly IT879xEcioPort _port;

private bool? _initialState;

private EcioPortGigabyteController(IT879xEcioPort port)
{
_port = port;
}

public static EcioPortGigabyteController TryCreate()
{
IT879xEcioPort port = new(EcioRegisterPort, EcioValuePort);

// Check compatibility by querying its version.
if (!port.Read(ControllerFanControlArea + ControllerVersionOffset, out byte majorVersion) || majorVersion != 1)
return null;

return new EcioPortGigabyteController(port);
}

public bool Enable(bool enabled)
{
ushort offset = ControllerFanControlArea + ControllerEnableRegister;

if (!_port.Read(offset, out byte bCurrent))
return false;

bool current = Convert.ToBoolean(bCurrent);

_initialState ??= current;

if (current != enabled)
{
if (!_port.Write(offset, (byte)(enabled ? 1 : 0)))
return false;

// Allow the system to catch up.
Thread.Sleep(250);
}

return true;
}

public void Restore()
{
if (_initialState.HasValue)
Enable(_initialState.Value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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 <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.

namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc;

internal interface IGigabyteController
{
bool Enable(bool enabled);

void Restore();
}
112 changes: 112 additions & 0 deletions LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT879xEcioPort.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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 <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.

using System.Diagnostics;
using System.Threading;

namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc;

internal class IT879xEcioPort
{
public IT879xEcioPort(ushort registerPort, ushort valuePort)
{
RegisterPort = registerPort;
ValuePort = valuePort;
}

public ushort RegisterPort { get; }

public ushort ValuePort { get; }

public bool Read(ushort offset, out byte value)
{
value = 0;

if (!WriteToRegister(0xB0) ||
!WriteToValue((byte)((offset >> 8) & 0xFF)) ||
!WriteToValue((byte)(offset & 0xFF)))
return false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow code style in this file (add braces, new lines, etc).


return ReadFromValue(out value);
}

public bool Write(ushort offset, byte value)
{
if (!WriteToRegister(0xB1) ||
!WriteToValue((byte)((offset >> 8) & 0xFF)) ||
!WriteToValue((byte)(offset & 0xFF)))
return false;

return WriteToValue(value);
}

private bool WriteToRegister(byte value)
{
if (!WaitIBE())
return false;
Ring0.WriteIoPort(RegisterPort, value);
return WaitIBE();
}

private bool WriteToValue(byte value)
{
if (!WaitIBE())
return false;
Ring0.WriteIoPort(ValuePort, value);
return WaitIBE();
}

private bool ReadFromValue(out byte value)
{
value = 0;
if (!WaitOBF())
return false;
value = Ring0.ReadIoPort(ValuePort);
return true;
}

private bool WaitIBE()
{
Stopwatch stopwatch = Stopwatch.StartNew();
try
{
while ((Ring0.ReadIoPort(RegisterPort) & 2) != 0)
{
if (stopwatch.ElapsedMilliseconds > WAIT_TIMEOUT)
return false;

Thread.Sleep(1);
}
return true;
}
finally
{
stopwatch.Stop();
}
}

private bool WaitOBF()
{
Stopwatch stopwatch = Stopwatch.StartNew();
try
{
while ((Ring0.ReadIoPort(RegisterPort) & 1) == 0)
{
if (stopwatch.ElapsedMilliseconds > WAIT_TIMEOUT)
return false;

Thread.Sleep(1);
}
return true;
}
finally
{
stopwatch.Stop();
}
}

private const long WAIT_TIMEOUT = 1000L;
}
7 changes: 4 additions & 3 deletions LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT87XX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ internal class IT87XX : ISuperIO
private readonly bool[] _restoreDefaultFanPwmControlRequired = new bool[MaxFanHeaders];
private readonly byte _version;
private readonly float _voltageGain;
private GigabyteController _gigabyteController;
private IGigabyteController _gigabyteController;

private bool SupportsMultipleBanks => _bankCount > 1;

public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version, Motherboard motherboard, GigabyteController gigabyteController)
public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version, Motherboard motherboard, IGigabyteController gigabyteController)
{
_address = address;
_version = version;
Expand Down Expand Up @@ -72,7 +72,8 @@ public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version, Mothe

FAN_PWM_CTRL_REG = chip switch
{
Chip.IT8665E or Chip.IT8625E =>new byte[] { 0x15, 0x16, 0x17, 0x1e, 0x1f, 0x92 },
Chip.IT8665E or Chip.IT8625E => new byte[] { 0x15, 0x16, 0x17, 0x1e, 0x1f, 0x92 },
Chip.IT8792E => new byte[] { 0x15, 0x16, 0x17 },
_ => new byte[] { 0x15, 0x16, 0x17, 0x7f, 0xa7, 0xaf }
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc;
/// It can be accessed by using memory mapped IO, mapping its internal RAM onto main RAM via the ISA Bridge.
/// This class can disable it so that the regular IT87XX code can drive the fans.
/// </summary>
internal class GigabyteController
internal class IsaBridgeGigabyteController : IGigabyteController
{
private const uint ControllerAddressRange = 0xFF;
private const int ControllerEnableRegister = 0x47;
Expand All @@ -32,7 +32,7 @@ internal class GigabyteController

private bool? _initialState;

public GigabyteController(uint address, Vendor vendor)
public IsaBridgeGigabyteController(uint address, Vendor vendor)
{
_controllerBaseAddress = address;
_vendor = vendor;
Expand Down Expand Up @@ -124,7 +124,7 @@ private bool Enable(bool enabled, IntPtr pciMmIoBaseAddress)
{
Marshal.WriteByte(mapped, ControllerEnableRegister, Convert.ToByte(enabled));
// Give it some time to see the change
Thread.Sleep(200);
Thread.Sleep(250);
}

InpOut.UnmapMemory(handle, mapped);
Expand Down
65 changes: 45 additions & 20 deletions LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcIO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,9 +536,15 @@ private bool DetectIT87(LpcPort port, Motherboard motherboard)
if (port.RegisterPort is not 0x2E and not 0x4E)
return false;

port.IT87Enter();

ushort chipId = port.ReadWord(CHIP_ID_REGISTER);
// Read the chip ID before entering.
// If already entered (not 0xFFFF) and the register port is 0x4E, it is most likely bugged and should be left alone.
// Entering IT8792 in this state will result in IT8792 reporting with chip ID of 0x8883.
if (port.RegisterPort != 0x4E || !port.TryReadWord(CHIP_ID_REGISTER, out ushort chipId))
{
port.IT87Enter();
chipId = port.ReadWord(CHIP_ID_REGISTER);
}

Chip chip = chipId switch
{
0x8613 => Chip.IT8613E,
Expand Down Expand Up @@ -604,7 +610,7 @@ private bool DetectIT87(LpcPort port, Motherboard motherboard)
gpioVerify = port.ReadWord(BASE_ADDRESS_REGISTER + 2);
}

GigabyteController gigabyteController = FindGigabyteEC(port, chip, motherboard);
IGigabyteController gigabyteController = FindGigabyteEC(port, chip, motherboard);

port.IT87Exit();

Expand Down Expand Up @@ -637,14 +643,45 @@ private bool DetectIT87(LpcPort port, Motherboard motherboard)
return false;
}

private GigabyteController FindGigabyteEC(LpcPort port, Chip chip, Motherboard motherboard)
private IGigabyteController FindGigabyteEC(LpcPort port, Chip chip, Motherboard motherboard)
{
// The controller only affects the 2nd ITE chip if present, and only a few
// models are known to use this controller.
// IT8795E likely to need this too, but may use different registers.
if (motherboard.Manufacturer != Manufacturer.Gigabyte || port.RegisterPort != 0x4E || chip is not (Chip.IT8790E or Chip.IT8792E or Chip.IT87952E))
return null;
return null;

Vendor vendor = DetectVendor();

IGigabyteController gigabyteController = FindGigabyteECUsingSmfi(port, chip, vendor);
if (gigabyteController != null)
return gigabyteController;

// ECIO is only available on AMD motherboards with IT8791E/IT8792E/IT8795E.
if (chip == Chip.IT8792E && vendor == Vendor.AMD)
{
gigabyteController = EcioPortGigabyteController.TryCreate();
if (gigabyteController != null)
return gigabyteController;
}

return null;

Vendor DetectVendor()
{
string manufacturer = motherboard.SMBios.Processors[0].ManufacturerName;
if (manufacturer.IndexOf("Intel", StringComparison.OrdinalIgnoreCase) != -1)
return Vendor.Intel;

if (manufacturer.IndexOf("Advanced Micro Devices", StringComparison.OrdinalIgnoreCase) != -1 || manufacturer.StartsWith("AMD", StringComparison.OrdinalIgnoreCase))
return Vendor.AMD;

return Vendor.Unknown;
}
}

private IGigabyteController FindGigabyteECUsingSmfi(LpcPort port, Chip chip, Vendor vendor)
{
port.Select(IT87XX_SMFI_LDN);

// Check if the SMFI logical device is enabled
Expand All @@ -662,7 +699,7 @@ private GigabyteController FindGigabyteEC(LpcPort port, Chip chip, Motherboard m
uint address = port.ReadWord(IT87_SMFI_HLPC_RAM_BASE_ADDRESS_REGISTER);
if (chip == Chip.IT87952E)
addressHi = port.ReadByte(IT87_SMFI_HLPC_RAM_BASE_ADDRESS_REGISTER_HIGH);

Thread.Sleep(1);
uint addressVerify = port.ReadWord(IT87_SMFI_HLPC_RAM_BASE_ADDRESS_REGISTER);
if (chip == Chip.IT87952E)
Expand All @@ -681,19 +718,7 @@ private GigabyteController FindGigabyteEC(LpcPort port, Chip chip, Motherboard m

hostAddress |= (address & 0xF000) | ((address & 0xFF) << 16) | ((addressHi & 0xF) << 24);

return new GigabyteController(hostAddress, DetectVendor());

Vendor DetectVendor()
{
string manufacturer = motherboard.SMBios.Processors[0].ManufacturerName;
if (manufacturer.IndexOf("Intel", StringComparison.OrdinalIgnoreCase) != -1)
return Vendor.Intel;

if (manufacturer.IndexOf("Advanced Micro Devices", StringComparison.OrdinalIgnoreCase) != -1 || manufacturer.StartsWith("AMD", StringComparison.OrdinalIgnoreCase))
return Vendor.AMD;

return Vendor.Unknown;
}
return new IsaBridgeGigabyteController(hostAddress, vendor);
}

// ReSharper disable InconsistentNaming
Expand Down
6 changes: 6 additions & 0 deletions LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcPort.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public ushort ReadWord(byte register)
return (ushort)((ReadByte(register) << 8) | ReadByte((byte)(register + 1)));
}

public bool TryReadWord(byte register, out ushort value)
{
value = ReadWord(register);
return value != 0xFFFF;
}

public void Select(byte logicalDeviceNumber)
{
Ring0.WriteIoPort(RegisterPort, DEVICE_SELECT_REGISTER);
Expand Down