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) 2010-2012 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;
24 private readonly float?[] temperatures;
25 private readonly float?[] fans ;
26 private readonly float?[] controls;
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 };
60 private readonly ushort[] FAN_PWM_COMMAND_REG =
61 { 0x109, 0x209, 0x309, 0x809, 0x909 };
62 private readonly ushort[] FAN_CONTROL_MODE_REG =
63 { 0x102, 0x202, 0x302, 0x802, 0x902 };
65 private readonly ushort fanRpmBaseRegister;
66 private readonly int minFanRPM;
68 private bool[] restoreDefaultFanControlRequired = new bool[5];
69 private byte[] initialFanControlMode = new byte[5];
70 private byte[] initialFanPwmCommand = new byte[5];
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 SourceNCT6779D : 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 };
219 fans = new float?[5];
220 fanRpmBaseRegister = 0x4C0;
222 // min value RPM value with 13-bit fan counter
223 minFanRPM = (int)(1.35e6 / 0x1FFF);
225 controls = new float?[5];
227 voltages = new float?[15];
228 voltageRegisters = new ushort[]
229 { 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488,
230 0x489, 0x48A, 0x48B, 0x48C, 0x48D, 0x48E };
231 voltageVBatRegister = 0x488;
233 temperatures = new float?[7];
234 temperaturesSource = new byte[] {
235 (byte)SourceNCT6779D.PECI_0,
236 (byte)SourceNCT6779D.CPUTIN,
237 (byte)SourceNCT6779D.SYSTIN,
238 (byte)SourceNCT6779D.AUXTIN0,
239 (byte)SourceNCT6779D.AUXTIN1,
240 (byte)SourceNCT6779D.AUXTIN2,
241 (byte)SourceNCT6779D.AUXTIN3
244 temperatureRegister = new ushort[]
245 { 0x027, 0x073, 0x075, 0x077, 0x079, 0x07B, 0x150 };
246 temperatureHalfRegister = new ushort[]
247 { 0, 0x074, 0x076, 0x078, 0x07A, 0x07C, 0x151 };
248 temperatureHalfBit = new int[]
249 { -1, 7, 7, 7, 7, 7, 7 };
250 temperatureSourceRegister = new ushort[]
251 { 0x621, 0x100, 0x200, 0x300, 0x800, 0x900, 0x622 };
253 alternateTemperatureRegister = new ushort?[]
254 {null, 0x491, 0x490, 0x492, 0x493, 0x494, 0x495 };
260 private bool IsNuvotonVendor() {
261 return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
262 ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
265 public byte? ReadGPIO(int index) {
269 public void WriteGPIO(int index, byte value) { }
272 private void SaveDefaultFanControl(int index) {
273 if (!restoreDefaultFanControlRequired[index]) {
274 initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
275 initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
276 restoreDefaultFanControlRequired[index] = true;
280 private void RestoreDefaultFanControl(int index) {
281 if (restoreDefaultFanControlRequired[index]) {
282 WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
283 WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
284 restoreDefaultFanControlRequired[index] = false;
288 public void SetControl(int index, byte? value) {
289 if (index < 0 || index >= controls.Length)
290 throw new ArgumentOutOfRangeException("index");
292 if (!Ring0.WaitIsaBusMutex(10))
295 if (value.HasValue) {
296 SaveDefaultFanControl(index);
299 WriteByte(FAN_CONTROL_MODE_REG[index], 0);
302 WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);
304 RestoreDefaultFanControl(index);
307 Ring0.ReleaseIsaBusMutex();
310 public Chip Chip { get { return chip; } }
311 public float?[] Voltages { get { return voltages; } }
312 public float?[] Temperatures { get { return temperatures; } }
313 public float?[] Fans { get { return fans; } }
314 public float?[] Controls { get { return controls; } }
316 public void Update() {
317 if (!Ring0.WaitIsaBusMutex(10))
320 for (int i = 0; i < voltages.Length; i++) {
321 float value = 0.008f * ReadByte(voltageRegisters[i]);
322 bool valid = value > 0;
324 // check if battery voltage monitor is enabled
325 if (valid && voltageRegisters[i] == voltageVBatRegister)
326 valid = (ReadByte(0x005D) & 0x01) > 0;
328 voltages[i] = valid ? value : (float?)null;
331 int temperatureSourceMask = 0;
332 for (int i = temperatureRegister.Length - 1; i >= 0 ; i--) {
333 int value = ((sbyte)ReadByte(temperatureRegister[i])) << 1;
334 if (temperatureHalfBit[i] > 0) {
335 value |= ((ReadByte(temperatureHalfRegister[i]) >>
336 temperatureHalfBit[i]) & 0x1);
339 byte source = ReadByte(temperatureSourceRegister[i]);
340 temperatureSourceMask |= 1 << source;
342 float? temperature = 0.5f * value;
343 if (temperature > 125 || temperature < -55)
346 for (int j = 0; j < temperatures.Length; j++)
347 if (temperaturesSource[j] == source)
348 temperatures[j] = temperature;
350 for (int i = 0; i < alternateTemperatureRegister.Length; i++) {
351 if (!alternateTemperatureRegister[i].HasValue)
354 if ((temperatureSourceMask & (1 << temperaturesSource[i])) > 0)
357 temperatures[i] = ReadByte(alternateTemperatureRegister[i].Value);
360 for (int i = 0; i < fans.Length; i++) {
361 byte high = ReadByte((ushort)(fanRpmBaseRegister + (i << 1)));
362 byte low = ReadByte((ushort)(fanRpmBaseRegister + (i << 1) + 1));
363 int value = (high << 8) | low;
365 fans[i] = value > minFanRPM ? value : 0;
368 for (int i = 0; i < controls.Length; i++) {
369 int value = ReadByte(FAN_PWM_OUT_REG[i]);
370 controls[i] = value / 2.55f;
373 Ring0.ReleaseIsaBusMutex();
376 public string GetReport() {
377 StringBuilder r = new StringBuilder();
379 r.AppendLine("LPC " + this.GetType().Name);
381 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
382 r.Append("Chip revision: 0x");
383 r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
384 r.Append("Base Adress: 0x");
385 r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
388 if (!Ring0.WaitIsaBusMutex(100))
391 ushort[] addresses = new ushort[] {
392 0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
393 0x100, 0x110, 0x120, 0x130, 0x140, 0x150,
394 0x200, 0x220, 0x230, 0x240, 0x250,
395 0x300, 0x320, 0x330, 0x340,
396 0x400, 0x410, 0x420, 0x440, 0x450, 0x460, 0x480, 0x490, 0x4C0,
398 0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670,
401 0xA00, 0xA10, 0xA20, 0xA30, 0xA50, 0xA60, 0xA70,
402 0xB00, 0xB10, 0xB20, 0xB30, 0xB50, 0xB60, 0xB70,
403 0xC00, 0xC10, 0xC20, 0xC30, 0xC50, 0xC60, 0xC70,
404 0xD00, 0xD10, 0xD20, 0xD30, 0xD50, 0xD60,
405 0xE00, 0xE10, 0xE20, 0xE30,
406 0xF00, 0xF10, 0xF20, 0xF30};
408 r.AppendLine("Hardware Monitor Registers");
410 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
412 foreach (ushort address in addresses) {
414 r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
416 for (ushort j = 0; j <= 0xF; j++) {
418 r.Append(ReadByte((ushort)(address | j)).ToString(
419 "X2", CultureInfo.InvariantCulture));
425 Ring0.ReleaseIsaBusMutex();