Files
CarlMonitor/LibreHardwareMonitor-0.9.4/LibreHardwareMonitorLib/Hardware/Cpu/Amd0FCpu.cs
2025-04-07 07:44:27 -07:00

162 lines
5.9 KiB
C#

// 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.Globalization;
using System.Text;
using System.Threading;
namespace LibreHardwareMonitor.Hardware.Cpu;
internal sealed class Amd0FCpu : AmdCpu
{
private readonly Sensor _busClock;
private readonly Sensor[] _coreClocks;
private readonly Sensor[] _coreTemperatures;
private readonly uint _miscellaneousControlAddress;
private readonly byte _thermSenseCoreSelCPU0;
private readonly byte _thermSenseCoreSelCPU1;
public Amd0FCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
{
float offset = -49.0f;
// AM2+ 65nm +21 offset
uint model = cpuId[0][0].Model;
if (model is >= 0x69 and not 0xc1 and not 0x6c and not 0x7c)
offset += 21;
if (model < 40)
{
// AMD Athlon 64 Processors
_thermSenseCoreSelCPU0 = 0x0;
_thermSenseCoreSelCPU1 = 0x4;
}
else
{
// AMD NPT Family 0Fh Revision F, G have the core selection swapped
_thermSenseCoreSelCPU0 = 0x4;
_thermSenseCoreSelCPU1 = 0x0;
}
// check if processor supports a digital thermal sensor
if (cpuId[0][0].ExtData.GetLength(0) > 7 && (cpuId[0][0].ExtData[7, 3] & 1) != 0)
{
_coreTemperatures = new Sensor[_coreCount];
for (int i = 0; i < _coreCount; i++)
{
_coreTemperatures[i] = new Sensor("Core #" + (i + 1),
i,
SensorType.Temperature,
this,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", offset) },
settings);
}
}
else
{
_coreTemperatures = Array.Empty<Sensor>();
}
_miscellaneousControlAddress = GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, MISCELLANEOUS_CONTROL_DEVICE_ID);
_busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
_coreClocks = new Sensor[_coreCount];
for (int i = 0; i < _coreClocks.Length; i++)
{
_coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
if (HasTimeStampCounter)
ActivateSensor(_coreClocks[i]);
}
Update();
}
protected override uint[] GetMsrs()
{
return new[] { FIDVID_STATUS };
}
public override string GetReport()
{
StringBuilder r = new();
r.Append(base.GetReport());
r.Append("Miscellaneous Control Address: 0x");
r.AppendLine(_miscellaneousControlAddress.ToString("X", CultureInfo.InvariantCulture));
r.AppendLine();
return r.ToString();
}
public override void Update()
{
base.Update();
if (Mutexes.WaitPciBus(10))
{
if (_miscellaneousControlAddress != Interop.Ring0.INVALID_PCI_ADDRESS)
{
for (uint i = 0; i < _coreTemperatures.Length; i++)
{
if (Ring0.WritePciConfig(_miscellaneousControlAddress,
THERMTRIP_STATUS_REGISTER,
i > 0 ? _thermSenseCoreSelCPU1 : _thermSenseCoreSelCPU0))
{
if (Ring0.ReadPciConfig(_miscellaneousControlAddress, THERMTRIP_STATUS_REGISTER, out uint value))
{
_coreTemperatures[i].Value = ((value >> 16) & 0xFF) + _coreTemperatures[i].Parameters[0].Value;
ActivateSensor(_coreTemperatures[i]);
}
else
{
DeactivateSensor(_coreTemperatures[i]);
}
}
}
}
Mutexes.ReleasePciBus();
}
if (HasTimeStampCounter)
{
double newBusClock = 0;
for (int i = 0; i < _coreClocks.Length; i++)
{
Thread.Sleep(1);
if (Ring0.ReadMsr(FIDVID_STATUS, out uint eax, out uint _, _cpuId[i][0].Affinity))
{
// CurrFID can be found in eax bits 0-5, MaxFID in 16-21
// 8-13 hold StartFID, we don't use that here.
double curMp = 0.5 * ((eax & 0x3F) + 8);
double maxMp = 0.5 * ((eax >> 16 & 0x3F) + 8);
_coreClocks[i].Value = (float)(curMp * TimeStampCounterFrequency / maxMp);
newBusClock = (float)(TimeStampCounterFrequency / maxMp);
}
else
{
// Fail-safe value - if the code above fails, we'll use this instead
_coreClocks[i].Value = (float)TimeStampCounterFrequency;
}
}
if (newBusClock > 0)
{
_busClock.Value = (float)newBusClock;
ActivateSensor(_busClock);
}
}
}
// ReSharper disable InconsistentNaming
private const uint FIDVID_STATUS = 0xC0010042;
private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1103;
private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
private const uint THERMTRIP_STATUS_REGISTER = 0xE4;
// ReSharper restore InconsistentNaming
}