Hardware/LPC/IT87XX.cs
author Stephane Lenclud
Sat, 30 Jan 2016 23:01:51 +0100
branchMiniDisplay
changeset 454 f84878f52cd9
parent 353 b4e37f5b2669
permissions -rw-r--r--
Disabling Nuvoton NCT6791D because of fan full speed bug on Asus Z97 WS.
moel@16
     1
/*
moel@16
     2
 
moel@344
     3
  This Source Code Form is subject to the terms of the Mozilla Public
moel@344
     4
  License, v. 2.0. If a copy of the MPL was not distributed with this
moel@344
     5
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@16
     6
 
moel@344
     7
  Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@344
     8
	
moel@16
     9
*/
moel@16
    10
moel@166
    11
using System.Globalization;
moel@16
    12
using System.Text;
moel@382
    13
using System;
moel@16
    14
moel@16
    15
namespace OpenHardwareMonitor.Hardware.LPC {
moel@165
    16
  internal class IT87XX : ISuperIO {
moel@130
    17
       
moel@195
    18
    private readonly ushort address;
moel@195
    19
    private readonly Chip chip;
moel@195
    20
    private readonly byte version;
moel@16
    21
moel@228
    22
    private readonly ushort gpioAddress;
moel@228
    23
    private readonly int gpioCount;
moel@228
    24
moel@78
    25
    private readonly ushort addressReg;
moel@78
    26
    private readonly ushort dataReg;
moel@78
    27
moel@195
    28
    private readonly float?[] voltages = new float?[0];
moel@195
    29
    private readonly float?[] temperatures = new float?[0];
moel@195
    30
    private readonly float?[] fans = new float?[0];
moel@323
    31
    private readonly float?[] controls = new float?[0];
moel@170
    32
moel@170
    33
    private readonly float voltageGain;
moel@272
    34
    private readonly bool has16bitFanCounter;
moel@16
    35
   
moel@16
    36
    // Consts
moel@16
    37
    private const byte ITE_VENDOR_ID = 0x90;
moel@16
    38
       
moel@16
    39
    // Environment Controller
moel@16
    40
    private const byte ADDRESS_REGISTER_OFFSET = 0x05;
moel@16
    41
    private const byte DATA_REGISTER_OFFSET = 0x06;
moel@16
    42
moel@16
    43
    // Environment Controller Registers    
moel@16
    44
    private const byte CONFIGURATION_REGISTER = 0x00;
moel@16
    45
    private const byte TEMPERATURE_BASE_REG = 0x29;
moel@16
    46
    private const byte VENDOR_ID_REGISTER = 0x58;
moel@272
    47
    private const byte FAN_TACHOMETER_DIVISOR_REGISTER = 0x0B;
moel@195
    48
    private readonly byte[] FAN_TACHOMETER_REG = 
moel@382
    49
      { 0x0d, 0x0e, 0x0f, 0x80, 0x82 };
moel@195
    50
    private readonly byte[] FAN_TACHOMETER_EXT_REG =
moel@382
    51
      { 0x18, 0x19, 0x1a, 0x81, 0x83 };
moel@78
    52
    private const byte VOLTAGE_BASE_REG = 0x20;
moel@382
    53
    private readonly byte[] FAN_PWM_CTRL_REG = { 0x15, 0x16, 0x17 };
moel@382
    54
moel@382
    55
    private bool[] restoreDefaultFanPwmControlRequired = new bool[3];       
moel@382
    56
    private byte[] initialFanPwmControl = new byte[3];
moel@78
    57
moel@78
    58
    private byte ReadByte(byte register, out bool valid) {
moel@236
    59
      Ring0.WriteIoPort(addressReg, register);
moel@236
    60
      byte value = Ring0.ReadIoPort(dataReg);
moel@236
    61
      valid = register == Ring0.ReadIoPort(addressReg);
moel@78
    62
      return value;
moel@228
    63
    }
moel@16
    64
moel@247
    65
    private bool WriteByte(byte register, byte value) {
moel@247
    66
      Ring0.WriteIoPort(addressReg, register);
moel@247
    67
      Ring0.WriteIoPort(dataReg, value);
moel@272
    68
      return register == Ring0.ReadIoPort(addressReg); 
moel@320
    69
    }
moel@247
    70
moel@228
    71
    public byte? ReadGPIO(int index) {
moel@228
    72
      if (index >= gpioCount)
moel@228
    73
        return null;
moel@228
    74
moel@236
    75
      return Ring0.ReadIoPort((ushort)(gpioAddress + index));
moel@228
    76
    }
moel@228
    77
moel@228
    78
    public void WriteGPIO(int index, byte value) {
moel@228
    79
      if (index >= gpioCount)
moel@228
    80
        return;
moel@228
    81
moel@236
    82
      Ring0.WriteIoPort((ushort)(gpioAddress + index), value);
moel@341
    83
    } 
moel@228
    84
moel@382
    85
    private void SaveDefaultFanPwmControl(int index) {
moel@382
    86
      bool valid;
moel@382
    87
      if (!restoreDefaultFanPwmControlRequired[index]) {
moel@382
    88
        initialFanPwmControl[index] = 
moel@382
    89
          ReadByte(FAN_PWM_CTRL_REG[index], out valid);
moel@382
    90
        restoreDefaultFanPwmControlRequired[index] = true;
moel@382
    91
      }
moel@382
    92
    }
moel@382
    93
moel@382
    94
    private void RestoreDefaultFanPwmControl(int index) {
moel@382
    95
      if (restoreDefaultFanPwmControlRequired[index]) {
moel@382
    96
        WriteByte(FAN_PWM_CTRL_REG[index], initialFanPwmControl[index]);
moel@382
    97
        restoreDefaultFanPwmControlRequired[index] = false;
moel@382
    98
      }
moel@382
    99
    }
moel@382
   100
moel@382
   101
    public void SetControl(int index, byte? value) {
moel@382
   102
      if (index < 0 || index >= controls.Length)
moel@382
   103
        throw new ArgumentOutOfRangeException("index");
moel@382
   104
moel@382
   105
      if (!Ring0.WaitIsaBusMutex(10))
moel@382
   106
        return;
moel@382
   107
moel@382
   108
      if (value.HasValue) {
moel@382
   109
        SaveDefaultFanPwmControl(index);
moel@382
   110
moel@382
   111
        // set output value
moel@382
   112
        WriteByte(FAN_PWM_CTRL_REG[index], (byte)(value.Value >> 1));  
moel@382
   113
      } else {
moel@382
   114
        RestoreDefaultFanPwmControl(index);
moel@382
   115
      }
moel@382
   116
moel@382
   117
      Ring0.ReleaseIsaBusMutex();
moel@382
   118
    } 
moel@323
   119
moel@228
   120
    public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version) {
moel@272
   121
moel@16
   122
      this.address = address;
moel@130
   123
      this.chip = chip;
moel@145
   124
      this.version = version;
moel@78
   125
      this.addressReg = (ushort)(address + ADDRESS_REGISTER_OFFSET);
moel@78
   126
      this.dataReg = (ushort)(address + DATA_REGISTER_OFFSET);
moel@228
   127
      this.gpioAddress = gpioAddress;
moel@228
   128
moel@16
   129
      // Check vendor id
moel@78
   130
      bool valid;
moel@78
   131
      byte vendorId = ReadByte(VENDOR_ID_REGISTER, out valid);       
moel@78
   132
      if (!valid || vendorId != ITE_VENDOR_ID)
moel@16
   133
        return;
moel@16
   134
moel@16
   135
      // Bit 0x10 of the configuration register should always be 1
moel@78
   136
      if ((ReadByte(CONFIGURATION_REGISTER, out valid) & 0x10) == 0)
moel@78
   137
        return;
moel@78
   138
      if (!valid)
moel@16
   139
        return;
moel@16
   140
moel@130
   141
      voltages = new float?[9];
moel@130
   142
      temperatures = new float?[3];
moel@353
   143
      fans = new float?[chip == Chip.IT8705F ? 3 : 5];
moel@382
   144
      controls = new float?[3];
moel@170
   145
moel@320
   146
      // IT8721F, IT8728F and IT8772E use a 12mV resultion ADC, all others 16mV
moel@341
   147
      if (chip == Chip.IT8721F || chip == Chip.IT8728F || chip == Chip.IT8771E 
moel@341
   148
        || chip == Chip.IT8772E) 
moel@319
   149
      {
moel@170
   150
        voltageGain = 0.012f;
moel@170
   151
      } else {
moel@272
   152
        voltageGain = 0.016f;        
moel@272
   153
      }
moel@272
   154
moel@353
   155
      // older IT8705F and IT8721F revisions do not have 16-bit fan counters
moel@353
   156
      if ((chip == Chip.IT8705F && version < 3) || 
moel@353
   157
          (chip == Chip.IT8712F && version < 8)) 
moel@353
   158
      {
moel@272
   159
        has16bitFanCounter = false;
moel@272
   160
      } else {
moel@272
   161
        has16bitFanCounter = true;
moel@170
   162
      }
moel@228
   163
moel@228
   164
      // Set the number of GPIO sets
moel@228
   165
      switch (chip) {
moel@228
   166
        case Chip.IT8712F:
moel@228
   167
        case Chip.IT8716F:
moel@228
   168
        case Chip.IT8718F:
moel@228
   169
        case Chip.IT8726F:
moel@228
   170
          gpioCount = 5;
moel@228
   171
          break;
moel@228
   172
        case Chip.IT8720F:
moel@228
   173
        case Chip.IT8721F:
moel@228
   174
          gpioCount = 8;
moel@228
   175
          break;
moel@353
   176
        case Chip.IT8705F: 
moel@277
   177
        case Chip.IT8728F:
moel@341
   178
        case Chip.IT8771E:
moel@319
   179
        case Chip.IT8772E:
moel@277
   180
          gpioCount = 0;
moel@277
   181
          break;
moel@228
   182
      }
moel@16
   183
    }
moel@16
   184
moel@130
   185
    public Chip Chip { get { return chip; } }
moel@130
   186
    public float?[] Voltages { get { return voltages; } }
moel@130
   187
    public float?[] Temperatures { get { return temperatures; } }
moel@130
   188
    public float?[] Fans { get { return fans; } }
moel@323
   189
    public float?[] Controls { get { return controls; } }
moel@16
   190
moel@130
   191
    public string GetReport() {
moel@16
   192
      StringBuilder r = new StringBuilder();
moel@16
   193
moel@31
   194
      r.AppendLine("LPC " + this.GetType().Name);
moel@16
   195
      r.AppendLine();
moel@16
   196
      r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
moel@166
   197
      r.Append("Chip Version: 0x"); r.AppendLine(
moel@166
   198
        version.ToString("X", CultureInfo.InvariantCulture));
moel@166
   199
      r.Append("Base Address: 0x"); r.AppendLine(
moel@166
   200
        address.ToString("X4", CultureInfo.InvariantCulture));
moel@228
   201
      r.Append("GPIO Address: 0x"); r.AppendLine(
moel@228
   202
        gpioAddress.ToString("X4", CultureInfo.InvariantCulture));
moel@16
   203
      r.AppendLine();
moel@162
   204
moel@236
   205
      if (!Ring0.WaitIsaBusMutex(100))
moel@162
   206
        return r.ToString();
moel@162
   207
moel@16
   208
      r.AppendLine("Environment Controller Registers");
moel@16
   209
      r.AppendLine();
moel@16
   210
      r.AppendLine("      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
moel@16
   211
      r.AppendLine();
moel@16
   212
      for (int i = 0; i <= 0xA; i++) {
moel@166
   213
        r.Append(" "); 
moel@166
   214
        r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture)); 
moel@166
   215
        r.Append("  ");
moel@16
   216
        for (int j = 0; j <= 0xF; j++) {
moel@16
   217
          r.Append(" ");
moel@78
   218
          bool valid;
moel@78
   219
          byte value = ReadByte((byte)((i << 4) | j), out valid);
moel@195
   220
          r.Append(
moel@195
   221
            valid ? value.ToString("X2", CultureInfo.InvariantCulture) : "??");
moel@16
   222
        }
moel@16
   223
        r.AppendLine();
moel@16
   224
      }
moel@16
   225
      r.AppendLine();
moel@16
   226
moel@228
   227
      r.AppendLine("GPIO Registers");
moel@228
   228
      r.AppendLine();
moel@228
   229
      for (int i = 0; i < gpioCount; i++) {
moel@228
   230
        r.Append(" ");
moel@228
   231
        r.Append(ReadGPIO(i).Value.ToString("X2",
moel@228
   232
          CultureInfo.InvariantCulture));
moel@228
   233
      }
moel@228
   234
      r.AppendLine();
moel@228
   235
      r.AppendLine();
moel@228
   236
moel@236
   237
      Ring0.ReleaseIsaBusMutex();
moel@162
   238
moel@16
   239
      return r.ToString();
moel@16
   240
    }
moel@16
   241
moel@130
   242
    public void Update() {
moel@236
   243
      if (!Ring0.WaitIsaBusMutex(10))
moel@162
   244
        return;
moel@16
   245
moel@130
   246
      for (int i = 0; i < voltages.Length; i++) {
moel@78
   247
        bool valid;
moel@170
   248
        
moel@170
   249
        float value = 
moel@170
   250
          voltageGain * ReadByte((byte)(VOLTAGE_BASE_REG + i), out valid);   
moel@170
   251
moel@130
   252
        if (!valid)
moel@130
   253
          continue;
moel@130
   254
        if (value > 0)
moel@130
   255
          voltages[i] = value;  
moel@130
   256
        else
moel@130
   257
          voltages[i] = null;
moel@130
   258
      }
moel@130
   259
moel@130
   260
      for (int i = 0; i < temperatures.Length; i++) {
moel@130
   261
        bool valid;
moel@130
   262
        sbyte value = (sbyte)ReadByte(
moel@130
   263
          (byte)(TEMPERATURE_BASE_REG + i), out valid);
moel@78
   264
        if (!valid)
moel@78
   265
          continue;
moel@78
   266
moel@130
   267
        if (value < sbyte.MaxValue && value > 0)
moel@130
   268
          temperatures[i] = value;
moel@130
   269
        else
moel@130
   270
          temperatures[i] = null;       
moel@16
   271
      }
moel@16
   272
moel@272
   273
      if (has16bitFanCounter) {
moel@272
   274
        for (int i = 0; i < fans.Length; i++) {
moel@272
   275
          bool valid;
moel@272
   276
          int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
moel@272
   277
          if (!valid)
moel@272
   278
            continue;
moel@272
   279
          value |= ReadByte(FAN_TACHOMETER_EXT_REG[i], out valid) << 8;
moel@272
   280
          if (!valid)
moel@272
   281
            continue;
moel@16
   282
moel@272
   283
          if (value > 0x3f) {
moel@272
   284
            fans[i] = (value < 0xffff) ? 1.35e6f / (value * 2) : 0;
moel@272
   285
          } else {
moel@272
   286
            fans[i] = null;
moel@272
   287
          }
moel@272
   288
        }
moel@272
   289
      } else {
moel@272
   290
        for (int i = 0; i < fans.Length; i++) {
moel@272
   291
          bool valid;
moel@272
   292
          int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
moel@272
   293
          if (!valid)
moel@272
   294
            continue;
moel@272
   295
moel@272
   296
          int divisor = 2;
moel@272
   297
          if (i < 2) {
moel@272
   298
            int divisors = ReadByte(FAN_TACHOMETER_DIVISOR_REGISTER, out valid);
moel@272
   299
            if (!valid)
moel@272
   300
              continue;
moel@272
   301
            divisor = 1 << ((divisors >> (3 * i)) & 0x7);
moel@272
   302
          }
moel@272
   303
moel@272
   304
          if (value > 0) {
moel@272
   305
            fans[i] = (value < 0xff) ? 1.35e6f / (value * divisor) : 0;
moel@272
   306
          } else {
moel@272
   307
            fans[i] = null;
moel@272
   308
          }
moel@16
   309
        }
moel@162
   310
      }
moel@162
   311
moel@382
   312
      for (int i = 0; i < controls.Length; i++) {
moel@382
   313
        bool valid;
moel@382
   314
        byte value = ReadByte(FAN_PWM_CTRL_REG[i], out valid);
moel@382
   315
        if (!valid)
moel@382
   316
          continue;
moel@382
   317
moel@382
   318
        if ((value & 0x80) > 0) {
moel@382
   319
          // automatic operation (value can't be read)
moel@382
   320
          controls[i] = null;  
moel@382
   321
        } else {
moel@382
   322
          // software operation
moel@382
   323
          controls[i] = (float)Math.Round((value & 0x7F) * 100.0f / 0x7F);
moel@382
   324
        }
moel@382
   325
      }
moel@382
   326
moel@236
   327
      Ring0.ReleaseIsaBusMutex();
moel@126
   328
    }
moel@130
   329
  } 
moel@16
   330
}