Hardware/LPC/IT87XX.cs
author moel.mich
Sun, 08 May 2011 22:10:13 +0000
changeset 279 6bce967ba1b5
parent 272 037a2d66082f
child 319 35cda448fc5f
permissions -rw-r--r--
Fixed the bus and core clock reading on AMD family 10h model Ah CPUs. The new "Core Performance Boost" feature of these CPUs resulted in very low accuracy of the bus speed (and as a consequence also an inaccurate TSC multiplier). This fixed Issue 205.
moel@16
     1
/*
moel@16
     2
  
moel@16
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@16
     4
moel@16
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@16
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@16
     7
  the License. You may obtain a copy of the License at
moel@16
     8
 
moel@16
     9
  http://www.mozilla.org/MPL/
moel@16
    10
moel@16
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@16
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@16
    13
  for the specific language governing rights and limitations under the License.
moel@16
    14
moel@16
    15
  The Original Code is the Open Hardware Monitor code.
moel@16
    16
moel@16
    17
  The Initial Developer of the Original Code is 
moel@16
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@16
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2010
moel@16
    20
  the Initial Developer. All Rights Reserved.
moel@16
    21
moel@16
    22
  Contributor(s):
moel@16
    23
moel@16
    24
  Alternatively, the contents of this file may be used under the terms of
moel@16
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@16
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@16
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@16
    28
  of those above. If you wish to allow use of your version of this file only
moel@16
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@16
    30
  use your version of this file under the terms of the MPL, indicate your
moel@16
    31
  decision by deleting the provisions above and replace them with the notice
moel@16
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@16
    33
  the provisions above, a recipient may use your version of this file under
moel@16
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@16
    35
 
moel@16
    36
*/
moel@16
    37
moel@166
    38
using System.Globalization;
moel@16
    39
using System.Text;
moel@16
    40
moel@16
    41
namespace OpenHardwareMonitor.Hardware.LPC {
moel@165
    42
  internal class IT87XX : ISuperIO {
moel@130
    43
       
moel@195
    44
    private readonly ushort address;
moel@195
    45
    private readonly Chip chip;
moel@195
    46
    private readonly byte version;
moel@16
    47
moel@228
    48
    private readonly ushort gpioAddress;
moel@228
    49
    private readonly int gpioCount;
moel@228
    50
moel@78
    51
    private readonly ushort addressReg;
moel@78
    52
    private readonly ushort dataReg;
moel@78
    53
moel@195
    54
    private readonly float?[] voltages = new float?[0];
moel@195
    55
    private readonly float?[] temperatures = new float?[0];
moel@195
    56
    private readonly float?[] fans = new float?[0];
moel@170
    57
moel@170
    58
    private readonly float voltageGain;
moel@272
    59
    private readonly bool has16bitFanCounter;
moel@16
    60
   
moel@16
    61
    // Consts
moel@16
    62
    private const byte ITE_VENDOR_ID = 0x90;
moel@16
    63
       
moel@16
    64
    // Environment Controller
moel@16
    65
    private const byte ADDRESS_REGISTER_OFFSET = 0x05;
moel@16
    66
    private const byte DATA_REGISTER_OFFSET = 0x06;
moel@16
    67
moel@16
    68
    // Environment Controller Registers    
moel@16
    69
    private const byte CONFIGURATION_REGISTER = 0x00;
moel@16
    70
    private const byte TEMPERATURE_BASE_REG = 0x29;
moel@16
    71
    private const byte VENDOR_ID_REGISTER = 0x58;
moel@272
    72
    private const byte FAN_TACHOMETER_DIVISOR_REGISTER = 0x0B;
moel@195
    73
    private readonly byte[] FAN_TACHOMETER_REG = 
moel@16
    74
      new byte[] { 0x0d, 0x0e, 0x0f, 0x80, 0x82 };
moel@195
    75
    private readonly byte[] FAN_TACHOMETER_EXT_REG =
moel@16
    76
      new byte[] { 0x18, 0x19, 0x1a, 0x81, 0x83 };
moel@78
    77
    private const byte VOLTAGE_BASE_REG = 0x20;
moel@78
    78
moel@78
    79
    private byte ReadByte(byte register, out bool valid) {
moel@236
    80
      Ring0.WriteIoPort(addressReg, register);
moel@236
    81
      byte value = Ring0.ReadIoPort(dataReg);
moel@236
    82
      valid = register == Ring0.ReadIoPort(addressReg);
moel@78
    83
      return value;
moel@228
    84
    }
moel@16
    85
moel@247
    86
    private bool WriteByte(byte register, byte value) {
moel@247
    87
      Ring0.WriteIoPort(addressReg, register);
moel@247
    88
      Ring0.WriteIoPort(dataReg, value);
moel@272
    89
      return register == Ring0.ReadIoPort(addressReg); 
moel@272
    90
    } 
moel@247
    91
moel@228
    92
    public byte? ReadGPIO(int index) {
moel@228
    93
      if (index >= gpioCount)
moel@228
    94
        return null;
moel@228
    95
moel@236
    96
      return Ring0.ReadIoPort((ushort)(gpioAddress + index));
moel@228
    97
    }
moel@228
    98
moel@228
    99
    public void WriteGPIO(int index, byte value) {
moel@228
   100
      if (index >= gpioCount)
moel@228
   101
        return;
moel@228
   102
moel@236
   103
      Ring0.WriteIoPort((ushort)(gpioAddress + index), value);
moel@228
   104
    }
moel@228
   105
moel@228
   106
    public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version) {
moel@272
   107
moel@16
   108
      this.address = address;
moel@130
   109
      this.chip = chip;
moel@145
   110
      this.version = version;
moel@78
   111
      this.addressReg = (ushort)(address + ADDRESS_REGISTER_OFFSET);
moel@78
   112
      this.dataReg = (ushort)(address + DATA_REGISTER_OFFSET);
moel@228
   113
      this.gpioAddress = gpioAddress;
moel@228
   114
moel@16
   115
      // Check vendor id
moel@78
   116
      bool valid;
moel@78
   117
      byte vendorId = ReadByte(VENDOR_ID_REGISTER, out valid);       
moel@78
   118
      if (!valid || vendorId != ITE_VENDOR_ID)
moel@16
   119
        return;
moel@16
   120
moel@16
   121
      // Bit 0x10 of the configuration register should always be 1
moel@78
   122
      if ((ReadByte(CONFIGURATION_REGISTER, out valid) & 0x10) == 0)
moel@78
   123
        return;
moel@78
   124
      if (!valid)
moel@16
   125
        return;
moel@16
   126
moel@130
   127
      voltages = new float?[9];
moel@130
   128
      temperatures = new float?[3];
moel@130
   129
      fans = new float?[5];
moel@170
   130
moel@170
   131
      // The IT8721F uses a 12mV resultion ADC, all others 16mV
moel@277
   132
      if (chip == Chip.IT8721F || chip == Chip.IT8728F) {
moel@170
   133
        voltageGain = 0.012f;
moel@170
   134
      } else {
moel@272
   135
        voltageGain = 0.016f;        
moel@272
   136
      }
moel@272
   137
moel@272
   138
      // older IT8721F revision do not have 16-bit fan counters
moel@272
   139
      if (chip == Chip.IT8712F && version < 8) {
moel@272
   140
        has16bitFanCounter = false;
moel@272
   141
      } else {
moel@272
   142
        has16bitFanCounter = true;
moel@170
   143
      }
moel@228
   144
moel@228
   145
      // Set the number of GPIO sets
moel@228
   146
      switch (chip) {
moel@228
   147
        case Chip.IT8712F:
moel@228
   148
        case Chip.IT8716F:
moel@228
   149
        case Chip.IT8718F:
moel@228
   150
        case Chip.IT8726F:
moel@228
   151
          gpioCount = 5;
moel@228
   152
          break;
moel@228
   153
        case Chip.IT8720F:
moel@228
   154
        case Chip.IT8721F:
moel@228
   155
          gpioCount = 8;
moel@228
   156
          break;
moel@277
   157
        case Chip.IT8728F:
moel@277
   158
          gpioCount = 0;
moel@277
   159
          break;
moel@228
   160
      }
moel@16
   161
    }
moel@16
   162
moel@130
   163
    public Chip Chip { get { return chip; } }
moel@130
   164
    public float?[] Voltages { get { return voltages; } }
moel@130
   165
    public float?[] Temperatures { get { return temperatures; } }
moel@130
   166
    public float?[] Fans { get { return fans; } }
moel@16
   167
moel@130
   168
    public string GetReport() {
moel@16
   169
      StringBuilder r = new StringBuilder();
moel@16
   170
moel@31
   171
      r.AppendLine("LPC " + this.GetType().Name);
moel@16
   172
      r.AppendLine();
moel@16
   173
      r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
moel@166
   174
      r.Append("Chip Version: 0x"); r.AppendLine(
moel@166
   175
        version.ToString("X", CultureInfo.InvariantCulture));
moel@166
   176
      r.Append("Base Address: 0x"); r.AppendLine(
moel@166
   177
        address.ToString("X4", CultureInfo.InvariantCulture));
moel@228
   178
      r.Append("GPIO Address: 0x"); r.AppendLine(
moel@228
   179
        gpioAddress.ToString("X4", CultureInfo.InvariantCulture));
moel@16
   180
      r.AppendLine();
moel@162
   181
moel@236
   182
      if (!Ring0.WaitIsaBusMutex(100))
moel@162
   183
        return r.ToString();
moel@162
   184
moel@16
   185
      r.AppendLine("Environment Controller Registers");
moel@16
   186
      r.AppendLine();
moel@16
   187
      r.AppendLine("      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
moel@16
   188
      r.AppendLine();
moel@16
   189
      for (int i = 0; i <= 0xA; i++) {
moel@166
   190
        r.Append(" "); 
moel@166
   191
        r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture)); 
moel@166
   192
        r.Append("  ");
moel@16
   193
        for (int j = 0; j <= 0xF; j++) {
moel@16
   194
          r.Append(" ");
moel@78
   195
          bool valid;
moel@78
   196
          byte value = ReadByte((byte)((i << 4) | j), out valid);
moel@195
   197
          r.Append(
moel@195
   198
            valid ? value.ToString("X2", CultureInfo.InvariantCulture) : "??");
moel@16
   199
        }
moel@16
   200
        r.AppendLine();
moel@16
   201
      }
moel@16
   202
      r.AppendLine();
moel@16
   203
moel@228
   204
      r.AppendLine("GPIO Registers");
moel@228
   205
      r.AppendLine();
moel@228
   206
      for (int i = 0; i < gpioCount; i++) {
moel@228
   207
        r.Append(" ");
moel@228
   208
        r.Append(ReadGPIO(i).Value.ToString("X2",
moel@228
   209
          CultureInfo.InvariantCulture));
moel@228
   210
      }
moel@228
   211
      r.AppendLine();
moel@228
   212
      r.AppendLine();
moel@228
   213
moel@236
   214
      Ring0.ReleaseIsaBusMutex();
moel@162
   215
moel@16
   216
      return r.ToString();
moel@16
   217
    }
moel@16
   218
moel@130
   219
    public void Update() {
moel@236
   220
      if (!Ring0.WaitIsaBusMutex(10))
moel@162
   221
        return;
moel@16
   222
moel@130
   223
      for (int i = 0; i < voltages.Length; i++) {
moel@78
   224
        bool valid;
moel@170
   225
        
moel@170
   226
        float value = 
moel@170
   227
          voltageGain * ReadByte((byte)(VOLTAGE_BASE_REG + i), out valid);   
moel@170
   228
moel@130
   229
        if (!valid)
moel@130
   230
          continue;
moel@130
   231
        if (value > 0)
moel@130
   232
          voltages[i] = value;  
moel@130
   233
        else
moel@130
   234
          voltages[i] = null;
moel@130
   235
      }
moel@130
   236
moel@130
   237
      for (int i = 0; i < temperatures.Length; i++) {
moel@130
   238
        bool valid;
moel@130
   239
        sbyte value = (sbyte)ReadByte(
moel@130
   240
          (byte)(TEMPERATURE_BASE_REG + i), out valid);
moel@78
   241
        if (!valid)
moel@78
   242
          continue;
moel@78
   243
moel@130
   244
        if (value < sbyte.MaxValue && value > 0)
moel@130
   245
          temperatures[i] = value;
moel@130
   246
        else
moel@130
   247
          temperatures[i] = null;       
moel@16
   248
      }
moel@16
   249
moel@272
   250
      if (has16bitFanCounter) {
moel@272
   251
        for (int i = 0; i < fans.Length; i++) {
moel@272
   252
          bool valid;
moel@272
   253
          int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
moel@272
   254
          if (!valid)
moel@272
   255
            continue;
moel@272
   256
          value |= ReadByte(FAN_TACHOMETER_EXT_REG[i], out valid) << 8;
moel@272
   257
          if (!valid)
moel@272
   258
            continue;
moel@16
   259
moel@272
   260
          if (value > 0x3f) {
moel@272
   261
            fans[i] = (value < 0xffff) ? 1.35e6f / (value * 2) : 0;
moel@272
   262
          } else {
moel@272
   263
            fans[i] = null;
moel@272
   264
          }
moel@272
   265
        }
moel@272
   266
      } else {
moel@272
   267
        for (int i = 0; i < fans.Length; i++) {
moel@272
   268
          bool valid;
moel@272
   269
          int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
moel@272
   270
          if (!valid)
moel@272
   271
            continue;
moel@272
   272
moel@272
   273
          int divisor = 2;
moel@272
   274
          if (i < 2) {
moel@272
   275
            int divisors = ReadByte(FAN_TACHOMETER_DIVISOR_REGISTER, out valid);
moel@272
   276
            if (!valid)
moel@272
   277
              continue;
moel@272
   278
            divisor = 1 << ((divisors >> (3 * i)) & 0x7);
moel@272
   279
          }
moel@272
   280
moel@272
   281
          if (value > 0) {
moel@272
   282
            fans[i] = (value < 0xff) ? 1.35e6f / (value * divisor) : 0;
moel@272
   283
          } else {
moel@272
   284
            fans[i] = null;
moel@272
   285
          }
moel@16
   286
        }
moel@162
   287
      }
moel@162
   288
moel@236
   289
      Ring0.ReleaseIsaBusMutex();
moel@126
   290
    }
moel@130
   291
  } 
moel@16
   292
}