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) 2010-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.
38 using System.Globalization;
41 namespace OpenHardwareMonitor.Hardware.LPC {
42 internal class NCT677X : ISuperIO {
44 private readonly ushort port;
45 private readonly byte revision;
47 private readonly Chip chip;
49 private readonly float?[] voltages = new float?[9];
50 private readonly float?[] temperatures = new float?[4];
51 private readonly float?[] fans = new float?[0];
52 private readonly float?[] controls = new float?[3];
55 private const uint ADDRESS_REGISTER_OFFSET = 0x05;
56 private const uint DATA_REGISTER_OFFSET = 0x06;
57 private const byte BANK_SELECT_REGISTER = 0x4E;
59 private byte ReadByte(ushort address) {
60 byte bank = (byte)(address >> 8);
61 byte register = (byte)(address & 0xFF);
62 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
63 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
64 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
65 return Ring0.ReadIoPort(port + DATA_REGISTER_OFFSET);
68 private void WriteByte(ushort address, byte value) {
69 byte bank = (byte)(address >> 8);
70 byte register = (byte)(address & 0xFF);
71 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
72 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
73 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
74 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, value);
78 private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
80 // Hardware Monitor Registers
81 private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
82 private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;
83 private const ushort VOLTAGE_VBAT_REG = 0x0551;
85 private readonly ushort[] TEMPERATURE_REG =
86 { 0x027, 0x73, 0x75, 0x77, 0x150, 0x250, 0x62B, 0x62C, 0x62D };
87 private readonly ushort[] TEMPERATURE_HALF_REG =
88 { 0, 0x74, 0x76, 0x78, 0x151, 0x251, 0x62E, 0x62E, 0x62E };
89 private readonly ushort[] TEMPERATURE_SRC_REG =
90 { 0x621, 0x100, 0x200, 0x300, 0x622, 0x623, 0x624, 0x625, 0x626 };
91 private readonly int[] TEMPERATURE_HALF_BIT =
92 { -1, 7, 7, 7, 7, 7, 0, 1, 2 };
93 private readonly ushort[] VOLTAGE_REG =
94 { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551 };
95 private readonly ushort[] FAN_RPM_REG =
96 { 0x656, 0x658, 0x65A, 0x65C, 0x65E};
97 private readonly ushort[] FAN_PWM_OUT_REG =
98 { 0x001, 0x003, 0x011 };
99 private readonly ushort[] FAN_PWM_COMMAND_REG =
100 { 0x109, 0x209, 0x309 };
101 private readonly ushort[] FAN_CONTROL_MODE_REG =
102 { 0x102, 0x202, 0x302 };
104 private readonly int minFanRPM;
106 private bool[] restoreDefaultFanControlRequired = { false, false, false };
107 private byte[] initialFanControlMode = new byte[3];
108 private byte[] initialFanPwmCommand = new byte[3];
110 private enum SourceNCT6771F : byte {
123 PCH_CHIP_CPU_MAX_TEMP = 13,
133 private enum SourceNCT6776F : byte {
147 PCH_CHIP_CPU_MAX_TEMP = 14,
158 public NCT677X(Chip chip, byte revision, ushort port) {
160 this.revision = revision;
163 if (!IsNuvotonVendor())
167 case LPC.Chip.NCT6771F:
168 fans = new float?[4];
169 // min value RPM value with 16-bit fan counter
170 minFanRPM = (int)(1.35e6 / 0xFFFF);
172 case LPC.Chip.NCT6776F:
173 fans = new float?[5];
174 // min value RPM value with 13-bit fan counter
175 minFanRPM = (int)(1.35e6 / 0x1FFF);
180 private bool IsNuvotonVendor() {
181 return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
182 ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
185 public byte? ReadGPIO(int index) {
189 public void WriteGPIO(int index, byte value) { }
192 private void SaveDefaultFanControl(int index) {
193 if (!restoreDefaultFanControlRequired[index]) {
194 initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
195 initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
196 restoreDefaultFanControlRequired[index] = true;
200 private void RestoreDefaultFanControl(int index) {
201 if (restoreDefaultFanControlRequired[index]) {
202 WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
203 WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
204 restoreDefaultFanControlRequired[index] = false;
208 public void SetControl(int index, byte? value) {
209 if (!Ring0.WaitIsaBusMutex(10))
212 if (value.HasValue) {
213 SaveDefaultFanControl(index);
216 WriteByte(FAN_CONTROL_MODE_REG[index], 0);
219 WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);
221 RestoreDefaultFanControl(index);
224 Ring0.ReleaseIsaBusMutex();
227 public Chip Chip { get { return chip; } }
228 public float?[] Voltages { get { return voltages; } }
229 public float?[] Temperatures { get { return temperatures; } }
230 public float?[] Fans { get { return fans; } }
231 public float?[] Controls { get { return controls; } }
233 public void Update() {
234 if (!Ring0.WaitIsaBusMutex(10))
237 for (int i = 0; i < voltages.Length; i++) {
238 float value = 0.008f * ReadByte(VOLTAGE_REG[i]);
239 bool valid = value > 0;
241 // check if battery voltage monitor is enabled
242 if (valid && VOLTAGE_REG[i] == VOLTAGE_VBAT_REG)
243 valid = (ReadByte(0x005D) & 0x01) > 0;
245 voltages[i] = valid ? value : (float?)null;
248 for (int i = TEMPERATURE_REG.Length - 1; i >= 0 ; i--) {
249 int value = ((sbyte)ReadByte(TEMPERATURE_REG[i])) << 1;
250 if (TEMPERATURE_HALF_BIT[i] > 0) {
251 value |= ((ReadByte(TEMPERATURE_HALF_REG[i]) >>
252 TEMPERATURE_HALF_BIT[i]) & 0x1);
255 byte source = ReadByte(TEMPERATURE_SRC_REG[i]);
257 float? temperature = 0.5f * value;
258 if (temperature > 125 || temperature < -55)
263 switch ((SourceNCT6771F)source) {
264 case SourceNCT6771F.PECI_0: temperatures[0] = temperature; break;
265 case SourceNCT6771F.CPUTIN: temperatures[1] = temperature; break;
266 case SourceNCT6771F.AUXTIN: temperatures[2] = temperature; break;
267 case SourceNCT6771F.SYSTIN: temperatures[3] = temperature; break;
271 switch ((SourceNCT6776F)source) {
272 case SourceNCT6776F.PECI_0: temperatures[0] = temperature; break;
273 case SourceNCT6776F.CPUTIN: temperatures[1] = temperature; break;
274 case SourceNCT6776F.AUXTIN: temperatures[2] = temperature; break;
275 case SourceNCT6776F.SYSTIN: temperatures[3] = temperature; break;
280 for (int i = 0; i < fans.Length; i++) {
281 byte high = ReadByte(FAN_RPM_REG[i]);
282 byte low = ReadByte((ushort)(FAN_RPM_REG[i] + 1));
283 int value = (high << 8) | low;
285 fans[i] = value > minFanRPM ? value : 0;
288 for (int i = 0; i < controls.Length; i++) {
289 int value = ReadByte(FAN_PWM_OUT_REG[i]);
290 controls[i] = value / 2.55f;
293 Ring0.ReleaseIsaBusMutex();
296 public string GetReport() {
297 StringBuilder r = new StringBuilder();
299 r.AppendLine("LPC " + this.GetType().Name);
301 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
302 r.Append("Chip revision: 0x");
303 r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
304 r.Append("Base Adress: 0x");
305 r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
308 if (!Ring0.WaitIsaBusMutex(100))
311 ushort[] addresses = new ushort[] {
312 0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
313 0x100, 0x110, 0x120, 0x130, 0x140, 0x150,
314 0x200, 0x220, 0x230, 0x240, 0x250,
315 0x300, 0x320, 0x330, 0x340,
316 0x400, 0x410, 0x420, 0x440, 0x450, 0x460,
318 0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670,
319 0xA00, 0xA10, 0xA20, 0xA30, 0xA50, 0xA60, 0xA70,
320 0xB00, 0xB10, 0xB20, 0xB30, 0xB50, 0xB60, 0xB70,
321 0xC00, 0xC10, 0xC20, 0xC30, 0xC50, 0xC60, 0xC70,
322 0xD00, 0xD10, 0xD20, 0xD30, 0xD50, 0xD60,
323 0xE00, 0xE10, 0xE20, 0xE30,
324 0xF00, 0xF10, 0xF20, 0xF30};
326 r.AppendLine("Hardware Monitor Registers");
328 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
330 foreach (ushort address in addresses) {
332 r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
334 for (ushort j = 0; j <= 0xF; j++) {
336 r.Append(ReadByte((ushort)(address | j)).ToString(
337 "X2", CultureInfo.InvariantCulture));
343 Ring0.ReleaseIsaBusMutex();