Hardware/LPC/NCT677X.cs
author moel.mich
Sun, 27 May 2012 14:23:31 +0000
changeset 344 3145aadca3d2
parent 323 3f2d9ebacf38
child 355 17dbf781401e
permissions -rw-r--r--
Changed the license to the Mozilla Public License 2.0 and update the licensing information.
     1 /*
     2  
     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/.
     6  
     7   Copyright (C) 2010-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System.Globalization;
    12 using System.Text;
    13 
    14 namespace OpenHardwareMonitor.Hardware.LPC {
    15   internal class NCT677X : ISuperIO {
    16 
    17     private readonly ushort port;
    18     private readonly byte revision;
    19 
    20     private readonly Chip chip;
    21 
    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];
    26 
    27     // Hardware Monitor
    28     private const uint ADDRESS_REGISTER_OFFSET = 0x05;
    29     private const uint DATA_REGISTER_OFFSET = 0x06;
    30     private const byte BANK_SELECT_REGISTER = 0x4E;
    31 
    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);
    39     }
    40 
    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);
    48     } 
    49 
    50     // Consts 
    51     private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
    52 
    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;
    57 
    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 };
    76 
    77     private readonly int minFanRPM;
    78 
    79     private bool[] restoreDefaultFanControlRequired = { false, false, false };
    80     private byte[] initialFanControlMode = new byte[3];
    81     private byte[] initialFanPwmCommand = new byte[3];
    82 
    83     private enum SourceNCT6771F : byte {
    84       SYSTIN = 1,
    85       CPUTIN = 2,
    86       AUXTIN = 3,
    87       SMBUSMASTER = 4,
    88       PECI_0 = 5, 
    89       PECI_1 = 6, 
    90       PECI_2 = 7,
    91       PECI_3 = 8,
    92       PECI_4 = 9,
    93       PECI_5 = 10,
    94       PECI_6 = 11,
    95       PECI_7 = 12,
    96       PCH_CHIP_CPU_MAX_TEMP = 13,
    97       PCH_CHIP_TEMP = 14,
    98       PCH_CPU_TEMP = 15,
    99       PCH_MCH_TEMP = 16, 
   100       PCH_DIM0_TEMP = 17,
   101       PCH_DIM1_TEMP = 18,
   102       PCH_DIM2_TEMP = 19,
   103       PCH_DIM3_TEMP = 20
   104     }
   105 
   106     private enum SourceNCT6776F : byte {
   107       SYSTIN = 1,
   108       CPUTIN = 2,
   109       AUXTIN = 3,
   110       SMBUSMASTER_0 = 4,
   111       SMBUSMASTER_1 = 5,
   112       SMBUSMASTER_2 = 6,
   113       SMBUSMASTER_3 = 7,
   114       SMBUSMASTER_4 = 8,
   115       SMBUSMASTER_5 = 9,
   116       SMBUSMASTER_6 = 10,
   117       SMBUSMASTER_7 = 11,
   118       PECI_0 = 12,
   119       PECI_1 = 13,
   120       PCH_CHIP_CPU_MAX_TEMP = 14,
   121       PCH_CHIP_TEMP = 15,
   122       PCH_CPU_TEMP = 16,
   123       PCH_MCH_TEMP = 17,
   124       PCH_DIM0_TEMP = 18,
   125       PCH_DIM1_TEMP = 19,
   126       PCH_DIM2_TEMP = 20,
   127       PCH_DIM3_TEMP = 21,
   128       BYTE_TEMP = 22
   129     }
   130 
   131     public NCT677X(Chip chip, byte revision, ushort port) {
   132       this.chip = chip;
   133       this.revision = revision;
   134       this.port = port;
   135 
   136       if (!IsNuvotonVendor())
   137         return;
   138 
   139       switch (chip) {
   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);
   144           break;
   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);
   149           break;        
   150       }
   151     }
   152 
   153     private bool IsNuvotonVendor() {
   154       return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
   155         ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
   156     }
   157 
   158     public byte? ReadGPIO(int index) {
   159       return null;
   160     }
   161 
   162     public void WriteGPIO(int index, byte value) { }
   163 
   164 
   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;
   170       }
   171     }
   172 
   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;
   178       }
   179     }
   180 
   181     public void SetControl(int index, byte? value) {
   182       if (!Ring0.WaitIsaBusMutex(10))
   183         return;
   184 
   185       if (value.HasValue) {
   186         SaveDefaultFanControl(index);
   187 
   188         // set manual mode
   189         WriteByte(FAN_CONTROL_MODE_REG[index], 0);
   190 
   191         // set output value
   192         WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);  
   193       } else {
   194         RestoreDefaultFanControl(index);
   195       }
   196 
   197       Ring0.ReleaseIsaBusMutex();
   198     }   
   199 
   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; } }
   205 
   206     public void Update() {
   207       if (!Ring0.WaitIsaBusMutex(10))
   208         return;
   209 
   210       for (int i = 0; i < voltages.Length; i++) {
   211         float value = 0.008f * ReadByte(VOLTAGE_REG[i]);
   212         bool valid = value > 0;
   213 
   214         // check if battery voltage monitor is enabled
   215         if (valid && VOLTAGE_REG[i] == VOLTAGE_VBAT_REG) 
   216           valid = (ReadByte(0x005D) & 0x01) > 0;
   217 
   218         voltages[i] = valid ? value : (float?)null;
   219       }
   220 
   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);
   226         }
   227 
   228         byte source = ReadByte(TEMPERATURE_SRC_REG[i]);
   229 
   230         float? temperature = 0.5f * value;
   231         if (temperature > 125 || temperature < -55)
   232           temperature = null;
   233 
   234         switch (chip) {
   235           case Chip.NCT6771F:
   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;
   241               
   242             } break;
   243           case Chip.NCT6776F:
   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;              
   249             } break;
   250         }  
   251       }
   252 
   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;
   257 
   258         fans[i] = value > minFanRPM ? value : 0;
   259       }
   260 
   261       for (int i = 0; i < controls.Length; i++) {
   262         int value = ReadByte(FAN_PWM_OUT_REG[i]);
   263         controls[i] = value / 2.55f;
   264       }
   265 
   266       Ring0.ReleaseIsaBusMutex();
   267     }
   268 
   269     public string GetReport() {
   270       StringBuilder r = new StringBuilder();
   271 
   272       r.AppendLine("LPC " + this.GetType().Name);
   273       r.AppendLine();
   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));
   279       r.AppendLine();
   280 
   281       if (!Ring0.WaitIsaBusMutex(100))
   282         return r.ToString();
   283 
   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, 
   290         0x500,                             0x550, 
   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};
   298 
   299       r.AppendLine("Hardware Monitor Registers");
   300       r.AppendLine();
   301       r.AppendLine("       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   302       r.AppendLine();
   303       foreach (ushort address in addresses) {
   304           r.Append(" ");
   305           r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
   306           r.Append("  ");
   307           for (ushort j = 0; j <= 0xF; j++) {
   308             r.Append(" ");
   309             r.Append(ReadByte((ushort)(address | j)).ToString(
   310               "X2", CultureInfo.InvariantCulture));
   311           }
   312           r.AppendLine();
   313       }
   314       r.AppendLine();
   315 
   316       Ring0.ReleaseIsaBusMutex();
   317 
   318       return r.ToString();
   319     }
   320   }
   321 }