Hardware/LPC/NCT677X.cs
author moel.mich
Sun, 21 Jul 2013 15:51:43 +0000
changeset 414 93dbee74f1e7
parent 413 362c5e77197d
child 415 a67db9051d19
permissions -rw-r--r--
Small correction for the Nuvoton NCT6791D super I/O chip.
     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 float?[] voltages;
    24     private readonly float?[] temperatures;
    25     private readonly float?[] fans ;
    26     private readonly float?[] controls;
    27 
    28     // Hardware Monitor
    29     private const uint ADDRESS_REGISTER_OFFSET = 0x05;
    30     private const uint DATA_REGISTER_OFFSET = 0x06;
    31     private const byte BANK_SELECT_REGISTER = 0x4E;
    32 
    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);
    40     }
    41 
    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);
    49     }
    50 
    51     // Consts 
    52     private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
    53 
    54     // Hardware Monitor Registers    
    55     private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
    56     private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;  
    57     
    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 };
    64 
    65     private readonly ushort fanRpmBaseRegister;
    66     private readonly int minFanRPM;
    67 
    68     private bool[] restoreDefaultFanControlRequired = new bool[6];       
    69     private byte[] initialFanControlMode = new byte[6];
    70     private byte[] initialFanPwmCommand = new byte[6];
    71 
    72     private readonly ushort[] voltageRegisters;
    73     private readonly ushort voltageVBatRegister;
    74 
    75     private readonly byte[] temperaturesSource;
    76 
    77     private readonly ushort[] temperatureRegister;
    78     private readonly ushort[] temperatureHalfRegister;
    79     private readonly int[] temperatureHalfBit;  
    80     private readonly ushort[] temperatureSourceRegister;        
    81 
    82     private readonly ushort?[] alternateTemperatureRegister;
    83 
    84     private enum SourceNCT6771F : byte {
    85       SYSTIN = 1,
    86       CPUTIN = 2,
    87       AUXTIN = 3,
    88       SMBUSMASTER = 4,
    89       PECI_0 = 5, 
    90       PECI_1 = 6, 
    91       PECI_2 = 7,
    92       PECI_3 = 8,
    93       PECI_4 = 9,
    94       PECI_5 = 10,
    95       PECI_6 = 11,
    96       PECI_7 = 12,
    97       PCH_CHIP_CPU_MAX_TEMP = 13,
    98       PCH_CHIP_TEMP = 14,
    99       PCH_CPU_TEMP = 15,
   100       PCH_MCH_TEMP = 16, 
   101       PCH_DIM0_TEMP = 17,
   102       PCH_DIM1_TEMP = 18,
   103       PCH_DIM2_TEMP = 19,
   104       PCH_DIM3_TEMP = 20
   105     }
   106 
   107     private enum SourceNCT6776F : byte {
   108       SYSTIN = 1,
   109       CPUTIN = 2,
   110       AUXTIN = 3,
   111       SMBUSMASTER_0 = 4,
   112       SMBUSMASTER_1 = 5,
   113       SMBUSMASTER_2 = 6,
   114       SMBUSMASTER_3 = 7,
   115       SMBUSMASTER_4 = 8,
   116       SMBUSMASTER_5 = 9,
   117       SMBUSMASTER_6 = 10,
   118       SMBUSMASTER_7 = 11,
   119       PECI_0 = 12,
   120       PECI_1 = 13,
   121       PCH_CHIP_CPU_MAX_TEMP = 14,
   122       PCH_CHIP_TEMP = 15,
   123       PCH_CPU_TEMP = 16,
   124       PCH_MCH_TEMP = 17,
   125       PCH_DIM0_TEMP = 18,
   126       PCH_DIM1_TEMP = 19,
   127       PCH_DIM2_TEMP = 20,
   128       PCH_DIM3_TEMP = 21,
   129       BYTE_TEMP = 22
   130     }
   131 
   132     private enum SourceNCT67XXD : byte {
   133       SYSTIN = 1,
   134       CPUTIN = 2,
   135       AUXTIN0 = 3,
   136       AUXTIN1 = 4,
   137       AUXTIN2 = 5,
   138       AUXTIN3 = 6,      
   139       SMBUSMASTER_0 = 8,
   140       SMBUSMASTER_1 = 9,
   141       SMBUSMASTER_2 = 10,
   142       SMBUSMASTER_3 = 11,
   143       SMBUSMASTER_4 = 12,
   144       SMBUSMASTER_5 = 13,
   145       SMBUSMASTER_6 = 14,
   146       SMBUSMASTER_7 = 15,
   147       PECI_0 = 16,
   148       PECI_1 = 17,
   149       PCH_CHIP_CPU_MAX_TEMP = 18,
   150       PCH_CHIP_TEMP = 19,
   151       PCH_CPU_TEMP = 20,
   152       PCH_MCH_TEMP = 21,
   153       PCH_DIM0_TEMP = 22,
   154       PCH_DIM1_TEMP = 23,
   155       PCH_DIM2_TEMP = 24,
   156       PCH_DIM3_TEMP = 25,
   157       BYTE_TEMP = 26
   158     }
   159 
   160     public NCT677X(Chip chip, byte revision, ushort port) {
   161       this.chip = chip;
   162       this.revision = revision;
   163       this.port = port;
   164 
   165       if (!IsNuvotonVendor())
   166         return;
   167 
   168       switch (chip) {
   169         case Chip.NCT6771F:
   170         case Chip.NCT6776F:          
   171           if (chip == Chip.NCT6771F) {
   172             fans = new float?[4];
   173 
   174             // min value RPM value with 16-bit fan counter
   175             minFanRPM = (int)(1.35e6 / 0xFFFF);
   176 
   177             temperaturesSource = new byte[] {
   178               (byte)SourceNCT6771F.PECI_0,
   179               (byte)SourceNCT6771F.CPUTIN,
   180               (byte)SourceNCT6771F.AUXTIN,
   181               (byte)SourceNCT6771F.SYSTIN
   182             };            
   183           } else {
   184             fans = new float?[5];
   185 
   186             // min value RPM value with 13-bit fan counter
   187             minFanRPM = (int)(1.35e6 / 0x1FFF);
   188 
   189             temperaturesSource = new byte[] {
   190               (byte)SourceNCT6776F.PECI_0,
   191               (byte)SourceNCT6776F.CPUTIN,
   192               (byte)SourceNCT6776F.AUXTIN,
   193               (byte)SourceNCT6776F.SYSTIN 
   194             };
   195           }
   196           fanRpmBaseRegister = 0x656;
   197 
   198           controls = new float?[3];
   199 
   200           voltages = new float?[9];
   201           voltageRegisters = new ushort[] 
   202             { 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x550, 0x551 };
   203           voltageVBatRegister = 0x551;
   204 
   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 };
   214 
   215           alternateTemperatureRegister = new ushort?[] 
   216             { null, null, null, null };
   217           break;
   218 
   219         case Chip.NCT6779D:
   220         case Chip.NCT6791D:
   221           if (chip == Chip.NCT6779D) {
   222             fans = new float?[5];
   223             controls = new float?[5];
   224           } else {
   225             fans = new float?[6];
   226             controls = new float?[6];
   227           }
   228 
   229           fanRpmBaseRegister = 0x4C0;
   230 
   231           // min value RPM value with 13-bit fan counter
   232           minFanRPM = (int)(1.35e6 / 0x1FFF);
   233 
   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;
   239 
   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
   249           };
   250 
   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 };
   259 
   260           alternateTemperatureRegister = new ushort?[] 
   261             {null, 0x491, 0x490, 0x492, 0x493, 0x494, 0x495 };
   262 
   263           break;
   264       }
   265     }
   266 
   267     private bool IsNuvotonVendor() {
   268       return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
   269         ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
   270     }
   271 
   272     public byte? ReadGPIO(int index) {
   273       return null;
   274     }
   275 
   276     public void WriteGPIO(int index, byte value) { }
   277 
   278 
   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;
   284       }
   285     }
   286 
   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;
   292       }
   293     }
   294 
   295     public void SetControl(int index, byte? value) {
   296       if (index < 0 || index >= controls.Length)
   297         throw new ArgumentOutOfRangeException("index");
   298 
   299       if (!Ring0.WaitIsaBusMutex(10))
   300         return;
   301 
   302       if (value.HasValue) {
   303         SaveDefaultFanControl(index);
   304 
   305         // set manual mode
   306         WriteByte(FAN_CONTROL_MODE_REG[index], 0);
   307 
   308         // set output value
   309         WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);  
   310       } else {
   311         RestoreDefaultFanControl(index);
   312       }
   313 
   314       Ring0.ReleaseIsaBusMutex();
   315     }   
   316 
   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; } }
   322 
   323     public void Update() {
   324       if (!Ring0.WaitIsaBusMutex(10))
   325         return;
   326 
   327       for (int i = 0; i < voltages.Length; i++) {
   328         float value = 0.008f * ReadByte(voltageRegisters[i]);
   329         bool valid = value > 0;
   330 
   331         // check if battery voltage monitor is enabled
   332         if (valid && voltageRegisters[i] == voltageVBatRegister) 
   333           valid = (ReadByte(0x005D) & 0x01) > 0;
   334 
   335         voltages[i] = valid ? value : (float?)null;
   336       }
   337 
   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);
   344         }
   345 
   346         byte source = ReadByte(temperatureSourceRegister[i]);
   347         temperatureSourceMask |= 1 << source;
   348 
   349         float? temperature = 0.5f * value;
   350         if (temperature > 125 || temperature < -55)
   351           temperature = null;
   352 
   353         for (int j = 0; j < temperatures.Length; j++) 
   354           if (temperaturesSource[j] == source)
   355             temperatures[j] = temperature; 
   356       }
   357       for (int i = 0; i < alternateTemperatureRegister.Length; i++) {
   358         if (!alternateTemperatureRegister[i].HasValue)
   359           continue;
   360 
   361         if ((temperatureSourceMask & (1 << temperaturesSource[i])) > 0)
   362           continue;
   363 
   364         float? temperature = (sbyte)
   365           ReadByte(alternateTemperatureRegister[i].Value);
   366 
   367         if (temperature > 125 || temperature < -55)
   368           temperature = null;
   369 
   370         temperatures[i] = temperature;
   371       }
   372 
   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;
   377 
   378         fans[i] = value > minFanRPM ? value : 0;
   379       }
   380 
   381       for (int i = 0; i < controls.Length; i++) {
   382         int value = ReadByte(FAN_PWM_OUT_REG[i]);
   383         controls[i] = value / 2.55f;
   384       }
   385 
   386       Ring0.ReleaseIsaBusMutex();
   387     }
   388 
   389     public string GetReport() {
   390       StringBuilder r = new StringBuilder();
   391 
   392       r.AppendLine("LPC " + this.GetType().Name);
   393       r.AppendLine();
   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));
   399       r.AppendLine();
   400 
   401       if (!Ring0.WaitIsaBusMutex(100))
   402         return r.ToString();
   403 
   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, 
   410                                                                 0x4C0, 0x4F0,
   411         0x500,                             0x550, 0x560,
   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};
   422 
   423       r.AppendLine("Hardware Monitor Registers");
   424       r.AppendLine();
   425       r.AppendLine("       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   426       r.AppendLine();
   427       foreach (ushort address in addresses) {
   428           r.Append(" ");
   429           r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
   430           r.Append("  ");
   431           for (ushort j = 0; j <= 0xF; j++) {
   432             r.Append(" ");
   433             r.Append(ReadByte((ushort)(address | j)).ToString(
   434               "X2", CultureInfo.InvariantCulture));
   435           }
   436           r.AppendLine();
   437       }
   438       r.AppendLine();
   439 
   440       Ring0.ReleaseIsaBusMutex();
   441 
   442       return r.ToString();
   443     }
   444   }
   445 }