Added support for reading more than one TBalancer fan controller.
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;
14 namespace OpenHardwareMonitor.Hardware.LPC {
15 internal class IT87XX : ISuperIO {
17 private readonly ushort address;
18 private readonly Chip chip;
19 private readonly byte version;
21 private readonly ushort gpioAddress;
22 private readonly int gpioCount;
24 private readonly ushort addressReg;
25 private readonly ushort dataReg;
27 private readonly float?[] voltages = new float?[0];
28 private readonly float?[] temperatures = new float?[0];
29 private readonly float?[] fans = new float?[0];
30 private readonly float?[] controls = new float?[0];
32 private readonly float voltageGain;
33 private readonly bool has16bitFanCounter;
36 private const byte ITE_VENDOR_ID = 0x90;
38 // Environment Controller
39 private const byte ADDRESS_REGISTER_OFFSET = 0x05;
40 private const byte DATA_REGISTER_OFFSET = 0x06;
42 // Environment Controller Registers
43 private const byte CONFIGURATION_REGISTER = 0x00;
44 private const byte TEMPERATURE_BASE_REG = 0x29;
45 private const byte VENDOR_ID_REGISTER = 0x58;
46 private const byte FAN_TACHOMETER_DIVISOR_REGISTER = 0x0B;
47 private readonly byte[] FAN_TACHOMETER_REG =
48 new byte[] { 0x0d, 0x0e, 0x0f, 0x80, 0x82 };
49 private readonly byte[] FAN_TACHOMETER_EXT_REG =
50 new byte[] { 0x18, 0x19, 0x1a, 0x81, 0x83 };
51 private const byte VOLTAGE_BASE_REG = 0x20;
53 private byte ReadByte(byte register, out bool valid) {
54 Ring0.WriteIoPort(addressReg, register);
55 byte value = Ring0.ReadIoPort(dataReg);
56 valid = register == Ring0.ReadIoPort(addressReg);
60 private bool WriteByte(byte register, byte value) {
61 Ring0.WriteIoPort(addressReg, register);
62 Ring0.WriteIoPort(dataReg, value);
63 return register == Ring0.ReadIoPort(addressReg);
66 public byte? ReadGPIO(int index) {
67 if (index >= gpioCount)
70 return Ring0.ReadIoPort((ushort)(gpioAddress + index));
73 public void WriteGPIO(int index, byte value) {
74 if (index >= gpioCount)
77 Ring0.WriteIoPort((ushort)(gpioAddress + index), value);
80 public void SetControl(int index, byte? value) { }
82 public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version) {
84 this.address = address;
86 this.version = version;
87 this.addressReg = (ushort)(address + ADDRESS_REGISTER_OFFSET);
88 this.dataReg = (ushort)(address + DATA_REGISTER_OFFSET);
89 this.gpioAddress = gpioAddress;
93 byte vendorId = ReadByte(VENDOR_ID_REGISTER, out valid);
94 if (!valid || vendorId != ITE_VENDOR_ID)
97 // Bit 0x10 of the configuration register should always be 1
98 if ((ReadByte(CONFIGURATION_REGISTER, out valid) & 0x10) == 0)
103 voltages = new float?[9];
104 temperatures = new float?[3];
105 fans = new float?[5];
107 // IT8721F, IT8728F and IT8772E use a 12mV resultion ADC, all others 16mV
108 if (chip == Chip.IT8721F || chip == Chip.IT8728F || chip == Chip.IT8771E
109 || chip == Chip.IT8772E)
111 voltageGain = 0.012f;
113 voltageGain = 0.016f;
116 // older IT8721F revision do not have 16-bit fan counters
117 if (chip == Chip.IT8712F && version < 8) {
118 has16bitFanCounter = false;
120 has16bitFanCounter = true;
123 // Set the number of GPIO sets
143 public Chip Chip { get { return chip; } }
144 public float?[] Voltages { get { return voltages; } }
145 public float?[] Temperatures { get { return temperatures; } }
146 public float?[] Fans { get { return fans; } }
147 public float?[] Controls { get { return controls; } }
149 public string GetReport() {
150 StringBuilder r = new StringBuilder();
152 r.AppendLine("LPC " + this.GetType().Name);
154 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
155 r.Append("Chip Version: 0x"); r.AppendLine(
156 version.ToString("X", CultureInfo.InvariantCulture));
157 r.Append("Base Address: 0x"); r.AppendLine(
158 address.ToString("X4", CultureInfo.InvariantCulture));
159 r.Append("GPIO Address: 0x"); r.AppendLine(
160 gpioAddress.ToString("X4", CultureInfo.InvariantCulture));
163 if (!Ring0.WaitIsaBusMutex(100))
166 r.AppendLine("Environment Controller Registers");
168 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
170 for (int i = 0; i <= 0xA; i++) {
172 r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture));
174 for (int j = 0; j <= 0xF; j++) {
177 byte value = ReadByte((byte)((i << 4) | j), out valid);
179 valid ? value.ToString("X2", CultureInfo.InvariantCulture) : "??");
185 r.AppendLine("GPIO Registers");
187 for (int i = 0; i < gpioCount; i++) {
189 r.Append(ReadGPIO(i).Value.ToString("X2",
190 CultureInfo.InvariantCulture));
195 Ring0.ReleaseIsaBusMutex();
200 public void Update() {
201 if (!Ring0.WaitIsaBusMutex(10))
204 for (int i = 0; i < voltages.Length; i++) {
208 voltageGain * ReadByte((byte)(VOLTAGE_BASE_REG + i), out valid);
218 for (int i = 0; i < temperatures.Length; i++) {
220 sbyte value = (sbyte)ReadByte(
221 (byte)(TEMPERATURE_BASE_REG + i), out valid);
225 if (value < sbyte.MaxValue && value > 0)
226 temperatures[i] = value;
228 temperatures[i] = null;
231 if (has16bitFanCounter) {
232 for (int i = 0; i < fans.Length; i++) {
234 int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
237 value |= ReadByte(FAN_TACHOMETER_EXT_REG[i], out valid) << 8;
242 fans[i] = (value < 0xffff) ? 1.35e6f / (value * 2) : 0;
248 for (int i = 0; i < fans.Length; i++) {
250 int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
256 int divisors = ReadByte(FAN_TACHOMETER_DIVISOR_REGISTER, out valid);
259 divisor = 1 << ((divisors >> (3 * i)) & 0x7);
263 fans[i] = (value < 0xff) ? 1.35e6f / (value * divisor) : 0;
270 Ring0.ReleaseIsaBusMutex();