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