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