Hardware/LPC/IT87XX.cs
author sl
Sun, 03 Feb 2013 18:01:50 +0100
changeset 391 ca4c0e7ae75d
parent 353 b4e37f5b2669
permissions -rw-r--r--
Converted project to VisualStudio 2012.
Adding SoundGraphDisplay and SensorFrontView classes.
They were respectively based on SystemTray and SensorNotifyIcon.
SoundGraphDisplay is now able to load iMONDisplay.dll providing it lives on your PATH.
Adding option to sensor context menu for adding it into FrontView.
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
}