Hardware/LPC/NCT677X.cs
author moel.mich
Sat, 07 Sep 2013 08:07:11 +0000
changeset 423 47f5dcaf8e9f
parent 417 4865cf06a7fa
permissions -rw-r--r--
Fixed an issue with the Nuvoton NCT6791D (hardware monitor i/o space lock wasn't disabled before attempting to read data).
     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-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System;
    12 using System.Globalization;
    13 using System.Text;
    14 
    15 namespace OpenHardwareMonitor.Hardware.LPC {
    16   internal class NCT677X : ISuperIO {
    17 
    18     private readonly ushort port;
    19     private readonly byte revision;
    20 
    21     private readonly Chip chip;
    22 
    23     private readonly bool isNuvotonVendor;
    24 
    25     private readonly float?[] voltages = new float?[0];
    26     private readonly float?[] temperatures = new float?[0];
    27     private readonly float?[] fans = new float?[0];
    28     private readonly float?[] controls = new float?[0];
    29 
    30     // Hardware Monitor
    31     private const uint ADDRESS_REGISTER_OFFSET = 0x05;
    32     private const uint DATA_REGISTER_OFFSET = 0x06;
    33     private const byte BANK_SELECT_REGISTER = 0x4E;
    34 
    35     private byte ReadByte(ushort address) {
    36       byte bank = (byte)(address >> 8);
    37       byte register = (byte)(address & 0xFF);
    38       Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
    39       Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
    40       Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
    41       return Ring0.ReadIoPort(port + DATA_REGISTER_OFFSET);
    42     }
    43 
    44     private void WriteByte(ushort address, byte value) {
    45       byte bank = (byte)(address >> 8);
    46       byte register = (byte)(address & 0xFF);
    47       Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
    48       Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
    49       Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
    50       Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, value);
    51     }
    52 
    53     // Consts 
    54     private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
    55 
    56     // Hardware Monitor Registers    
    57     private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
    58     private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;  
    59     
    60     private readonly ushort[] FAN_PWM_OUT_REG = 
    61       { 0x001, 0x003, 0x011, 0x013, 0x015, 0x017 };
    62     private readonly ushort[] FAN_PWM_COMMAND_REG = 
    63       { 0x109, 0x209, 0x309, 0x809, 0x909, 0xA09 };
    64     private readonly ushort[] FAN_CONTROL_MODE_REG = 
    65       { 0x102, 0x202, 0x302, 0x802, 0x902, 0xA02 };
    66 
    67     private readonly ushort fanRpmBaseRegister;
    68     private readonly int minFanRPM;
    69 
    70     private bool[] restoreDefaultFanControlRequired = new bool[6];       
    71     private byte[] initialFanControlMode = new byte[6];
    72     private byte[] initialFanPwmCommand = new byte[6];
    73 
    74     private readonly ushort[] voltageRegisters;
    75     private readonly ushort voltageVBatRegister;
    76 
    77     private readonly byte[] temperaturesSource;
    78 
    79     private readonly ushort[] temperatureRegister;
    80     private readonly ushort[] temperatureHalfRegister;
    81     private readonly int[] temperatureHalfBit;  
    82     private readonly ushort[] temperatureSourceRegister;        
    83 
    84     private readonly ushort?[] alternateTemperatureRegister;
    85 
    86     private enum SourceNCT6771F : byte {
    87       SYSTIN = 1,
    88       CPUTIN = 2,
    89       AUXTIN = 3,
    90       SMBUSMASTER = 4,
    91       PECI_0 = 5, 
    92       PECI_1 = 6, 
    93       PECI_2 = 7,
    94       PECI_3 = 8,
    95       PECI_4 = 9,
    96       PECI_5 = 10,
    97       PECI_6 = 11,
    98       PECI_7 = 12,
    99       PCH_CHIP_CPU_MAX_TEMP = 13,
   100       PCH_CHIP_TEMP = 14,
   101       PCH_CPU_TEMP = 15,
   102       PCH_MCH_TEMP = 16, 
   103       PCH_DIM0_TEMP = 17,
   104       PCH_DIM1_TEMP = 18,
   105       PCH_DIM2_TEMP = 19,
   106       PCH_DIM3_TEMP = 20
   107     }
   108 
   109     private enum SourceNCT6776F : byte {
   110       SYSTIN = 1,
   111       CPUTIN = 2,
   112       AUXTIN = 3,
   113       SMBUSMASTER_0 = 4,
   114       SMBUSMASTER_1 = 5,
   115       SMBUSMASTER_2 = 6,
   116       SMBUSMASTER_3 = 7,
   117       SMBUSMASTER_4 = 8,
   118       SMBUSMASTER_5 = 9,
   119       SMBUSMASTER_6 = 10,
   120       SMBUSMASTER_7 = 11,
   121       PECI_0 = 12,
   122       PECI_1 = 13,
   123       PCH_CHIP_CPU_MAX_TEMP = 14,
   124       PCH_CHIP_TEMP = 15,
   125       PCH_CPU_TEMP = 16,
   126       PCH_MCH_TEMP = 17,
   127       PCH_DIM0_TEMP = 18,
   128       PCH_DIM1_TEMP = 19,
   129       PCH_DIM2_TEMP = 20,
   130       PCH_DIM3_TEMP = 21,
   131       BYTE_TEMP = 22
   132     }
   133 
   134     private enum SourceNCT67XXD : byte {
   135       SYSTIN = 1,
   136       CPUTIN = 2,
   137       AUXTIN0 = 3,
   138       AUXTIN1 = 4,
   139       AUXTIN2 = 5,
   140       AUXTIN3 = 6,      
   141       SMBUSMASTER_0 = 8,
   142       SMBUSMASTER_1 = 9,
   143       SMBUSMASTER_2 = 10,
   144       SMBUSMASTER_3 = 11,
   145       SMBUSMASTER_4 = 12,
   146       SMBUSMASTER_5 = 13,
   147       SMBUSMASTER_6 = 14,
   148       SMBUSMASTER_7 = 15,
   149       PECI_0 = 16,
   150       PECI_1 = 17,
   151       PCH_CHIP_CPU_MAX_TEMP = 18,
   152       PCH_CHIP_TEMP = 19,
   153       PCH_CPU_TEMP = 20,
   154       PCH_MCH_TEMP = 21,
   155       PCH_DIM0_TEMP = 22,
   156       PCH_DIM1_TEMP = 23,
   157       PCH_DIM2_TEMP = 24,
   158       PCH_DIM3_TEMP = 25,
   159       BYTE_TEMP = 26
   160     }
   161 
   162     public NCT677X(Chip chip, byte revision, ushort port) {
   163       this.chip = chip;
   164       this.revision = revision;
   165       this.port = port;
   166 
   167 
   168       this.isNuvotonVendor = IsNuvotonVendor();
   169 
   170       if (!isNuvotonVendor)
   171         return;
   172 
   173       switch (chip) {
   174         case Chip.NCT6771F:
   175         case Chip.NCT6776F:          
   176           if (chip == Chip.NCT6771F) {
   177             fans = new float?[4];
   178 
   179             // min value RPM value with 16-bit fan counter
   180             minFanRPM = (int)(1.35e6 / 0xFFFF);
   181 
   182             temperaturesSource = new byte[] {
   183               (byte)SourceNCT6771F.PECI_0,
   184               (byte)SourceNCT6771F.CPUTIN,
   185               (byte)SourceNCT6771F.AUXTIN,
   186               (byte)SourceNCT6771F.SYSTIN
   187             };            
   188           } else {
   189             fans = new float?[5];
   190 
   191             // min value RPM value with 13-bit fan counter
   192             minFanRPM = (int)(1.35e6 / 0x1FFF);
   193 
   194             temperaturesSource = new byte[] {
   195               (byte)SourceNCT6776F.PECI_0,
   196               (byte)SourceNCT6776F.CPUTIN,
   197               (byte)SourceNCT6776F.AUXTIN,
   198               (byte)SourceNCT6776F.SYSTIN 
   199             };
   200           }
   201           fanRpmBaseRegister = 0x656;
   202 
   203           controls = new float?[3];
   204 
   205           voltages = new float?[9];
   206           voltageRegisters = new ushort[] 
   207             { 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x550, 0x551 };
   208           voltageVBatRegister = 0x551;
   209 
   210           temperatures = new float?[4];
   211           temperatureRegister = new ushort[]
   212             { 0x027, 0x073, 0x075, 0x077, 0x150, 0x250, 0x62B, 0x62C, 0x62D };
   213           temperatureHalfRegister = new ushort[]
   214             { 0, 0x074, 0x076, 0x078, 0x151, 0x251, 0x62E, 0x62E, 0x62E };
   215           temperatureHalfBit = new int[] 
   216             { -1, 7, 7, 7, 7, 7, 0, 1, 2 };
   217           temperatureSourceRegister = new ushort[]
   218             { 0x621, 0x100, 0x200, 0x300, 0x622, 0x623, 0x624, 0x625, 0x626 };
   219 
   220           alternateTemperatureRegister = new ushort?[] 
   221             { null, null, null, null };
   222           break;
   223 
   224         case Chip.NCT6779D:
   225         case Chip.NCT6791D:
   226           if (chip == Chip.NCT6779D) {
   227             fans = new float?[5];
   228             controls = new float?[5];
   229           } else {
   230             fans = new float?[6];
   231             controls = new float?[6];
   232           }
   233 
   234           fanRpmBaseRegister = 0x4C0;
   235 
   236           // min value RPM value with 13-bit fan counter
   237           minFanRPM = (int)(1.35e6 / 0x1FFF);
   238 
   239           voltages = new float?[15];
   240           voltageRegisters = new ushort[] 
   241             { 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488, 
   242               0x489, 0x48A, 0x48B, 0x48C, 0x48D, 0x48E };
   243           voltageVBatRegister = 0x488;
   244 
   245           temperatures = new float?[7];
   246           temperaturesSource = new byte[] {
   247             (byte)SourceNCT67XXD.PECI_0,
   248             (byte)SourceNCT67XXD.CPUTIN,
   249             (byte)SourceNCT67XXD.SYSTIN,
   250             (byte)SourceNCT67XXD.AUXTIN0,
   251             (byte)SourceNCT67XXD.AUXTIN1,
   252             (byte)SourceNCT67XXD.AUXTIN2,
   253             (byte)SourceNCT67XXD.AUXTIN3
   254           };
   255 
   256           temperatureRegister = new ushort[]
   257             { 0x027, 0x073, 0x075, 0x077, 0x079, 0x07B, 0x150 };
   258           temperatureHalfRegister = new ushort[]
   259             { 0, 0x074, 0x076, 0x078, 0x07A, 0x07C, 0x151 };              
   260           temperatureHalfBit = new int[]
   261             { -1, 7, 7, 7, 7, 7, 7 };
   262           temperatureSourceRegister = new ushort[] 
   263             { 0x621, 0x100, 0x200, 0x300, 0x800, 0x900, 0x622 };
   264 
   265           alternateTemperatureRegister = new ushort?[] 
   266             {null, 0x491, 0x490, 0x492, 0x493, 0x494, 0x495 };
   267 
   268           break;
   269       }
   270     }
   271 
   272     private bool IsNuvotonVendor() {
   273       return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
   274         ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
   275     }
   276 
   277     public byte? ReadGPIO(int index) {
   278       return null;
   279     }
   280 
   281     public void WriteGPIO(int index, byte value) { }
   282 
   283 
   284     private void SaveDefaultFanControl(int index) {
   285       if (!restoreDefaultFanControlRequired[index]) {
   286         initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
   287         initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
   288         restoreDefaultFanControlRequired[index] = true;
   289       }
   290     }
   291 
   292     private void RestoreDefaultFanControl(int index) {
   293       if (restoreDefaultFanControlRequired[index]) {
   294         WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
   295         WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
   296         restoreDefaultFanControlRequired[index] = false;
   297       }
   298     }
   299 
   300     public void SetControl(int index, byte? value) {
   301       if (!isNuvotonVendor)
   302         return;
   303 
   304       if (index < 0 || index >= controls.Length)
   305         throw new ArgumentOutOfRangeException("index");
   306 
   307       if (!Ring0.WaitIsaBusMutex(10))
   308         return;
   309 
   310       if (value.HasValue) {
   311         SaveDefaultFanControl(index);
   312 
   313         // set manual mode
   314         WriteByte(FAN_CONTROL_MODE_REG[index], 0);
   315 
   316         // set output value
   317         WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);  
   318       } else {
   319         RestoreDefaultFanControl(index);
   320       }
   321 
   322       Ring0.ReleaseIsaBusMutex();
   323     }   
   324 
   325     public Chip Chip { get { return chip; } }
   326     public float?[] Voltages { get { return voltages; } }
   327     public float?[] Temperatures { get { return temperatures; } }
   328     public float?[] Fans { get { return fans; } }
   329     public float?[] Controls { get { return controls; } }
   330 
   331     public void Update() {
   332       if (!isNuvotonVendor)
   333         return;
   334 
   335       if (!Ring0.WaitIsaBusMutex(10))
   336         return;
   337 
   338       for (int i = 0; i < voltages.Length; i++) {
   339         float value = 0.008f * ReadByte(voltageRegisters[i]);
   340         bool valid = value > 0;
   341 
   342         // check if battery voltage monitor is enabled
   343         if (valid && voltageRegisters[i] == voltageVBatRegister) 
   344           valid = (ReadByte(0x005D) & 0x01) > 0;
   345 
   346         voltages[i] = valid ? value : (float?)null;
   347       }
   348 
   349       int temperatureSourceMask = 0;
   350       for (int i = temperatureRegister.Length - 1; i >= 0 ; i--) {
   351         int value = ((sbyte)ReadByte(temperatureRegister[i])) << 1;
   352         if (temperatureHalfBit[i] > 0) {
   353           value |= ((ReadByte(temperatureHalfRegister[i]) >>
   354             temperatureHalfBit[i]) & 0x1);
   355         }
   356 
   357         byte source = ReadByte(temperatureSourceRegister[i]);
   358         temperatureSourceMask |= 1 << source;
   359 
   360         float? temperature = 0.5f * value;
   361         if (temperature > 125 || temperature < -55)
   362           temperature = null;
   363 
   364         for (int j = 0; j < temperatures.Length; j++) 
   365           if (temperaturesSource[j] == source)
   366             temperatures[j] = temperature; 
   367       }
   368       for (int i = 0; i < alternateTemperatureRegister.Length; i++) {
   369         if (!alternateTemperatureRegister[i].HasValue)
   370           continue;
   371 
   372         if ((temperatureSourceMask & (1 << temperaturesSource[i])) > 0)
   373           continue;
   374 
   375         float? temperature = (sbyte)
   376           ReadByte(alternateTemperatureRegister[i].Value);
   377 
   378         if (temperature > 125 || temperature < -55)
   379           temperature = null;
   380 
   381         temperatures[i] = temperature;
   382       }
   383 
   384       for (int i = 0; i < fans.Length; i++) {
   385         byte high = ReadByte((ushort)(fanRpmBaseRegister + (i << 1)));
   386         byte low = ReadByte((ushort)(fanRpmBaseRegister + (i << 1) + 1));
   387         int value = (high << 8) | low;
   388 
   389         fans[i] = value > minFanRPM ? value : 0;
   390       }
   391 
   392       for (int i = 0; i < controls.Length; i++) {
   393         int value = ReadByte(FAN_PWM_OUT_REG[i]);
   394         controls[i] = value / 2.55f;
   395       }
   396 
   397       Ring0.ReleaseIsaBusMutex();
   398     }
   399 
   400     public string GetReport() {
   401       StringBuilder r = new StringBuilder();
   402 
   403       r.AppendLine("LPC " + this.GetType().Name);
   404       r.AppendLine();
   405       r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
   406       r.Append("Chip revision: 0x");
   407       r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
   408       r.Append("Base Adress: 0x");
   409       r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
   410       r.AppendLine();
   411 
   412       if (!Ring0.WaitIsaBusMutex(100))
   413         return r.ToString();
   414 
   415       ushort[] addresses = new ushort[] { 
   416         0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
   417         0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 
   418         0x200,        0x220, 0x230, 0x240, 0x250, 0x260,
   419         0x300,        0x320, 0x330, 0x340,        0x360,
   420         0x400, 0x410, 0x420,        0x440, 0x450, 0x460, 0x480, 0x490, 0x4B0, 
   421                                                                 0x4C0, 0x4F0,
   422         0x500,                             0x550, 0x560,
   423         0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670, 
   424         0x700, 0x710, 0x720, 0x730,
   425         0x800,        0x820, 0x830, 0x840,
   426         0x900,        0x920, 0x930, 0x940,        0x960,
   427         0xA00, 0xA10, 0xA20, 0xA30, 0xA40, 0xA50, 0xA60, 0xA70, 
   428         0xB00, 0xB10, 0xB20, 0xB30,        0xB50, 0xB60, 0xB70, 
   429         0xC00, 0xC10, 0xC20, 0xC30,        0xC50, 0xC60, 0xC70,
   430         0xD00, 0xD10, 0xD20, 0xD30,        0xD50, 0xD60, 
   431         0xE00, 0xE10, 0xE20, 0xE30, 
   432         0xF00, 0xF10, 0xF20, 0xF30,
   433         0x8040};
   434 
   435       r.AppendLine("Hardware Monitor Registers");
   436       r.AppendLine();
   437       r.AppendLine("        00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   438       r.AppendLine();
   439       foreach (ushort address in addresses) {
   440           r.Append(" ");
   441           r.Append(address.ToString("X4", CultureInfo.InvariantCulture));
   442           r.Append("  ");
   443           for (ushort j = 0; j <= 0xF; j++) {
   444             r.Append(" ");
   445             r.Append(ReadByte((ushort)(address | j)).ToString(
   446               "X2", CultureInfo.InvariantCulture));
   447           }
   448           r.AppendLine();
   449       }
   450       r.AppendLine();
   451 
   452       Ring0.ReleaseIsaBusMutex();
   453 
   454       return r.ToString();
   455     }
   456   }
   457 }