Hardware/LPC/W836XX.cs
author moel.mich
Sat, 31 Dec 2011 17:31:04 +0000
changeset 324 c6ee430d6995
parent 236 763675f19ff4
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@34
     1
/*
moel@34
     2
  
moel@34
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@34
     4
moel@34
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@34
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@34
     7
  the License. You may obtain a copy of the License at
moel@34
     8
 
moel@34
     9
  http://www.mozilla.org/MPL/
moel@34
    10
moel@34
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@34
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@34
    13
  for the specific language governing rights and limitations under the License.
moel@34
    14
moel@34
    15
  The Original Code is the Open Hardware Monitor code.
moel@34
    16
moel@34
    17
  The Initial Developer of the Original Code is 
moel@34
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@323
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2011
moel@34
    20
  the Initial Developer. All Rights Reserved.
moel@34
    21
moel@34
    22
  Contributor(s):
moel@34
    23
moel@34
    24
  Alternatively, the contents of this file may be used under the terms of
moel@34
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@34
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@34
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@34
    28
  of those above. If you wish to allow use of your version of this file only
moel@34
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@34
    30
  use your version of this file under the terms of the MPL, indicate your
moel@34
    31
  decision by deleting the provisions above and replace them with the notice
moel@34
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@34
    33
  the provisions above, a recipient may use your version of this file under
moel@34
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@34
    35
 
moel@34
    36
*/
moel@34
    37
moel@34
    38
using System;
moel@166
    39
using System.Globalization;
moel@34
    40
using System.Text;
moel@34
    41
moel@34
    42
namespace OpenHardwareMonitor.Hardware.LPC {
moel@165
    43
  internal class W836XX : ISuperIO {
moel@34
    44
moel@195
    45
    private readonly ushort address;
moel@195
    46
    private readonly byte revision;
moel@34
    47
moel@195
    48
    private readonly Chip chip;
moel@34
    49
moel@195
    50
    private readonly float?[] voltages = new float?[0];
moel@195
    51
    private readonly float?[] temperatures = new float?[0];    
moel@195
    52
    private readonly float?[] fans = new float?[0];
moel@323
    53
    private readonly float?[] controls = new float?[0];
moel@34
    54
moel@195
    55
    private readonly bool[] peciTemperature = new bool[0];
moel@195
    56
    private readonly byte[] voltageRegister = new byte[0];
moel@195
    57
    private readonly byte[] voltageBank = new byte[0];
moel@195
    58
    private readonly float voltageGain = 0.008f;
moel@34
    59
moel@34
    60
    // Consts 
moel@34
    61
    private const ushort WINBOND_VENDOR_ID = 0x5CA3;
moel@34
    62
    private const byte HIGH_BYTE = 0x80;
moel@34
    63
moel@34
    64
    // Hardware Monitor
moel@34
    65
    private const byte ADDRESS_REGISTER_OFFSET = 0x05;
moel@34
    66
    private const byte DATA_REGISTER_OFFSET = 0x06;
moel@34
    67
moel@34
    68
    // Hardware Monitor Registers
moel@130
    69
    private const byte VOLTAGE_VBAT_REG = 0x51;
moel@54
    70
    private const byte BANK_SELECT_REGISTER = 0x4E;
moel@34
    71
    private const byte VENDOR_ID_REGISTER = 0x4F;
moel@56
    72
    private const byte TEMPERATURE_SOURCE_SELECT_REG = 0x49;
moel@56
    73
moel@195
    74
    private readonly byte[] TEMPERATURE_REG = new byte[] { 0x50, 0x50, 0x27 };
moel@195
    75
    private readonly byte[] TEMPERATURE_BANK = new byte[] { 1, 2, 0 };
moel@34
    76
moel@195
    77
    private readonly byte[] FAN_TACHO_REG = 
moel@195
    78
      new byte[] { 0x28, 0x29, 0x2A, 0x3F, 0x53 };
moel@195
    79
    private readonly byte[] FAN_TACHO_BANK = 
moel@195
    80
      new byte[] { 0, 0, 0, 0, 5 };       
moel@195
    81
    private readonly byte[] FAN_BIT_REG =
moel@195
    82
      new byte[] { 0x47, 0x4B, 0x4C, 0x59, 0x5D };
moel@195
    83
    private readonly byte[] FAN_DIV_BIT0 = new byte[] { 36, 38, 30, 8, 10 };
moel@195
    84
    private readonly byte[] FAN_DIV_BIT1 = new byte[] { 37, 39, 31, 9, 11 };
moel@195
    85
    private readonly byte[] FAN_DIV_BIT2 = new byte[] { 5, 6, 7, 23, 15 };
moel@34
    86
moel@34
    87
    private byte ReadByte(byte bank, byte register) {
moel@236
    88
      Ring0.WriteIoPort(
moel@34
    89
         (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
moel@236
    90
      Ring0.WriteIoPort(
moel@34
    91
         (ushort)(address + DATA_REGISTER_OFFSET), bank);
moel@236
    92
      Ring0.WriteIoPort(
moel@34
    93
         (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
moel@236
    94
      return Ring0.ReadIoPort(
moel@34
    95
        (ushort)(address + DATA_REGISTER_OFFSET));
moel@130
    96
    } 
moel@34
    97
moel@56
    98
    private void WriteByte(byte bank, byte register, byte value) {
moel@236
    99
      Ring0.WriteIoPort(
moel@56
   100
         (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
moel@236
   101
      Ring0.WriteIoPort(
moel@56
   102
         (ushort)(address + DATA_REGISTER_OFFSET), bank);
moel@236
   103
      Ring0.WriteIoPort(
moel@56
   104
         (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
moel@236
   105
      Ring0.WriteIoPort(
moel@56
   106
         (ushort)(address + DATA_REGISTER_OFFSET), value); 
moel@56
   107
    }
moel@228
   108
moel@228
   109
    public byte? ReadGPIO(int index) {
moel@228
   110
      return null;
moel@228
   111
    }
moel@228
   112
moel@228
   113
    public void WriteGPIO(int index, byte value) { }
moel@323
   114
moel@323
   115
    public void SetControl(int index, byte? value) { }   
moel@323
   116
moel@130
   117
    public W836XX(Chip chip, byte revision, ushort address) {
moel@34
   118
      this.address = address;
moel@34
   119
      this.revision = revision;
moel@130
   120
      this.chip = chip;
moel@34
   121
moel@130
   122
      if (!IsWinbondVendor())
moel@130
   123
        return;
moel@130
   124
      
moel@130
   125
      temperatures = new float?[3];
moel@130
   126
      peciTemperature = new bool[3];
moel@56
   127
      switch (chip) {
moel@63
   128
        case Chip.W83667HG:
moel@63
   129
        case Chip.W83667HGB:
moel@130
   130
          // note temperature sensor registers that read PECI
moel@63
   131
          byte flag = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
moel@130
   132
          peciTemperature[0] = (flag & 0x04) != 0;
moel@130
   133
          peciTemperature[1] = (flag & 0x40) != 0;
moel@130
   134
          peciTemperature[2] = false;
moel@63
   135
          break;
moel@63
   136
        case Chip.W83627DHG:        
moel@63
   137
        case Chip.W83627DHGP:
moel@152
   138
          // note temperature sensor registers that read PECI
moel@56
   139
          byte sel = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
moel@130
   140
          peciTemperature[0] = (sel & 0x07) != 0;
moel@130
   141
          peciTemperature[1] = (sel & 0x70) != 0;
moel@130
   142
          peciTemperature[2] = false;
moel@56
   143
          break;
moel@56
   144
        default:
moel@130
   145
          // no PECI support
moel@130
   146
          peciTemperature[0] = false;
moel@130
   147
          peciTemperature[1] = false;
moel@130
   148
          peciTemperature[2] = false;
moel@56
   149
          break;
moel@56
   150
      }
moel@34
   151
moel@34
   152
      switch (chip) {
moel@130
   153
        case Chip.W83627EHF:
moel@130
   154
          voltages = new float?[10];
moel@130
   155
          voltageRegister = new byte[] { 
moel@130
   156
            0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51, 0x52 };
moel@130
   157
          voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5, 5 };
moel@130
   158
          voltageGain = 0.008f;
moel@130
   159
          fans = new float?[5];
moel@130
   160
          break;
moel@34
   161
        case Chip.W83627DHG:
moel@130
   162
        case Chip.W83627DHGP:        
moel@34
   163
        case Chip.W83667HG:
moel@130
   164
        case Chip.W83667HGB:
moel@130
   165
          voltages = new float?[9];
moel@130
   166
          voltageRegister = new byte[] { 
moel@130
   167
            0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51 };
moel@130
   168
          voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5 };
moel@130
   169
          voltageGain = 0.008f;
moel@130
   170
          fans = new float?[5];
moel@34
   171
          break;
moel@54
   172
        case Chip.W83627HF:
moel@54
   173
        case Chip.W83627THF:
moel@67
   174
        case Chip.W83687THF:
moel@130
   175
          voltages = new float?[7];
moel@130
   176
          voltageRegister = new byte[] { 
moel@130
   177
            0x20, 0x21, 0x22, 0x23, 0x24, 0x50, 0x51 };
moel@130
   178
          voltageBank = new byte[] { 0, 0, 0, 0, 0, 5, 5 };
moel@130
   179
          voltageGain = 0.016f;
moel@130
   180
          fans = new float?[3];         
moel@34
   181
          break;
moel@34
   182
      }
moel@130
   183
    }    
moel@34
   184
moel@34
   185
    private bool IsWinbondVendor() {
moel@34
   186
      ushort vendorId =
moel@34
   187
        (ushort)((ReadByte(HIGH_BYTE, VENDOR_ID_REGISTER) << 8) |
moel@34
   188
           ReadByte(0, VENDOR_ID_REGISTER));
moel@34
   189
      return vendorId == WINBOND_VENDOR_ID;
moel@34
   190
    }
moel@34
   191
moel@167
   192
    private static ulong SetBit(ulong target, int bit, int value) {
moel@85
   193
      if ((value & 1) != value)
moel@85
   194
        throw new ArgumentException("Value must be one bit only.");
moel@85
   195
moel@85
   196
      if (bit < 0 || bit > 63)
moel@85
   197
        throw new ArgumentException("Bit out of range.");
moel@85
   198
moel@85
   199
      ulong mask = (((ulong)1) << bit);
moel@85
   200
      return value > 0 ? target | mask : target & ~mask;
moel@85
   201
    }
moel@85
   202
moel@130
   203
    public Chip Chip { get { return chip; } }
moel@130
   204
    public float?[] Voltages { get { return voltages; } }
moel@130
   205
    public float?[] Temperatures { get { return temperatures; } }
moel@130
   206
    public float?[] Fans { get { return fans; } }
moel@323
   207
    public float?[] Controls { get { return controls; } }
moel@56
   208
moel@130
   209
    public void Update() {
moel@236
   210
      if (!Ring0.WaitIsaBusMutex(10))
moel@162
   211
        return;
moel@130
   212
moel@130
   213
      for (int i = 0; i < voltages.Length; i++) {
moel@130
   214
        if (voltageRegister[i] != VOLTAGE_VBAT_REG) {
moel@58
   215
          // two special VCore measurement modes for W83627THF
moel@130
   216
          float fvalue;
moel@67
   217
          if ((chip == Chip.W83627HF || chip == Chip.W83627THF || 
moel@130
   218
            chip == Chip.W83687THF) && i == 0) 
moel@67
   219
          {
moel@58
   220
            byte vrmConfiguration = ReadByte(0, 0x18);
moel@130
   221
            int value = ReadByte(voltageBank[i], voltageRegister[i]);
moel@58
   222
            if ((vrmConfiguration & 0x01) == 0)
moel@130
   223
              fvalue = 0.016f * value; // VRM8 formula
moel@58
   224
            else
moel@130
   225
              fvalue = 0.00488f * value + 0.69f; // VRM9 formula
moel@58
   226
          } else {
moel@130
   227
            int value = ReadByte(voltageBank[i], voltageRegister[i]);
moel@130
   228
            fvalue = voltageGain * value;
moel@58
   229
          }
moel@130
   230
          if (fvalue > 0)
moel@130
   231
            voltages[i] = fvalue;
moel@130
   232
          else
moel@130
   233
            voltages[i] = null;
moel@34
   234
        } else {
moel@34
   235
          // Battery voltage
moel@34
   236
          bool valid = (ReadByte(0, 0x5D) & 0x01) > 0;
moel@34
   237
          if (valid) {
moel@130
   238
            voltages[i] = voltageGain * ReadByte(5, VOLTAGE_VBAT_REG);
moel@84
   239
          } else {
moel@130
   240
            voltages[i] = null;
moel@84
   241
          }
moel@34
   242
        }
moel@34
   243
      }
moel@34
   244
moel@130
   245
      for (int i = 0; i < temperatures.Length; i++) {
moel@130
   246
        int value = ((sbyte)ReadByte(TEMPERATURE_BANK[i], 
moel@130
   247
          TEMPERATURE_REG[i])) << 1;
moel@130
   248
        if (TEMPERATURE_BANK[i] > 0) 
moel@130
   249
          value |= ReadByte(TEMPERATURE_BANK[i],
moel@130
   250
            (byte)(TEMPERATURE_REG[i] + 1)) >> 7;
moel@63
   251
moel@56
   252
        float temperature = value / 2.0f;
moel@130
   253
        if (temperature <= 125 && temperature >= -55 && !peciTemperature[i]) {
moel@130
   254
          temperatures[i] = temperature;
moel@34
   255
        } else {
moel@130
   256
          temperatures[i] = null;
moel@34
   257
        }
moel@34
   258
      }
moel@34
   259
moel@85
   260
      ulong bits = 0;
moel@34
   261
      for (int i = 0; i < FAN_BIT_REG.Length; i++)
moel@34
   262
        bits = (bits << 8) | ReadByte(0, FAN_BIT_REG[i]);
moel@85
   263
      ulong newBits = bits;
moel@130
   264
      for (int i = 0; i < fans.Length; i++) {
moel@130
   265
        int count = ReadByte(FAN_TACHO_BANK[i], FAN_TACHO_REG[i]);
moel@85
   266
        
moel@85
   267
        // assemble fan divisor
moel@34
   268
        int divisorBits = (int)(
moel@130
   269
          (((bits >> FAN_DIV_BIT2[i]) & 1) << 2) |
moel@130
   270
          (((bits >> FAN_DIV_BIT1[i]) & 1) << 1) |
moel@130
   271
           ((bits >> FAN_DIV_BIT0[i]) & 1));
moel@34
   272
        int divisor = 1 << divisorBits;
moel@85
   273
       
moel@34
   274
        float value = (count < 0xff) ? 1.35e6f / (count * divisor) : 0;
moel@130
   275
        fans[i] = value;
moel@85
   276
moel@85
   277
        // update fan divisor
moel@85
   278
        if (count > 192 && divisorBits < 7) 
moel@85
   279
          divisorBits++;
moel@85
   280
        if (count < 96 && divisorBits > 0)
moel@85
   281
          divisorBits--;
moel@85
   282
moel@130
   283
        newBits = SetBit(newBits, FAN_DIV_BIT2[i], (divisorBits >> 2) & 1);
moel@130
   284
        newBits = SetBit(newBits, FAN_DIV_BIT1[i], (divisorBits >> 1) & 1);
moel@130
   285
        newBits = SetBit(newBits, FAN_DIV_BIT0[i], divisorBits & 1);
moel@85
   286
      }
moel@85
   287
     
moel@85
   288
      // write new fan divisors 
moel@85
   289
      for (int i = FAN_BIT_REG.Length - 1; i >= 0; i--) {
moel@85
   290
        byte oldByte = (byte)(bits & 0xFF);
moel@85
   291
        byte newByte = (byte)(newBits & 0xFF);
moel@85
   292
        bits = bits >> 8;
moel@85
   293
        newBits = newBits >> 8;
moel@85
   294
        if (oldByte != newByte) 
moel@85
   295
          WriteByte(0, FAN_BIT_REG[i], newByte);        
moel@85
   296
      }
moel@162
   297
moel@236
   298
      Ring0.ReleaseIsaBusMutex();
moel@34
   299
    }
moel@34
   300
moel@130
   301
    public string GetReport() {
moel@34
   302
      StringBuilder r = new StringBuilder();
moel@34
   303
moel@34
   304
      r.AppendLine("LPC " + this.GetType().Name);
moel@34
   305
      r.AppendLine();
moel@34
   306
      r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
moel@166
   307
      r.Append("Chip revision: 0x");
moel@166
   308
      r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
moel@166
   309
      r.Append("Base Adress: 0x");
moel@166
   310
      r.AppendLine(address.ToString("X4", CultureInfo.InvariantCulture));
moel@34
   311
      r.AppendLine();
moel@162
   312
moel@236
   313
      if (!Ring0.WaitIsaBusMutex(100))
moel@162
   314
        return r.ToString();
moel@162
   315
moel@34
   316
      r.AppendLine("Hardware Monitor Registers");
moel@34
   317
      r.AppendLine();
moel@34
   318
      r.AppendLine("      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
moel@34
   319
      r.AppendLine();
moel@56
   320
      for (int i = 0; i <= 0x7; i++) {
moel@166
   321
        r.Append(" ");
moel@166
   322
        r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture)); 
moel@166
   323
        r.Append("  ");
moel@34
   324
        for (int j = 0; j <= 0xF; j++) {
moel@34
   325
          r.Append(" ");
moel@166
   326
          r.Append(ReadByte(0, (byte)((i << 4) | j)).ToString(
moel@166
   327
            "X2", CultureInfo.InvariantCulture));
moel@34
   328
        }
moel@34
   329
        r.AppendLine();
moel@34
   330
      }
moel@53
   331
      for (int k = 1; k <= 15; k++) {
moel@34
   332
        r.AppendLine("Bank " + k);
moel@34
   333
        for (int i = 0x5; i < 0x6; i++) {
moel@166
   334
          r.Append(" "); 
moel@166
   335
          r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture)); 
moel@166
   336
          r.Append("  ");
moel@34
   337
          for (int j = 0; j <= 0xF; j++) {
moel@34
   338
            r.Append(" ");
moel@166
   339
            r.Append(ReadByte((byte)(k), (byte)((i << 4) | j)).ToString(
moel@166
   340
              "X2", CultureInfo.InvariantCulture));
moel@34
   341
          }
moel@34
   342
          r.AppendLine();
moel@34
   343
        }
moel@34
   344
      }
moel@34
   345
      r.AppendLine();
moel@34
   346
moel@236
   347
      Ring0.ReleaseIsaBusMutex();
moel@162
   348
moel@34
   349
      return r.ToString();
moel@34
   350
    }
moel@130
   351
  } 
moel@34
   352
}