// 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.Globalization; using System.Text; // ReSharper disable once InconsistentNaming namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc; internal class F718XX : ISuperIO { private readonly ushort _address; private readonly byte[] _initialFanPwmControl = new byte[4]; private readonly bool[] _restoreDefaultFanPwmControlRequired = new bool[4]; public F718XX(Chip chip, ushort address) { _address = address; Chip = chip; Voltages = new float?[chip == Chip.F71858 ? 3 : 9]; Temperatures = new float?[chip == Chip.F71808E ? 2 : 3]; Fans = new float?[chip is Chip.F71882 or Chip.F71858 ? 4 : 3]; Controls = new float?[chip == Chip.F71878AD || chip == Chip.F71889AD ? 3 : (chip == Chip.F71882 ? 4 : 0)]; } public Chip Chip { get; } public float?[] Controls { get; } public float?[] Fans { get; } public float?[] Temperatures { get; } public float?[] Voltages { get; } public byte? ReadGpio(int index) { return null; } public void WriteGpio(int index, byte value) { } public void SetControl(int index, byte? value) { if (index < 0 || index >= Controls.Length) throw new ArgumentOutOfRangeException(nameof(index)); if (!Mutexes.WaitIsaBus(10)) return; if (value.HasValue) { SaveDefaultFanPwmControl(index); WriteByte(FAN_PWM_REG[index], value.Value); } else { RestoreDefaultFanPwmControl(index); } Mutexes.ReleaseIsaBus(); } public string GetReport() { StringBuilder r = new(); r.AppendLine("LPC " + GetType().Name); r.AppendLine(); r.Append("Base Address: 0x"); r.AppendLine(_address.ToString("X4", CultureInfo.InvariantCulture)); r.AppendLine(); if (!Mutexes.WaitIsaBus(100)) return r.ToString(); r.AppendLine("Hardware Monitor Registers"); r.AppendLine(); r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"); r.AppendLine(); for (int i = 0; i <= 0xF; i++) { r.Append(" "); r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture)); r.Append(" "); for (int j = 0; j <= 0xF; j++) { r.Append(" "); r.Append(ReadByte((byte)((i << 4) | j)).ToString("X2", CultureInfo.InvariantCulture)); } r.AppendLine(); } r.AppendLine(); Mutexes.ReleaseIsaBus(); return r.ToString(); } public void Update() { if (!Mutexes.WaitIsaBus(10)) return; for (int i = 0; i < Voltages.Length; i++) { if (Chip == Chip.F71808E && i == 6) { // 0x26 is reserved on F71808E Voltages[i] = 0; } else { int value = ReadByte((byte)(VOLTAGE_BASE_REG + i)); Voltages[i] = 0.008f * value; } } for (int i = 0; i < Temperatures.Length; i++) { switch (Chip) { case Chip.F71858: { int tableMode = 0x3 & ReadByte(TEMPERATURE_CONFIG_REG); int high = ReadByte((byte)(TEMPERATURE_BASE_REG + (2 * i))); int low = ReadByte((byte)(TEMPERATURE_BASE_REG + (2 * i) + 1)); if (high is not 0xbb and not 0xcc) { int bits = 0; switch (tableMode) { case 0: break; case 1: bits = 0; break; case 2: bits = (high & 0x80) << 8; break; case 3: bits = (low & 0x01) << 15; break; } bits |= high << 7; bits |= (low & 0xe0) >> 1; short value = (short)(bits & 0xfff0); Temperatures[i] = value / 128.0f; } else { Temperatures[i] = null; } } break; default: { sbyte value = (sbyte)ReadByte((byte)(TEMPERATURE_BASE_REG + (2 * (i + 1)))); if (value is < sbyte.MaxValue and > 0) Temperatures[i] = value; else Temperatures[i] = null; } break; } } for (int i = 0; i < Fans.Length; i++) { int value = ReadByte(FAN_TACHOMETER_REG[i]) << 8; value |= ReadByte((byte)(FAN_TACHOMETER_REG[i] + 1)); if (value > 0) Fans[i] = value < 0x0fff ? 1.5e6f / value : 0; else Fans[i] = null; } for (int i = 0; i < Controls.Length; i++) { if (Chip == Chip.F71882 || Chip == Chip.F71889AD) { Controls[i] = ReadByte((byte)(FAN_PWM_REG[i])) * 100.0f / 0xFF; } else { Controls[i] = ReadByte((byte)(PWM_VALUES_OFFSET + i)) * 100.0f / 0xFF; } } Mutexes.ReleaseIsaBus(); } private void SaveDefaultFanPwmControl(int index) { if (!_restoreDefaultFanPwmControlRequired[index]) { _initialFanPwmControl[index] = ReadByte(FAN_PWM_REG[index]); _restoreDefaultFanPwmControlRequired[index] = true; } } private void RestoreDefaultFanPwmControl(int index) { if (_restoreDefaultFanPwmControlRequired[index]) { WriteByte(FAN_PWM_REG[index], _initialFanPwmControl[index]); _restoreDefaultFanPwmControlRequired[index] = false; } } private byte ReadByte(byte register) { Ring0.WriteIoPort((ushort)(_address + ADDRESS_REGISTER_OFFSET), register); return Ring0.ReadIoPort((ushort)(_address + DATA_REGISTER_OFFSET)); } private void WriteByte(byte register, byte value) { Ring0.WriteIoPort((ushort)(_address + ADDRESS_REGISTER_OFFSET), register); Ring0.WriteIoPort((ushort)(_address + DATA_REGISTER_OFFSET), value); } // ReSharper disable InconsistentNaming #pragma warning disable IDE1006 // Naming Styles private const byte ADDRESS_REGISTER_OFFSET = 0x05; private const byte DATA_REGISTER_OFFSET = 0x06; private const byte PWM_VALUES_OFFSET = 0x2D; private const byte TEMPERATURE_BASE_REG = 0x70; private const byte TEMPERATURE_CONFIG_REG = 0x69; private const byte VOLTAGE_BASE_REG = 0x20; private readonly byte[] FAN_PWM_REG = { 0xA3, 0xB3, 0xC3, 0xD3 }; private readonly byte[] FAN_TACHOMETER_REG = { 0xA0, 0xB0, 0xC0, 0xD0 }; // ReSharper restore InconsistentNaming #pragma warning restore IDE1006 // Naming Styles }