Hardware/LPC/NCT677X.cs
author moel.mich
Thu, 16 Feb 2012 22:10:30 +0000
changeset 341 5172a92c5c20
parent 276 04905193c432
child 344 3145aadca3d2
permissions -rw-r--r--
Added experimental support for ITE IT8771E super I/O chips.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     5   The contents of this file are subject to the Mozilla Public License Version
     6   1.1 (the "License"); you may not use this file except in compliance with
     7   the License. You may obtain a copy of the License at
     8  
     9   http://www.mozilla.org/MPL/
    10 
    11   Software distributed under the License is distributed on an "AS IS" basis,
    12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    13   for the specific language governing rights and limitations under the License.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    17   The Initial Developer of the Original Code is 
    18   Michael Möller <m.moeller@gmx.ch>.
    19   Portions created by the Initial Developer are Copyright (C) 2010-2011
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s):
    23 
    24   Alternatively, the contents of this file may be used under the terms of
    25   either the GNU General Public License Version 2 or later (the "GPL"), or
    26   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    27   in which case the provisions of the GPL or the LGPL are applicable instead
    28   of those above. If you wish to allow use of your version of this file only
    29   under the terms of either the GPL or the LGPL, and not to allow others to
    30   use your version of this file under the terms of the MPL, indicate your
    31   decision by deleting the provisions above and replace them with the notice
    32   and other provisions required by the GPL or the LGPL. If you do not delete
    33   the provisions above, a recipient may use your version of this file under
    34   the terms of any one of the MPL, the GPL or the LGPL.
    35  
    36 */
    37 
    38 using System.Globalization;
    39 using System.Text;
    40 
    41 namespace OpenHardwareMonitor.Hardware.LPC {
    42   internal class NCT677X : ISuperIO {
    43 
    44     private readonly ushort port;
    45     private readonly byte revision;
    46 
    47     private readonly Chip chip;
    48 
    49     private readonly float?[] voltages = new float?[9];
    50     private readonly float?[] temperatures = new float?[4];
    51     private readonly float?[] fans = new float?[0];
    52     private readonly float?[] controls = new float?[3];
    53 
    54     // Hardware Monitor
    55     private const uint ADDRESS_REGISTER_OFFSET = 0x05;
    56     private const uint DATA_REGISTER_OFFSET = 0x06;
    57     private const byte BANK_SELECT_REGISTER = 0x4E;
    58 
    59     private byte ReadByte(ushort address) {
    60       byte bank = (byte)(address >> 8);
    61       byte register = (byte)(address & 0xFF);
    62       Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
    63       Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
    64       Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
    65       return Ring0.ReadIoPort(port + DATA_REGISTER_OFFSET);
    66     }
    67 
    68     private void WriteByte(ushort address, byte value) {
    69       byte bank = (byte)(address >> 8);
    70       byte register = (byte)(address & 0xFF);
    71       Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
    72       Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
    73       Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
    74       Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, value);
    75     } 
    76 
    77     // Consts 
    78     private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
    79 
    80     // Hardware Monitor Registers    
    81     private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
    82     private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;
    83     private const ushort VOLTAGE_VBAT_REG = 0x0551;
    84 
    85     private readonly ushort[] TEMPERATURE_REG = 
    86       { 0x027, 0x73, 0x75, 0x77, 0x150, 0x250, 0x62B, 0x62C, 0x62D };
    87     private readonly ushort[] TEMPERATURE_HALF_REG = 
    88       { 0, 0x74, 0x76, 0x78, 0x151, 0x251, 0x62E, 0x62E, 0x62E };    
    89     private readonly ushort[] TEMPERATURE_SRC_REG = 
    90       { 0x621, 0x100, 0x200, 0x300, 0x622, 0x623, 0x624, 0x625, 0x626 };
    91     private readonly int[] TEMPERATURE_HALF_BIT =
    92       { -1, 7, 7, 7, 7, 7, 0, 1, 2 };
    93     private readonly ushort[] VOLTAGE_REG = 
    94       { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551 };
    95     private readonly ushort[] FAN_RPM_REG = 
    96       { 0x656, 0x658, 0x65A, 0x65C, 0x65E};
    97     private readonly ushort[] FAN_PWM_OUT_REG = 
    98       { 0x001, 0x003, 0x011 };
    99     private readonly ushort[] FAN_PWM_COMMAND_REG = 
   100       { 0x109, 0x209, 0x309 };
   101     private readonly ushort[] FAN_CONTROL_MODE_REG = 
   102       { 0x102, 0x202, 0x302 };
   103 
   104     private readonly int minFanRPM;
   105 
   106     private bool[] restoreDefaultFanControlRequired = { false, false, false };
   107     private byte[] initialFanControlMode = new byte[3];
   108     private byte[] initialFanPwmCommand = new byte[3];
   109 
   110     private enum SourceNCT6771F : byte {
   111       SYSTIN = 1,
   112       CPUTIN = 2,
   113       AUXTIN = 3,
   114       SMBUSMASTER = 4,
   115       PECI_0 = 5, 
   116       PECI_1 = 6, 
   117       PECI_2 = 7,
   118       PECI_3 = 8,
   119       PECI_4 = 9,
   120       PECI_5 = 10,
   121       PECI_6 = 11,
   122       PECI_7 = 12,
   123       PCH_CHIP_CPU_MAX_TEMP = 13,
   124       PCH_CHIP_TEMP = 14,
   125       PCH_CPU_TEMP = 15,
   126       PCH_MCH_TEMP = 16, 
   127       PCH_DIM0_TEMP = 17,
   128       PCH_DIM1_TEMP = 18,
   129       PCH_DIM2_TEMP = 19,
   130       PCH_DIM3_TEMP = 20
   131     }
   132 
   133     private enum SourceNCT6776F : byte {
   134       SYSTIN = 1,
   135       CPUTIN = 2,
   136       AUXTIN = 3,
   137       SMBUSMASTER_0 = 4,
   138       SMBUSMASTER_1 = 5,
   139       SMBUSMASTER_2 = 6,
   140       SMBUSMASTER_3 = 7,
   141       SMBUSMASTER_4 = 8,
   142       SMBUSMASTER_5 = 9,
   143       SMBUSMASTER_6 = 10,
   144       SMBUSMASTER_7 = 11,
   145       PECI_0 = 12,
   146       PECI_1 = 13,
   147       PCH_CHIP_CPU_MAX_TEMP = 14,
   148       PCH_CHIP_TEMP = 15,
   149       PCH_CPU_TEMP = 16,
   150       PCH_MCH_TEMP = 17,
   151       PCH_DIM0_TEMP = 18,
   152       PCH_DIM1_TEMP = 19,
   153       PCH_DIM2_TEMP = 20,
   154       PCH_DIM3_TEMP = 21,
   155       BYTE_TEMP = 22
   156     }
   157 
   158     public NCT677X(Chip chip, byte revision, ushort port) {
   159       this.chip = chip;
   160       this.revision = revision;
   161       this.port = port;
   162 
   163       if (!IsNuvotonVendor())
   164         return;
   165 
   166       switch (chip) {
   167         case LPC.Chip.NCT6771F:
   168           fans = new float?[4];
   169           // min value RPM value with 16-bit fan counter
   170           minFanRPM = (int)(1.35e6 / 0xFFFF);
   171           break;
   172         case LPC.Chip.NCT6776F:
   173           fans = new float?[5];
   174           // min value RPM value with 13-bit fan counter
   175           minFanRPM = (int)(1.35e6 / 0x1FFF);
   176           break;        
   177       }
   178     }
   179 
   180     private bool IsNuvotonVendor() {
   181       return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
   182         ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
   183     }
   184 
   185     public byte? ReadGPIO(int index) {
   186       return null;
   187     }
   188 
   189     public void WriteGPIO(int index, byte value) { }
   190 
   191 
   192     private void SaveDefaultFanControl(int index) {
   193       if (!restoreDefaultFanControlRequired[index]) {
   194         initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
   195         initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
   196         restoreDefaultFanControlRequired[index] = true;
   197       }
   198     }
   199 
   200     private void RestoreDefaultFanControl(int index) {
   201       if (restoreDefaultFanControlRequired[index]) {
   202         WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
   203         WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
   204         restoreDefaultFanControlRequired[index] = false;
   205       }
   206     }
   207 
   208     public void SetControl(int index, byte? value) {
   209       if (!Ring0.WaitIsaBusMutex(10))
   210         return;
   211 
   212       if (value.HasValue) {
   213         SaveDefaultFanControl(index);
   214 
   215         // set manual mode
   216         WriteByte(FAN_CONTROL_MODE_REG[index], 0);
   217 
   218         // set output value
   219         WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);  
   220       } else {
   221         RestoreDefaultFanControl(index);
   222       }
   223 
   224       Ring0.ReleaseIsaBusMutex();
   225     }   
   226 
   227     public Chip Chip { get { return chip; } }
   228     public float?[] Voltages { get { return voltages; } }
   229     public float?[] Temperatures { get { return temperatures; } }
   230     public float?[] Fans { get { return fans; } }
   231     public float?[] Controls { get { return controls; } }
   232 
   233     public void Update() {
   234       if (!Ring0.WaitIsaBusMutex(10))
   235         return;
   236 
   237       for (int i = 0; i < voltages.Length; i++) {
   238         float value = 0.008f * ReadByte(VOLTAGE_REG[i]);
   239         bool valid = value > 0;
   240 
   241         // check if battery voltage monitor is enabled
   242         if (valid && VOLTAGE_REG[i] == VOLTAGE_VBAT_REG) 
   243           valid = (ReadByte(0x005D) & 0x01) > 0;
   244 
   245         voltages[i] = valid ? value : (float?)null;
   246       }
   247 
   248       for (int i = TEMPERATURE_REG.Length - 1; i >= 0 ; i--) {
   249         int value = ((sbyte)ReadByte(TEMPERATURE_REG[i])) << 1;
   250         if (TEMPERATURE_HALF_BIT[i] > 0) {
   251           value |= ((ReadByte(TEMPERATURE_HALF_REG[i]) >>
   252             TEMPERATURE_HALF_BIT[i]) & 0x1);
   253         }
   254 
   255         byte source = ReadByte(TEMPERATURE_SRC_REG[i]);
   256 
   257         float? temperature = 0.5f * value;
   258         if (temperature > 125 || temperature < -55)
   259           temperature = null;
   260 
   261         switch (chip) {
   262           case Chip.NCT6771F:
   263             switch ((SourceNCT6771F)source) {
   264               case SourceNCT6771F.PECI_0: temperatures[0] = temperature; break;
   265               case SourceNCT6771F.CPUTIN: temperatures[1] = temperature; break;
   266               case SourceNCT6771F.AUXTIN: temperatures[2] = temperature; break;
   267               case SourceNCT6771F.SYSTIN: temperatures[3] = temperature; break;
   268               
   269             } break;
   270           case Chip.NCT6776F:
   271             switch ((SourceNCT6776F)source) {
   272               case SourceNCT6776F.PECI_0: temperatures[0] = temperature; break;
   273               case SourceNCT6776F.CPUTIN: temperatures[1] = temperature; break;
   274               case SourceNCT6776F.AUXTIN: temperatures[2] = temperature; break;
   275               case SourceNCT6776F.SYSTIN: temperatures[3] = temperature; break;              
   276             } break;
   277         }  
   278       }
   279 
   280       for (int i = 0; i < fans.Length; i++) {
   281         byte high = ReadByte(FAN_RPM_REG[i]);
   282         byte low = ReadByte((ushort)(FAN_RPM_REG[i] + 1));
   283         int value = (high << 8) | low;
   284 
   285         fans[i] = value > minFanRPM ? value : 0;
   286       }
   287 
   288       for (int i = 0; i < controls.Length; i++) {
   289         int value = ReadByte(FAN_PWM_OUT_REG[i]);
   290         controls[i] = value / 2.55f;
   291       }
   292 
   293       Ring0.ReleaseIsaBusMutex();
   294     }
   295 
   296     public string GetReport() {
   297       StringBuilder r = new StringBuilder();
   298 
   299       r.AppendLine("LPC " + this.GetType().Name);
   300       r.AppendLine();
   301       r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
   302       r.Append("Chip revision: 0x");
   303       r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
   304       r.Append("Base Adress: 0x");
   305       r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
   306       r.AppendLine();
   307 
   308       if (!Ring0.WaitIsaBusMutex(100))
   309         return r.ToString();
   310 
   311       ushort[] addresses = new ushort[] { 
   312         0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
   313         0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 
   314         0x200,        0x220, 0x230, 0x240, 0x250,
   315         0x300,        0x320, 0x330, 0x340, 
   316         0x400, 0x410, 0x420,        0x440, 0x450, 0x460, 
   317         0x500,                             0x550, 
   318         0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670, 
   319         0xA00, 0xA10, 0xA20, 0xA30,        0xA50, 0xA60, 0xA70, 
   320         0xB00, 0xB10, 0xB20, 0xB30,        0xB50, 0xB60, 0xB70, 
   321         0xC00, 0xC10, 0xC20, 0xC30,        0xC50, 0xC60, 0xC70,
   322         0xD00, 0xD10, 0xD20, 0xD30,        0xD50, 0xD60, 
   323         0xE00, 0xE10, 0xE20, 0xE30, 
   324         0xF00, 0xF10, 0xF20, 0xF30};
   325 
   326       r.AppendLine("Hardware Monitor Registers");
   327       r.AppendLine();
   328       r.AppendLine("       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   329       r.AppendLine();
   330       foreach (ushort address in addresses) {
   331           r.Append(" ");
   332           r.Append(address.ToString("X3", CultureInfo.InvariantCulture));
   333           r.Append("  ");
   334           for (ushort j = 0; j <= 0xF; j++) {
   335             r.Append(" ");
   336             r.Append(ReadByte((ushort)(address | j)).ToString(
   337               "X2", CultureInfo.InvariantCulture));
   338           }
   339           r.AppendLine();
   340       }
   341       r.AppendLine();
   342 
   343       Ring0.ReleaseIsaBusMutex();
   344 
   345       return r.ToString();
   346     }
   347   }
   348 }