Refactored the SuperIOHardware class.
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) 2010-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
12 using System.Globalization;
15 namespace OpenHardwareMonitor.Hardware.LPC {
16 internal class NCT677X : ISuperIO {
18 private readonly ushort port;
19 private readonly byte revision;
21 private readonly Chip chip;
23 private readonly float?[] voltages = new float?[0];
24 private readonly float?[] temperatures = new float?[0];
25 private readonly float?[] fans = new float?[0];
26 private readonly float?[] controls = new float?[0];
29 private const uint ADDRESS_REGISTER_OFFSET = 0x05;
30 private const uint DATA_REGISTER_OFFSET = 0x06;
31 private const byte BANK_SELECT_REGISTER = 0x4E;
33 private byte ReadByte(ushort address) {
34 byte bank = (byte)(address >> 8);
35 byte register = (byte)(address & 0xFF);
36 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
37 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
38 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
39 return Ring0.ReadIoPort(port + DATA_REGISTER_OFFSET);
42 private void WriteByte(ushort address, byte value) {
43 byte bank = (byte)(address >> 8);
44 byte register = (byte)(address & 0xFF);
45 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
46 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
47 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
48 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, value);
52 private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
54 // Hardware Monitor Registers
55 private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
56 private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;
58 private readonly ushort[] FAN_PWM_OUT_REG =
59 { 0x001, 0x003, 0x011, 0x013, 0x015, 0x017 };
60 private readonly ushort[] FAN_PWM_COMMAND_REG =
61 { 0x109, 0x209, 0x309, 0x809, 0x909, 0xA09 };
62 private readonly ushort[] FAN_CONTROL_MODE_REG =
63 { 0x102, 0x202, 0x302, 0x802, 0x902, 0xA02 };
65 private readonly ushort fanRpmBaseRegister;
66 private readonly int minFanRPM;
68 private bool[] restoreDefaultFanControlRequired = new bool[6];
69 private byte[] initialFanControlMode = new byte[6];
70 private byte[] initialFanPwmCommand = new byte[6];
72 private readonly ushort[] voltageRegisters;
73 private readonly ushort voltageVBatRegister;
75 private readonly byte[] temperaturesSource;
77 private readonly ushort[] temperatureRegister;
78 private readonly ushort[] temperatureHalfRegister;
79 private readonly int[] temperatureHalfBit;
80 private readonly ushort[] temperatureSourceRegister;
82 private readonly ushort?[] alternateTemperatureRegister;
84 private enum SourceNCT6771F : byte {
97 PCH_CHIP_CPU_MAX_TEMP = 13,
107 private enum SourceNCT6776F : byte {
121 PCH_CHIP_CPU_MAX_TEMP = 14,
132 private enum SourceNCT67XXD : byte {
149 PCH_CHIP_CPU_MAX_TEMP = 18,
160 public NCT677X(Chip chip, byte revision, ushort port) {
162 this.revision = revision;
165 if (!IsNuvotonVendor())
171 if (chip == Chip.NCT6771F) {
172 fans = new float?[4];
174 // min value RPM value with 16-bit fan counter
175 minFanRPM = (int)(1.35e6 / 0xFFFF);
177 temperaturesSource = new byte[] {
178 (byte)SourceNCT6771F.PECI_0,
179 (byte)SourceNCT6771F.CPUTIN,
180 (byte)SourceNCT6771F.AUXTIN,
181 (byte)SourceNCT6771F.SYSTIN
184 fans = new float?[5];
186 // min value RPM value with 13-bit fan counter
187 minFanRPM = (int)(1.35e6 / 0x1FFF);
189 temperaturesSource = new byte[] {
190 (byte)SourceNCT6776F.PECI_0,
191 (byte)SourceNCT6776F.CPUTIN,
192 (byte)SourceNCT6776F.AUXTIN,
193 (byte)SourceNCT6776F.SYSTIN
196 fanRpmBaseRegister = 0x656;
198 controls = new float?[3];
200 voltages = new float?[9];
201 voltageRegisters = new ushort[]
202 { 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x550, 0x551 };
203 voltageVBatRegister = 0x551;
205 temperatures = new float?[4];
206 temperatureRegister = new ushort[]
207 { 0x027, 0x073, 0x075, 0x077, 0x150, 0x250, 0x62B, 0x62C, 0x62D };
208 temperatureHalfRegister = new ushort[]
209 { 0, 0x074, 0x076, 0x078, 0x151, 0x251, 0x62E, 0x62E, 0x62E };
210 temperatureHalfBit = new int[]
211 { -1, 7, 7, 7, 7, 7, 0, 1, 2 };
212 temperatureSourceRegister = new ushort[]
213 { 0x621, 0x100, 0x200, 0x300, 0x622, 0x623, 0x624, 0x625, 0x626 };
215 alternateTemperatureRegister = new ushort?[]
216 { null, null, null, null };
221 if (chip == Chip.NCT6779D) {
222 fans = new float?[5];
223 controls = new float?[5];
225 fans = new float?[6];
226 controls = new float?[6];
229 fanRpmBaseRegister = 0x4C0;
231 // min value RPM value with 13-bit fan counter
232 minFanRPM = (int)(1.35e6 / 0x1FFF);
234 voltages = new float?[15];
235 voltageRegisters = new ushort[]
236 { 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488,
237 0x489, 0x48A, 0x48B, 0x48C, 0x48D, 0x48E };
238 voltageVBatRegister = 0x488;
240 temperatures = new float?[7];
241 temperaturesSource = new byte[] {
242 (byte)SourceNCT67XXD.PECI_0,
243 (byte)SourceNCT67XXD.CPUTIN,
244 (byte)SourceNCT67XXD.SYSTIN,
245 (byte)SourceNCT67XXD.AUXTIN0,
246 (byte)SourceNCT67XXD.AUXTIN1,
247 (byte)SourceNCT67XXD.AUXTIN2,
248 (byte)SourceNCT67XXD.AUXTIN3
251 temperatureRegister = new ushort[]
252 { 0x027, 0x073, 0x075, 0x077, 0x079, 0x07B, 0x150 };
253 temperatureHalfRegister = new ushort[]
254 { 0, 0x074, 0x076, 0x078, 0x07A, 0x07C, 0x151 };
255 temperatureHalfBit = new int[]
256 { -1, 7, 7, 7, 7, 7, 7 };
257 temperatureSourceRegister = new ushort[]
258 { 0x621, 0x100, 0x200, 0x300, 0x800, 0x900, 0x622 };
260 alternateTemperatureRegister = new ushort?[]
261 {null, 0x491, 0x490, 0x492, 0x493, 0x494, 0x495 };
267 private bool IsNuvotonVendor() {
268 return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
269 ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
272 public byte? ReadGPIO(int index) {
276 public void WriteGPIO(int index, byte value) { }
279 private void SaveDefaultFanControl(int index) {
280 if (!restoreDefaultFanControlRequired[index]) {
281 initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
282 initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
283 restoreDefaultFanControlRequired[index] = true;
287 private void RestoreDefaultFanControl(int index) {
288 if (restoreDefaultFanControlRequired[index]) {
289 WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
290 WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
291 restoreDefaultFanControlRequired[index] = false;
295 public void SetControl(int index, byte? value) {
296 if (index < 0 || index >= controls.Length)
297 throw new ArgumentOutOfRangeException("index");
299 if (!Ring0.WaitIsaBusMutex(10))
302 if (value.HasValue) {
303 SaveDefaultFanControl(index);
306 WriteByte(FAN_CONTROL_MODE_REG[index], 0);
309 WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);
311 RestoreDefaultFanControl(index);
314 Ring0.ReleaseIsaBusMutex();
317 public Chip Chip { get { return chip; } }
318 public float?[] Voltages { get { return voltages; } }
319 public float?[] Temperatures { get { return temperatures; } }
320 public float?[] Fans { get { return fans; } }
321 public float?[] Controls { get { return controls; } }
323 public void Update() {
324 if (!Ring0.WaitIsaBusMutex(10))
327 for (int i = 0; i < voltages.Length; i++) {
328 float value = 0.008f * ReadByte(voltageRegisters[i]);
329 bool valid = value > 0;
331 // check if battery voltage monitor is enabled
332 if (valid && voltageRegisters[i] == voltageVBatRegister)
333 valid = (ReadByte(0x005D) & 0x01) > 0;
335 voltages[i] = valid ? value : (float?)null;
338 int temperatureSourceMask = 0;
339 for (int i = temperatureRegister.Length - 1; i >= 0 ; i--) {
340 int value = ((sbyte)ReadByte(temperatureRegister[i])) << 1;
341 if (temperatureHalfBit[i] > 0) {
342 value |= ((ReadByte(temperatureHalfRegister[i]) >>
343 temperatureHalfBit[i]) & 0x1);
346 byte source = ReadByte(temperatureSourceRegister[i]);
347 temperatureSourceMask |= 1 << source;
349 float? temperature = 0.5f * value;
350 if (temperature > 125 || temperature < -55)
353 for (int j = 0; j < temperatures.Length; j++)
354 if (temperaturesSource[j] == source)
355 temperatures[j] = temperature;
357 for (int i = 0; i < alternateTemperatureRegister.Length; i++) {
358 if (!alternateTemperatureRegister[i].HasValue)
361 if ((temperatureSourceMask & (1 << temperaturesSource[i])) > 0)
364 float? temperature = (sbyte)
365 ReadByte(alternateTemperatureRegister[i].Value);
367 if (temperature > 125 || temperature < -55)
370 temperatures[i] = temperature;
373 for (int i = 0; i < fans.Length; i++) {
374 byte high = ReadByte((ushort)(fanRpmBaseRegister + (i << 1)));
375 byte low = ReadByte((ushort)(fanRpmBaseRegister + (i << 1) + 1));
376 int value = (high << 8) | low;
378 fans[i] = value > minFanRPM ? value : 0;
381 for (int i = 0; i < controls.Length; i++) {
382 int value = ReadByte(FAN_PWM_OUT_REG[i]);
383 controls[i] = value / 2.55f;
386 Ring0.ReleaseIsaBusMutex();
389 public string GetReport() {
390 StringBuilder r = new StringBuilder();
392 r.AppendLine("LPC " + this.GetType().Name);
394 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
395 r.Append("Chip revision: 0x");
396 r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
397 r.Append("Base Adress: 0x");
398 r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
401 if (!Ring0.WaitIsaBusMutex(100))
404 ushort[] addresses = new ushort[] {
405 0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
406 0x100, 0x110, 0x120, 0x130, 0x140, 0x150,
407 0x200, 0x220, 0x230, 0x240, 0x250, 0x260,
408 0x300, 0x320, 0x330, 0x340, 0x360,
409 0x400, 0x410, 0x420, 0x440, 0x450, 0x460, 0x480, 0x490, 0x4B0,
412 0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670,
413 0x700, 0x710, 0x720, 0x730,
414 0x800, 0x820, 0x830, 0x840,
415 0x900, 0x920, 0x930, 0x940, 0x960,
416 0xA00, 0xA10, 0xA20, 0xA30, 0xA40, 0xA50, 0xA60, 0xA70,
417 0xB00, 0xB10, 0xB20, 0xB30, 0xB50, 0xB60, 0xB70,
418 0xC00, 0xC10, 0xC20, 0xC30, 0xC50, 0xC60, 0xC70,
419 0xD00, 0xD10, 0xD20, 0xD30, 0xD50, 0xD60,
420 0xE00, 0xE10, 0xE20, 0xE30,
421 0xF00, 0xF10, 0xF20, 0xF30};
423 r.AppendLine("Hardware Monitor Registers");
425 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
427 foreach (ushort address in addresses) {
429 r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
431 for (ushort j = 0; j <= 0xF; j++) {
433 r.Append(ReadByte((ushort)(address | j)).ToString(
434 "X2", CultureInfo.InvariantCulture));
440 Ring0.ReleaseIsaBusMutex();