Added experimental support for ITE IT8771E super I/O chips.
3 Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 The contents of this file are subject to the Mozilla Public License Version
6 1.1 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 for the specific language governing rights and limitations under the License.
15 The Original Code is the Open Hardware Monitor code.
17 The Initial Developer of the Original Code is
18 Michael Möller <m.moeller@gmx.ch>.
19 Portions created by the Initial Developer are Copyright (C) 2009-2011
20 the Initial Developer. All Rights Reserved.
24 Alternatively, the contents of this file may be used under the terms of
25 either the GNU General Public License Version 2 or later (the "GPL"), or
26 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 in which case the provisions of the GPL or the LGPL are applicable instead
28 of those above. If you wish to allow use of your version of this file only
29 under the terms of either the GPL or the LGPL, and not to allow others to
30 use your version of this file under the terms of the MPL, indicate your
31 decision by deleting the provisions above and replace them with the notice
32 and other provisions required by the GPL or the LGPL. If you do not delete
33 the provisions above, a recipient may use your version of this file under
34 the terms of any one of the MPL, the GPL or the LGPL.
39 using System.Globalization;
42 namespace OpenHardwareMonitor.Hardware.LPC {
43 internal class W836XX : ISuperIO {
45 private readonly ushort address;
46 private readonly byte revision;
48 private readonly Chip chip;
50 private readonly float?[] voltages = new float?[0];
51 private readonly float?[] temperatures = new float?[0];
52 private readonly float?[] fans = new float?[0];
53 private readonly float?[] controls = new float?[0];
55 private readonly bool[] peciTemperature = new bool[0];
56 private readonly byte[] voltageRegister = new byte[0];
57 private readonly byte[] voltageBank = new byte[0];
58 private readonly float voltageGain = 0.008f;
61 private const ushort WINBOND_VENDOR_ID = 0x5CA3;
62 private const byte HIGH_BYTE = 0x80;
65 private const byte ADDRESS_REGISTER_OFFSET = 0x05;
66 private const byte DATA_REGISTER_OFFSET = 0x06;
68 // Hardware Monitor Registers
69 private const byte VOLTAGE_VBAT_REG = 0x51;
70 private const byte BANK_SELECT_REGISTER = 0x4E;
71 private const byte VENDOR_ID_REGISTER = 0x4F;
72 private const byte TEMPERATURE_SOURCE_SELECT_REG = 0x49;
74 private readonly byte[] TEMPERATURE_REG = new byte[] { 0x50, 0x50, 0x27 };
75 private readonly byte[] TEMPERATURE_BANK = new byte[] { 1, 2, 0 };
77 private readonly byte[] FAN_TACHO_REG =
78 new byte[] { 0x28, 0x29, 0x2A, 0x3F, 0x53 };
79 private readonly byte[] FAN_TACHO_BANK =
80 new byte[] { 0, 0, 0, 0, 5 };
81 private readonly byte[] FAN_BIT_REG =
82 new byte[] { 0x47, 0x4B, 0x4C, 0x59, 0x5D };
83 private readonly byte[] FAN_DIV_BIT0 = new byte[] { 36, 38, 30, 8, 10 };
84 private readonly byte[] FAN_DIV_BIT1 = new byte[] { 37, 39, 31, 9, 11 };
85 private readonly byte[] FAN_DIV_BIT2 = new byte[] { 5, 6, 7, 23, 15 };
87 private byte ReadByte(byte bank, byte register) {
89 (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
91 (ushort)(address + DATA_REGISTER_OFFSET), bank);
93 (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
94 return Ring0.ReadIoPort(
95 (ushort)(address + DATA_REGISTER_OFFSET));
98 private void WriteByte(byte bank, byte register, byte value) {
100 (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
102 (ushort)(address + DATA_REGISTER_OFFSET), bank);
104 (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
106 (ushort)(address + DATA_REGISTER_OFFSET), value);
109 public byte? ReadGPIO(int index) {
113 public void WriteGPIO(int index, byte value) { }
115 public void SetControl(int index, byte? value) { }
117 public W836XX(Chip chip, byte revision, ushort address) {
118 this.address = address;
119 this.revision = revision;
122 if (!IsWinbondVendor())
125 temperatures = new float?[3];
126 peciTemperature = new bool[3];
130 // note temperature sensor registers that read PECI
131 byte flag = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
132 peciTemperature[0] = (flag & 0x04) != 0;
133 peciTemperature[1] = (flag & 0x40) != 0;
134 peciTemperature[2] = false;
137 case Chip.W83627DHGP:
138 // note temperature sensor registers that read PECI
139 byte sel = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
140 peciTemperature[0] = (sel & 0x07) != 0;
141 peciTemperature[1] = (sel & 0x70) != 0;
142 peciTemperature[2] = false;
146 peciTemperature[0] = false;
147 peciTemperature[1] = false;
148 peciTemperature[2] = false;
154 voltages = new float?[10];
155 voltageRegister = new byte[] {
156 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51, 0x52 };
157 voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5, 5 };
158 voltageGain = 0.008f;
159 fans = new float?[5];
162 case Chip.W83627DHGP:
165 voltages = new float?[9];
166 voltageRegister = new byte[] {
167 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51 };
168 voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5 };
169 voltageGain = 0.008f;
170 fans = new float?[5];
175 voltages = new float?[7];
176 voltageRegister = new byte[] {
177 0x20, 0x21, 0x22, 0x23, 0x24, 0x50, 0x51 };
178 voltageBank = new byte[] { 0, 0, 0, 0, 0, 5, 5 };
179 voltageGain = 0.016f;
180 fans = new float?[3];
185 private bool IsWinbondVendor() {
187 (ushort)((ReadByte(HIGH_BYTE, VENDOR_ID_REGISTER) << 8) |
188 ReadByte(0, VENDOR_ID_REGISTER));
189 return vendorId == WINBOND_VENDOR_ID;
192 private static ulong SetBit(ulong target, int bit, int value) {
193 if ((value & 1) != value)
194 throw new ArgumentException("Value must be one bit only.");
196 if (bit < 0 || bit > 63)
197 throw new ArgumentException("Bit out of range.");
199 ulong mask = (((ulong)1) << bit);
200 return value > 0 ? target | mask : target & ~mask;
203 public Chip Chip { get { return chip; } }
204 public float?[] Voltages { get { return voltages; } }
205 public float?[] Temperatures { get { return temperatures; } }
206 public float?[] Fans { get { return fans; } }
207 public float?[] Controls { get { return controls; } }
209 public void Update() {
210 if (!Ring0.WaitIsaBusMutex(10))
213 for (int i = 0; i < voltages.Length; i++) {
214 if (voltageRegister[i] != VOLTAGE_VBAT_REG) {
215 // two special VCore measurement modes for W83627THF
217 if ((chip == Chip.W83627HF || chip == Chip.W83627THF ||
218 chip == Chip.W83687THF) && i == 0)
220 byte vrmConfiguration = ReadByte(0, 0x18);
221 int value = ReadByte(voltageBank[i], voltageRegister[i]);
222 if ((vrmConfiguration & 0x01) == 0)
223 fvalue = 0.016f * value; // VRM8 formula
225 fvalue = 0.00488f * value + 0.69f; // VRM9 formula
227 int value = ReadByte(voltageBank[i], voltageRegister[i]);
228 fvalue = voltageGain * value;
231 voltages[i] = fvalue;
236 bool valid = (ReadByte(0, 0x5D) & 0x01) > 0;
238 voltages[i] = voltageGain * ReadByte(5, VOLTAGE_VBAT_REG);
245 for (int i = 0; i < temperatures.Length; i++) {
246 int value = ((sbyte)ReadByte(TEMPERATURE_BANK[i],
247 TEMPERATURE_REG[i])) << 1;
248 if (TEMPERATURE_BANK[i] > 0)
249 value |= ReadByte(TEMPERATURE_BANK[i],
250 (byte)(TEMPERATURE_REG[i] + 1)) >> 7;
252 float temperature = value / 2.0f;
253 if (temperature <= 125 && temperature >= -55 && !peciTemperature[i]) {
254 temperatures[i] = temperature;
256 temperatures[i] = null;
261 for (int i = 0; i < FAN_BIT_REG.Length; i++)
262 bits = (bits << 8) | ReadByte(0, FAN_BIT_REG[i]);
263 ulong newBits = bits;
264 for (int i = 0; i < fans.Length; i++) {
265 int count = ReadByte(FAN_TACHO_BANK[i], FAN_TACHO_REG[i]);
267 // assemble fan divisor
268 int divisorBits = (int)(
269 (((bits >> FAN_DIV_BIT2[i]) & 1) << 2) |
270 (((bits >> FAN_DIV_BIT1[i]) & 1) << 1) |
271 ((bits >> FAN_DIV_BIT0[i]) & 1));
272 int divisor = 1 << divisorBits;
274 float value = (count < 0xff) ? 1.35e6f / (count * divisor) : 0;
277 // update fan divisor
278 if (count > 192 && divisorBits < 7)
280 if (count < 96 && divisorBits > 0)
283 newBits = SetBit(newBits, FAN_DIV_BIT2[i], (divisorBits >> 2) & 1);
284 newBits = SetBit(newBits, FAN_DIV_BIT1[i], (divisorBits >> 1) & 1);
285 newBits = SetBit(newBits, FAN_DIV_BIT0[i], divisorBits & 1);
288 // write new fan divisors
289 for (int i = FAN_BIT_REG.Length - 1; i >= 0; i--) {
290 byte oldByte = (byte)(bits & 0xFF);
291 byte newByte = (byte)(newBits & 0xFF);
293 newBits = newBits >> 8;
294 if (oldByte != newByte)
295 WriteByte(0, FAN_BIT_REG[i], newByte);
298 Ring0.ReleaseIsaBusMutex();
301 public string GetReport() {
302 StringBuilder r = new StringBuilder();
304 r.AppendLine("LPC " + this.GetType().Name);
306 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
307 r.Append("Chip revision: 0x");
308 r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
309 r.Append("Base Adress: 0x");
310 r.AppendLine(address.ToString("X4", CultureInfo.InvariantCulture));
313 if (!Ring0.WaitIsaBusMutex(100))
316 r.AppendLine("Hardware Monitor Registers");
318 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
320 for (int i = 0; i <= 0x7; i++) {
322 r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture));
324 for (int j = 0; j <= 0xF; j++) {
326 r.Append(ReadByte(0, (byte)((i << 4) | j)).ToString(
327 "X2", CultureInfo.InvariantCulture));
331 for (int k = 1; k <= 15; k++) {
332 r.AppendLine("Bank " + k);
333 for (int i = 0x5; i < 0x6; i++) {
335 r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture));
337 for (int j = 0; j <= 0xF; j++) {
339 r.Append(ReadByte((byte)(k), (byte)((i << 4) | j)).ToString(
340 "X2", CultureInfo.InvariantCulture));
347 Ring0.ReleaseIsaBusMutex();