Changed the license to the Mozilla Public License 2.0 and update the licensing information.
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-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
11 using System.Globalization;
14 namespace OpenHardwareMonitor.Hardware.LPC {
15 internal class NCT677X : ISuperIO {
17 private readonly ushort port;
18 private readonly byte revision;
20 private readonly Chip chip;
22 private readonly float?[] voltages = new float?[9];
23 private readonly float?[] temperatures = new float?[4];
24 private readonly float?[] fans = new float?[0];
25 private readonly float?[] controls = new float?[3];
28 private const uint ADDRESS_REGISTER_OFFSET = 0x05;
29 private const uint DATA_REGISTER_OFFSET = 0x06;
30 private const byte BANK_SELECT_REGISTER = 0x4E;
32 private byte ReadByte(ushort address) {
33 byte bank = (byte)(address >> 8);
34 byte register = (byte)(address & 0xFF);
35 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
36 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
37 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
38 return Ring0.ReadIoPort(port + DATA_REGISTER_OFFSET);
41 private void WriteByte(ushort address, byte value) {
42 byte bank = (byte)(address >> 8);
43 byte register = (byte)(address & 0xFF);
44 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
45 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
46 Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
47 Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, value);
51 private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
53 // Hardware Monitor Registers
54 private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
55 private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;
56 private const ushort VOLTAGE_VBAT_REG = 0x0551;
58 private readonly ushort[] TEMPERATURE_REG =
59 { 0x027, 0x73, 0x75, 0x77, 0x150, 0x250, 0x62B, 0x62C, 0x62D };
60 private readonly ushort[] TEMPERATURE_HALF_REG =
61 { 0, 0x74, 0x76, 0x78, 0x151, 0x251, 0x62E, 0x62E, 0x62E };
62 private readonly ushort[] TEMPERATURE_SRC_REG =
63 { 0x621, 0x100, 0x200, 0x300, 0x622, 0x623, 0x624, 0x625, 0x626 };
64 private readonly int[] TEMPERATURE_HALF_BIT =
65 { -1, 7, 7, 7, 7, 7, 0, 1, 2 };
66 private readonly ushort[] VOLTAGE_REG =
67 { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551 };
68 private readonly ushort[] FAN_RPM_REG =
69 { 0x656, 0x658, 0x65A, 0x65C, 0x65E};
70 private readonly ushort[] FAN_PWM_OUT_REG =
71 { 0x001, 0x003, 0x011 };
72 private readonly ushort[] FAN_PWM_COMMAND_REG =
73 { 0x109, 0x209, 0x309 };
74 private readonly ushort[] FAN_CONTROL_MODE_REG =
75 { 0x102, 0x202, 0x302 };
77 private readonly int minFanRPM;
79 private bool[] restoreDefaultFanControlRequired = { false, false, false };
80 private byte[] initialFanControlMode = new byte[3];
81 private byte[] initialFanPwmCommand = new byte[3];
83 private enum SourceNCT6771F : byte {
96 PCH_CHIP_CPU_MAX_TEMP = 13,
106 private enum SourceNCT6776F : byte {
120 PCH_CHIP_CPU_MAX_TEMP = 14,
131 public NCT677X(Chip chip, byte revision, ushort port) {
133 this.revision = revision;
136 if (!IsNuvotonVendor())
140 case LPC.Chip.NCT6771F:
141 fans = new float?[4];
142 // min value RPM value with 16-bit fan counter
143 minFanRPM = (int)(1.35e6 / 0xFFFF);
145 case LPC.Chip.NCT6776F:
146 fans = new float?[5];
147 // min value RPM value with 13-bit fan counter
148 minFanRPM = (int)(1.35e6 / 0x1FFF);
153 private bool IsNuvotonVendor() {
154 return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
155 ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
158 public byte? ReadGPIO(int index) {
162 public void WriteGPIO(int index, byte value) { }
165 private void SaveDefaultFanControl(int index) {
166 if (!restoreDefaultFanControlRequired[index]) {
167 initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
168 initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
169 restoreDefaultFanControlRequired[index] = true;
173 private void RestoreDefaultFanControl(int index) {
174 if (restoreDefaultFanControlRequired[index]) {
175 WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
176 WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
177 restoreDefaultFanControlRequired[index] = false;
181 public void SetControl(int index, byte? value) {
182 if (!Ring0.WaitIsaBusMutex(10))
185 if (value.HasValue) {
186 SaveDefaultFanControl(index);
189 WriteByte(FAN_CONTROL_MODE_REG[index], 0);
192 WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);
194 RestoreDefaultFanControl(index);
197 Ring0.ReleaseIsaBusMutex();
200 public Chip Chip { get { return chip; } }
201 public float?[] Voltages { get { return voltages; } }
202 public float?[] Temperatures { get { return temperatures; } }
203 public float?[] Fans { get { return fans; } }
204 public float?[] Controls { get { return controls; } }
206 public void Update() {
207 if (!Ring0.WaitIsaBusMutex(10))
210 for (int i = 0; i < voltages.Length; i++) {
211 float value = 0.008f * ReadByte(VOLTAGE_REG[i]);
212 bool valid = value > 0;
214 // check if battery voltage monitor is enabled
215 if (valid && VOLTAGE_REG[i] == VOLTAGE_VBAT_REG)
216 valid = (ReadByte(0x005D) & 0x01) > 0;
218 voltages[i] = valid ? value : (float?)null;
221 for (int i = TEMPERATURE_REG.Length - 1; i >= 0 ; i--) {
222 int value = ((sbyte)ReadByte(TEMPERATURE_REG[i])) << 1;
223 if (TEMPERATURE_HALF_BIT[i] > 0) {
224 value |= ((ReadByte(TEMPERATURE_HALF_REG[i]) >>
225 TEMPERATURE_HALF_BIT[i]) & 0x1);
228 byte source = ReadByte(TEMPERATURE_SRC_REG[i]);
230 float? temperature = 0.5f * value;
231 if (temperature > 125 || temperature < -55)
236 switch ((SourceNCT6771F)source) {
237 case SourceNCT6771F.PECI_0: temperatures[0] = temperature; break;
238 case SourceNCT6771F.CPUTIN: temperatures[1] = temperature; break;
239 case SourceNCT6771F.AUXTIN: temperatures[2] = temperature; break;
240 case SourceNCT6771F.SYSTIN: temperatures[3] = temperature; break;
244 switch ((SourceNCT6776F)source) {
245 case SourceNCT6776F.PECI_0: temperatures[0] = temperature; break;
246 case SourceNCT6776F.CPUTIN: temperatures[1] = temperature; break;
247 case SourceNCT6776F.AUXTIN: temperatures[2] = temperature; break;
248 case SourceNCT6776F.SYSTIN: temperatures[3] = temperature; break;
253 for (int i = 0; i < fans.Length; i++) {
254 byte high = ReadByte(FAN_RPM_REG[i]);
255 byte low = ReadByte((ushort)(FAN_RPM_REG[i] + 1));
256 int value = (high << 8) | low;
258 fans[i] = value > minFanRPM ? value : 0;
261 for (int i = 0; i < controls.Length; i++) {
262 int value = ReadByte(FAN_PWM_OUT_REG[i]);
263 controls[i] = value / 2.55f;
266 Ring0.ReleaseIsaBusMutex();
269 public string GetReport() {
270 StringBuilder r = new StringBuilder();
272 r.AppendLine("LPC " + this.GetType().Name);
274 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
275 r.Append("Chip revision: 0x");
276 r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
277 r.Append("Base Adress: 0x");
278 r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
281 if (!Ring0.WaitIsaBusMutex(100))
284 ushort[] addresses = new ushort[] {
285 0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
286 0x100, 0x110, 0x120, 0x130, 0x140, 0x150,
287 0x200, 0x220, 0x230, 0x240, 0x250,
288 0x300, 0x320, 0x330, 0x340,
289 0x400, 0x410, 0x420, 0x440, 0x450, 0x460,
291 0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670,
292 0xA00, 0xA10, 0xA20, 0xA30, 0xA50, 0xA60, 0xA70,
293 0xB00, 0xB10, 0xB20, 0xB30, 0xB50, 0xB60, 0xB70,
294 0xC00, 0xC10, 0xC20, 0xC30, 0xC50, 0xC60, 0xC70,
295 0xD00, 0xD10, 0xD20, 0xD30, 0xD50, 0xD60,
296 0xE00, 0xE10, 0xE20, 0xE30,
297 0xF00, 0xF10, 0xF20, 0xF30};
299 r.AppendLine("Hardware Monitor Registers");
301 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
303 foreach (ushort address in addresses) {
305 r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
307 for (ushort j = 0; j <= 0xF; j++) {
309 r.Append(ReadByte((ushort)(address | j)).ToString(
310 "X2", CultureInfo.InvariantCulture));
316 Ring0.ReleaseIsaBusMutex();