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