Added experimental support for Nuvoton NCT6779D super I/O chips.
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?[chip == Chip.IT8705F ? 3 : 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 IT8705F and IT8721F revisions do not have 16-bit fan counters
117 if ((chip == Chip.IT8705F && version < 3) ||
118 (chip == Chip.IT8712F && version < 8))
120 has16bitFanCounter = false;
122 has16bitFanCounter = true;
125 // Set the number of GPIO sets
146 public Chip Chip { get { return chip; } }
147 public float?[] Voltages { get { return voltages; } }
148 public float?[] Temperatures { get { return temperatures; } }
149 public float?[] Fans { get { return fans; } }
150 public float?[] Controls { get { return controls; } }
152 public string GetReport() {
153 StringBuilder r = new StringBuilder();
155 r.AppendLine("LPC " + this.GetType().Name);
157 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
158 r.Append("Chip Version: 0x"); r.AppendLine(
159 version.ToString("X", CultureInfo.InvariantCulture));
160 r.Append("Base Address: 0x"); r.AppendLine(
161 address.ToString("X4", CultureInfo.InvariantCulture));
162 r.Append("GPIO Address: 0x"); r.AppendLine(
163 gpioAddress.ToString("X4", CultureInfo.InvariantCulture));
166 if (!Ring0.WaitIsaBusMutex(100))
169 r.AppendLine("Environment Controller Registers");
171 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
173 for (int i = 0; i <= 0xA; i++) {
175 r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture));
177 for (int j = 0; j <= 0xF; j++) {
180 byte value = ReadByte((byte)((i << 4) | j), out valid);
182 valid ? value.ToString("X2", CultureInfo.InvariantCulture) : "??");
188 r.AppendLine("GPIO Registers");
190 for (int i = 0; i < gpioCount; i++) {
192 r.Append(ReadGPIO(i).Value.ToString("X2",
193 CultureInfo.InvariantCulture));
198 Ring0.ReleaseIsaBusMutex();
203 public void Update() {
204 if (!Ring0.WaitIsaBusMutex(10))
207 for (int i = 0; i < voltages.Length; i++) {
211 voltageGain * ReadByte((byte)(VOLTAGE_BASE_REG + i), out valid);
221 for (int i = 0; i < temperatures.Length; i++) {
223 sbyte value = (sbyte)ReadByte(
224 (byte)(TEMPERATURE_BASE_REG + i), out valid);
228 if (value < sbyte.MaxValue && value > 0)
229 temperatures[i] = value;
231 temperatures[i] = null;
234 if (has16bitFanCounter) {
235 for (int i = 0; i < fans.Length; i++) {
237 int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
240 value |= ReadByte(FAN_TACHOMETER_EXT_REG[i], out valid) << 8;
245 fans[i] = (value < 0xffff) ? 1.35e6f / (value * 2) : 0;
251 for (int i = 0; i < fans.Length; i++) {
253 int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
259 int divisors = ReadByte(FAN_TACHOMETER_DIVISOR_REGISTER, out valid);
262 divisor = 1 << ((divisors >> (3 * i)) & 0x7);
266 fans[i] = (value < 0xff) ? 1.35e6f / (value * divisor) : 0;
273 Ring0.ReleaseIsaBusMutex();