Hardware/LPC/NCT677X.cs
author sl
Thu, 01 Jan 2015 23:35:49 +0100
changeset 405 5715aefd2bcc
parent 355 17dbf781401e
permissions -rw-r--r--
SharpDisplay: Migrating to new robust client scheme.
     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-2012 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 };
    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 };
    64 
    65     private readonly ushort fanRpmBaseRegister;
    66     private readonly int minFanRPM;
    67 
    68     private bool[] restoreDefaultFanControlRequired = new bool[5];       
    69     private byte[] initialFanControlMode = new byte[5];
    70     private byte[] initialFanPwmCommand = new byte[5];
    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 SourceNCT6779D : 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           fans = new float?[5];
   221           fanRpmBaseRegister = 0x4C0;
   222 
   223           // min value RPM value with 13-bit fan counter
   224           minFanRPM = (int)(1.35e6 / 0x1FFF);
   225 
   226           controls = new float?[5];
   227 
   228           voltages = new float?[15];
   229           voltageRegisters = new ushort[] 
   230             { 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488, 
   231               0x489, 0x48A, 0x48B, 0x48C, 0x48D, 0x48E };
   232           voltageVBatRegister = 0x488;
   233 
   234           temperatures = new float?[7];
   235           temperaturesSource = new byte[] {
   236             (byte)SourceNCT6779D.PECI_0,
   237             (byte)SourceNCT6779D.CPUTIN,
   238             (byte)SourceNCT6779D.SYSTIN,
   239             (byte)SourceNCT6779D.AUXTIN0,
   240             (byte)SourceNCT6779D.AUXTIN1,
   241             (byte)SourceNCT6779D.AUXTIN2,
   242             (byte)SourceNCT6779D.AUXTIN3
   243           };
   244 
   245           temperatureRegister = new ushort[]
   246             { 0x027, 0x073, 0x075, 0x077, 0x079, 0x07B, 0x150 };
   247           temperatureHalfRegister = new ushort[]
   248             { 0, 0x074, 0x076, 0x078, 0x07A, 0x07C, 0x151 };              
   249           temperatureHalfBit = new int[]
   250             { -1, 7, 7, 7, 7, 7, 7 };
   251           temperatureSourceRegister = new ushort[] 
   252             { 0x621, 0x100, 0x200, 0x300, 0x800, 0x900, 0x622 };
   253 
   254           alternateTemperatureRegister = new ushort?[] 
   255             {null, 0x491, 0x490, 0x492, 0x493, 0x494, 0x495 };
   256 
   257           break;        
   258       }
   259     }
   260 
   261     private bool IsNuvotonVendor() {
   262       return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
   263         ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
   264     }
   265 
   266     public byte? ReadGPIO(int index) {
   267       return null;
   268     }
   269 
   270     public void WriteGPIO(int index, byte value) { }
   271 
   272 
   273     private void SaveDefaultFanControl(int index) {
   274       if (!restoreDefaultFanControlRequired[index]) {
   275         initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
   276         initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
   277         restoreDefaultFanControlRequired[index] = true;
   278       }
   279     }
   280 
   281     private void RestoreDefaultFanControl(int index) {
   282       if (restoreDefaultFanControlRequired[index]) {
   283         WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
   284         WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
   285         restoreDefaultFanControlRequired[index] = false;
   286       }
   287     }
   288 
   289     public void SetControl(int index, byte? value) {
   290       if (index < 0 || index >= controls.Length)
   291         throw new ArgumentOutOfRangeException("index");
   292 
   293       if (!Ring0.WaitIsaBusMutex(10))
   294         return;
   295 
   296       if (value.HasValue) {
   297         SaveDefaultFanControl(index);
   298 
   299         // set manual mode
   300         WriteByte(FAN_CONTROL_MODE_REG[index], 0);
   301 
   302         // set output value
   303         WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);  
   304       } else {
   305         RestoreDefaultFanControl(index);
   306       }
   307 
   308       Ring0.ReleaseIsaBusMutex();
   309     }   
   310 
   311     public Chip Chip { get { return chip; } }
   312     public float?[] Voltages { get { return voltages; } }
   313     public float?[] Temperatures { get { return temperatures; } }
   314     public float?[] Fans { get { return fans; } }
   315     public float?[] Controls { get { return controls; } }
   316 
   317     public void Update() {
   318       if (!Ring0.WaitIsaBusMutex(10))
   319         return;
   320 
   321       for (int i = 0; i < voltages.Length; i++) {
   322         float value = 0.008f * ReadByte(voltageRegisters[i]);
   323         bool valid = value > 0;
   324 
   325         // check if battery voltage monitor is enabled
   326         if (valid && voltageRegisters[i] == voltageVBatRegister) 
   327           valid = (ReadByte(0x005D) & 0x01) > 0;
   328 
   329         voltages[i] = valid ? value : (float?)null;
   330       }
   331 
   332       int temperatureSourceMask = 0;
   333       for (int i = temperatureRegister.Length - 1; i >= 0 ; i--) {
   334         int value = ((sbyte)ReadByte(temperatureRegister[i])) << 1;
   335         if (temperatureHalfBit[i] > 0) {
   336           value |= ((ReadByte(temperatureHalfRegister[i]) >>
   337             temperatureHalfBit[i]) & 0x1);
   338         }
   339 
   340         byte source = ReadByte(temperatureSourceRegister[i]);
   341         temperatureSourceMask |= 1 << source;
   342 
   343         float? temperature = 0.5f * value;
   344         if (temperature > 125 || temperature < -55)
   345           temperature = null;
   346 
   347         for (int j = 0; j < temperatures.Length; j++) 
   348           if (temperaturesSource[j] == source)
   349             temperatures[j] = temperature; 
   350       }
   351       for (int i = 0; i < alternateTemperatureRegister.Length; i++) {
   352         if (!alternateTemperatureRegister[i].HasValue)
   353           continue;
   354 
   355         if ((temperatureSourceMask & (1 << temperaturesSource[i])) > 0)
   356           continue;
   357 
   358         float? temperature = (sbyte)
   359           ReadByte(alternateTemperatureRegister[i].Value);
   360 
   361         if (temperature > 125 || temperature < -55)
   362           temperature = null;
   363 
   364         temperatures[i] = temperature;
   365       }
   366 
   367       for (int i = 0; i < fans.Length; i++) {
   368         byte high = ReadByte((ushort)(fanRpmBaseRegister + (i << 1)));
   369         byte low = ReadByte((ushort)(fanRpmBaseRegister + (i << 1) + 1));
   370         int value = (high << 8) | low;
   371 
   372         fans[i] = value > minFanRPM ? value : 0;
   373       }
   374 
   375       for (int i = 0; i < controls.Length; i++) {
   376         int value = ReadByte(FAN_PWM_OUT_REG[i]);
   377         controls[i] = value / 2.55f;
   378       }
   379 
   380       Ring0.ReleaseIsaBusMutex();
   381     }
   382 
   383     public string GetReport() {
   384       StringBuilder r = new StringBuilder();
   385 
   386       r.AppendLine("LPC " + this.GetType().Name);
   387       r.AppendLine();
   388       r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
   389       r.Append("Chip revision: 0x");
   390       r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
   391       r.Append("Base Adress: 0x");
   392       r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
   393       r.AppendLine();
   394 
   395       if (!Ring0.WaitIsaBusMutex(100))
   396         return r.ToString();
   397 
   398       ushort[] addresses = new ushort[] { 
   399         0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
   400         0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 
   401         0x200,        0x220, 0x230, 0x240, 0x250,
   402         0x300,        0x320, 0x330, 0x340, 
   403         0x400, 0x410, 0x420,        0x440, 0x450, 0x460, 0x480, 0x490, 0x4C0,
   404         0x500,                             0x550, 
   405         0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670, 
   406         0x800,
   407         0x900,
   408         0xA00, 0xA10, 0xA20, 0xA30,        0xA50, 0xA60, 0xA70, 
   409         0xB00, 0xB10, 0xB20, 0xB30,        0xB50, 0xB60, 0xB70, 
   410         0xC00, 0xC10, 0xC20, 0xC30,        0xC50, 0xC60, 0xC70,
   411         0xD00, 0xD10, 0xD20, 0xD30,        0xD50, 0xD60, 
   412         0xE00, 0xE10, 0xE20, 0xE30, 
   413         0xF00, 0xF10, 0xF20, 0xF30};
   414 
   415       r.AppendLine("Hardware Monitor Registers");
   416       r.AppendLine();
   417       r.AppendLine("       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   418       r.AppendLine();
   419       foreach (ushort address in addresses) {
   420           r.Append(" ");
   421           r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
   422           r.Append("  ");
   423           for (ushort j = 0; j <= 0xF; j++) {
   424             r.Append(" ");
   425             r.Append(ReadByte((ushort)(address | j)).ToString(
   426               "X2", CultureInfo.InvariantCulture));
   427           }
   428           r.AppendLine();
   429       }
   430       r.AppendLine();
   431 
   432       Ring0.ReleaseIsaBusMutex();
   433 
   434       return r.ToString();
   435     }
   436   }
   437 }