From af7fca13978ca9f69d12abf123ca036bc8fbb50b Mon Sep 17 00:00:00 2001 From: Ezra Indrastata Date: Wed, 12 Jun 2024 00:28:37 +0900 Subject: [PATCH] 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 --- .../Lpc/EcioPortGigabyteController.cs | 77 ++++++++++ .../Motherboard/Lpc/IGigabyteController.cs | 14 ++ .../Motherboard/Lpc/IT879xEcioPort.cs | 139 ++++++++++++++++++ .../Hardware/Motherboard/Lpc/IT87XX.cs | 7 +- ...ller.cs => IsaBridgeGigabyteController.cs} | 8 +- .../Hardware/Motherboard/Lpc/LpcIO.cs | 65 +++++--- .../Hardware/Motherboard/Lpc/LpcPort.cs | 6 + 7 files changed, 289 insertions(+), 27 deletions(-) create mode 100644 LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EcioPortGigabyteController.cs create mode 100644 LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IGigabyteController.cs create mode 100644 LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT879xEcioPort.cs rename LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/{GigabyteController.cs => IsaBridgeGigabyteController.cs} (96%) diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EcioPortGigabyteController.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EcioPortGigabyteController.cs new file mode 100644 index 0000000..ee09b0a --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EcioPortGigabyteController.cs @@ -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 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); + } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IGigabyteController.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IGigabyteController.cs new file mode 100644 index 0000000..e57c361 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IGigabyteController.cs @@ -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 and Contributors. +// All Rights Reserved. + +namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc; + +internal interface IGigabyteController +{ + bool Enable(bool enabled); + + void Restore(); +} diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT879xEcioPort.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT879xEcioPort.cs new file mode 100644 index 0000000..32a36cb --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT879xEcioPort.cs @@ -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 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; +} diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT87XX.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT87XX.cs index 94ebc78..0f50daa 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT87XX.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT87XX.cs @@ -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 } }; diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/GigabyteController.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IsaBridgeGigabyteController.cs similarity index 96% rename from LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/GigabyteController.cs rename to LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IsaBridgeGigabyteController.cs index ab7e33c..82394e4 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/GigabyteController.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IsaBridgeGigabyteController.cs @@ -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. /// -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); diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcIO.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcIO.cs index 888b95c..8ecdbce 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcIO.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcIO.cs @@ -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 diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcPort.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcPort.cs index e9cc744..d0df353 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcPort.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/LpcPort.cs @@ -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);