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

* Check before entering bugged 0x4E IT87

* Specify fan control registers for IT8792E

* Refactor chip ID reading using TryReadWord

* Introduce ECIO gigabyte EC control

* Add ECIO compatibility check

* Refactor to consider result of read/write

* Refactor styles and syntaxes

* Rename SMFI to Smfi

* Remove public modifier for consistency

* Update code style

* Increase timeout and remove sleep for ecio waits

* Remove extra WaitIBE
This commit is contained in:
Ezra Indrastata
2024-06-11 17:28:37 +02:00
committed by GitHub
parent 4a169defb6
commit af7fca1397
7 changed files with 289 additions and 27 deletions
@@ -0,0 +1,77 @@
// 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, Convert.ToByte(enabled)))
{
return false;
}
// Allow the system to catch up.
Thread.Sleep(500);
}
return true;
}
public void Restore()
{
if (_initialState.HasValue)
{
Enable(_initialState.Value);
}
}
}
@@ -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();
}
@@ -0,0 +1,139 @@
// 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;
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)
{
if (!Init(0xB0, offset))
{
value = 0;
return false;
}
return ReadFromValue(out value);
}
public bool Write(ushort offset, byte value)
{
if (!Init(0xB1, offset))
{
return false;
}
return WriteToValue(value);
}
private bool Init(byte command, ushort offset)
{
if (!WriteToRegister(command))
{
return false;
}
if (!WriteToValue((byte)((offset >> 8) & 0xFF)))
{
return false;
}
if (!WriteToValue((byte)(offset & 0xFF)))
{
return false;
}
return true;
}
private bool WriteToRegister(byte value)
{
if (!WaitIBE())
{
return false;
}
Ring0.WriteIoPort(RegisterPort, value);
return true;
}
private bool WriteToValue(byte value)
{
if (!WaitIBE())
{
return false;
}
Ring0.WriteIoPort(ValuePort, value);
return true;
}
private bool ReadFromValue(out byte value)
{
if (!WaitOBF())
{
value = 0;
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;
}
}
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;
}
}
return true;
}
finally
{
stopwatch.Stop();
}
}
private const long WAIT_TIMEOUT = 1000L;
}
@@ -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;
@@ -72,7 +72,8 @@ internal class IT87XX : ISuperIO
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 }
};
@@ -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;
@@ -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;
@@ -58,7 +58,7 @@ internal class GigabyteController
if (!Mutexes.WaitPciBus(10))
return false;
// see D14F3x https://www.amd.com/system/files/TechDocs/55072_AMD_Family_15h_Models_70h-7Fh_BKDG.pdf
// see D14F3x https://www.amd.com/system/files/TechDocs/55072_AMD_Family_15h_Models_70h-7Fh_BKDG.pdf
uint amdIsaBridgeAddress = Ring0.GetPciAddress(0x0, 0x14, 0x3);
const uint ioOrMemoryPortDecodeEnableRegister = 0x48;
@@ -124,7 +124,7 @@ internal class GigabyteController
{
Marshal.WriteByte(mapped, ControllerEnableRegister, Convert.ToByte(enabled));
// Give it some time to see the change
Thread.Sleep(200);
Thread.Sleep(500);
}
InpOut.UnmapMemory(handle, mapped);
@@ -536,9 +536,15 @@ internal class LpcIO
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,
@@ -604,7 +610,7 @@ internal class LpcIO
gpioVerify = port.ReadWord(BASE_ADDRESS_REGISTER + 2);
}
GigabyteController gigabyteController = FindGigabyteEC(port, chip, motherboard);
IGigabyteController gigabyteController = FindGigabyteEC(port, chip, motherboard);
port.IT87Exit();
@@ -637,14 +643,45 @@ internal class LpcIO
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
@@ -662,7 +699,7 @@ internal class LpcIO
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)
@@ -681,19 +718,7 @@ internal class LpcIO
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
@@ -35,6 +35,12 @@ internal class LpcPort
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);