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:
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user