Added support for fan control on ITE 87XX chips and a mainboard specific configuration for the Gigabyte GA 970A UD3 (both based on a patch from Eric Hokanson).
3 This Source Code Form is subject to the terms of the Mozilla Public
4 License, v. 2.0. If a copy of the MPL was not distributed with this
5 file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
11 using System.Globalization;
15 namespace OpenHardwareMonitor.Hardware.LPC {
16 internal class IT87XX : ISuperIO {
18 private readonly ushort address;
19 private readonly Chip chip;
20 private readonly byte version;
22 private readonly ushort gpioAddress;
23 private readonly int gpioCount;
25 private readonly ushort addressReg;
26 private readonly ushort dataReg;
28 private readonly float?[] voltages = new float?[0];
29 private readonly float?[] temperatures = new float?[0];
30 private readonly float?[] fans = new float?[0];
31 private readonly float?[] controls = new float?[0];
33 private readonly float voltageGain;
34 private readonly bool has16bitFanCounter;
37 private const byte ITE_VENDOR_ID = 0x90;
39 // Environment Controller
40 private const byte ADDRESS_REGISTER_OFFSET = 0x05;
41 private const byte DATA_REGISTER_OFFSET = 0x06;
43 // Environment Controller Registers
44 private const byte CONFIGURATION_REGISTER = 0x00;
45 private const byte TEMPERATURE_BASE_REG = 0x29;
46 private const byte VENDOR_ID_REGISTER = 0x58;
47 private const byte FAN_TACHOMETER_DIVISOR_REGISTER = 0x0B;
48 private readonly byte[] FAN_TACHOMETER_REG =
49 { 0x0d, 0x0e, 0x0f, 0x80, 0x82 };
50 private readonly byte[] FAN_TACHOMETER_EXT_REG =
51 { 0x18, 0x19, 0x1a, 0x81, 0x83 };
52 private const byte VOLTAGE_BASE_REG = 0x20;
53 private readonly byte[] FAN_PWM_CTRL_REG = { 0x15, 0x16, 0x17 };
55 private bool[] restoreDefaultFanPwmControlRequired = new bool[3];
56 private byte[] initialFanPwmControl = new byte[3];
58 private byte ReadByte(byte register, out bool valid) {
59 Ring0.WriteIoPort(addressReg, register);
60 byte value = Ring0.ReadIoPort(dataReg);
61 valid = register == Ring0.ReadIoPort(addressReg);
65 private bool WriteByte(byte register, byte value) {
66 Ring0.WriteIoPort(addressReg, register);
67 Ring0.WriteIoPort(dataReg, value);
68 return register == Ring0.ReadIoPort(addressReg);
71 public byte? ReadGPIO(int index) {
72 if (index >= gpioCount)
75 return Ring0.ReadIoPort((ushort)(gpioAddress + index));
78 public void WriteGPIO(int index, byte value) {
79 if (index >= gpioCount)
82 Ring0.WriteIoPort((ushort)(gpioAddress + index), value);
85 private void SaveDefaultFanPwmControl(int index) {
87 if (!restoreDefaultFanPwmControlRequired[index]) {
88 initialFanPwmControl[index] =
89 ReadByte(FAN_PWM_CTRL_REG[index], out valid);
90 restoreDefaultFanPwmControlRequired[index] = true;
94 private void RestoreDefaultFanPwmControl(int index) {
95 if (restoreDefaultFanPwmControlRequired[index]) {
96 WriteByte(FAN_PWM_CTRL_REG[index], initialFanPwmControl[index]);
97 restoreDefaultFanPwmControlRequired[index] = false;
101 public void SetControl(int index, byte? value) {
102 if (index < 0 || index >= controls.Length)
103 throw new ArgumentOutOfRangeException("index");
105 if (!Ring0.WaitIsaBusMutex(10))
108 if (value.HasValue) {
109 SaveDefaultFanPwmControl(index);
112 WriteByte(FAN_PWM_CTRL_REG[index], (byte)(value.Value >> 1));
114 RestoreDefaultFanPwmControl(index);
117 Ring0.ReleaseIsaBusMutex();
120 public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version) {
122 this.address = address;
124 this.version = version;
125 this.addressReg = (ushort)(address + ADDRESS_REGISTER_OFFSET);
126 this.dataReg = (ushort)(address + DATA_REGISTER_OFFSET);
127 this.gpioAddress = gpioAddress;
131 byte vendorId = ReadByte(VENDOR_ID_REGISTER, out valid);
132 if (!valid || vendorId != ITE_VENDOR_ID)
135 // Bit 0x10 of the configuration register should always be 1
136 if ((ReadByte(CONFIGURATION_REGISTER, out valid) & 0x10) == 0)
141 voltages = new float?[9];
142 temperatures = new float?[3];
143 fans = new float?[chip == Chip.IT8705F ? 3 : 5];
144 controls = new float?[3];
146 // IT8721F, IT8728F and IT8772E use a 12mV resultion ADC, all others 16mV
147 if (chip == Chip.IT8721F || chip == Chip.IT8728F || chip == Chip.IT8771E
148 || chip == Chip.IT8772E)
150 voltageGain = 0.012f;
152 voltageGain = 0.016f;
155 // older IT8705F and IT8721F revisions do not have 16-bit fan counters
156 if ((chip == Chip.IT8705F && version < 3) ||
157 (chip == Chip.IT8712F && version < 8))
159 has16bitFanCounter = false;
161 has16bitFanCounter = true;
164 // Set the number of GPIO sets
185 public Chip Chip { get { return chip; } }
186 public float?[] Voltages { get { return voltages; } }
187 public float?[] Temperatures { get { return temperatures; } }
188 public float?[] Fans { get { return fans; } }
189 public float?[] Controls { get { return controls; } }
191 public string GetReport() {
192 StringBuilder r = new StringBuilder();
194 r.AppendLine("LPC " + this.GetType().Name);
196 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
197 r.Append("Chip Version: 0x"); r.AppendLine(
198 version.ToString("X", CultureInfo.InvariantCulture));
199 r.Append("Base Address: 0x"); r.AppendLine(
200 address.ToString("X4", CultureInfo.InvariantCulture));
201 r.Append("GPIO Address: 0x"); r.AppendLine(
202 gpioAddress.ToString("X4", CultureInfo.InvariantCulture));
205 if (!Ring0.WaitIsaBusMutex(100))
208 r.AppendLine("Environment Controller Registers");
210 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
212 for (int i = 0; i <= 0xA; i++) {
214 r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture));
216 for (int j = 0; j <= 0xF; j++) {
219 byte value = ReadByte((byte)((i << 4) | j), out valid);
221 valid ? value.ToString("X2", CultureInfo.InvariantCulture) : "??");
227 r.AppendLine("GPIO Registers");
229 for (int i = 0; i < gpioCount; i++) {
231 r.Append(ReadGPIO(i).Value.ToString("X2",
232 CultureInfo.InvariantCulture));
237 Ring0.ReleaseIsaBusMutex();
242 public void Update() {
243 if (!Ring0.WaitIsaBusMutex(10))
246 for (int i = 0; i < voltages.Length; i++) {
250 voltageGain * ReadByte((byte)(VOLTAGE_BASE_REG + i), out valid);
260 for (int i = 0; i < temperatures.Length; i++) {
262 sbyte value = (sbyte)ReadByte(
263 (byte)(TEMPERATURE_BASE_REG + i), out valid);
267 if (value < sbyte.MaxValue && value > 0)
268 temperatures[i] = value;
270 temperatures[i] = null;
273 if (has16bitFanCounter) {
274 for (int i = 0; i < fans.Length; i++) {
276 int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
279 value |= ReadByte(FAN_TACHOMETER_EXT_REG[i], out valid) << 8;
284 fans[i] = (value < 0xffff) ? 1.35e6f / (value * 2) : 0;
290 for (int i = 0; i < fans.Length; i++) {
292 int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
298 int divisors = ReadByte(FAN_TACHOMETER_DIVISOR_REGISTER, out valid);
301 divisor = 1 << ((divisors >> (3 * i)) & 0x7);
305 fans[i] = (value < 0xff) ? 1.35e6f / (value * divisor) : 0;
312 for (int i = 0; i < controls.Length; i++) {
314 byte value = ReadByte(FAN_PWM_CTRL_REG[i], out valid);
318 if ((value & 0x80) > 0) {
319 // automatic operation (value can't be read)
322 // software operation
323 controls[i] = (float)Math.Round((value & 0x7F) * 100.0f / 0x7F);
327 Ring0.ReleaseIsaBusMutex();