Skip to content

Commit

Permalink
Fix missing view for IT8792E and allow fan control for IT8792E on som…
Browse files Browse the repository at this point in the history
…e 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
  • Loading branch information
pwnedgod committed Jun 11, 2024
1 parent 4a169de commit af7fca1
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
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();
}
139 changes: 139 additions & 0 deletions LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/IT879xEcioPort.cs
Original file line number Diff line number Diff line change
@@ -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;
}
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 All @@ -58,7 +58,7 @@ private bool AmdEnable(bool enabled)
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;
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(500);
}

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
Loading

0 comments on commit af7fca1

Please sign in to comment.