Hardware/LPC/W836XX.cs
author moel.mich
Sun, 08 Aug 2010 13:57:26 +0000
changeset 165 813d8bc3192f
parent 163 67be1c62f950
child 166 fa9dfbfc4145
permissions -rw-r--r--
Refactored the hardware monitoring code into a library (Issue 101).
     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.Collections.Generic;
    40 using System.Text;
    41 
    42 namespace OpenHardwareMonitor.Hardware.LPC {
    43   internal class W836XX : ISuperIO {
    44 
    45     private ushort address;
    46     private byte revision;
    47 
    48     private Chip chip;
    49 
    50     private float?[] voltages = new float?[0];
    51     private float?[] temperatures = new float?[0];    
    52     private float?[] fans = new float?[0];
    53 
    54     private bool[] peciTemperature = new bool[0];
    55     private byte[] voltageRegister = new byte[0];
    56     private byte[] voltageBank = new byte[0];
    57     private 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 byte[] TEMPERATURE_REG = new byte[] { 0x50, 0x50, 0x27 };
    74     private byte[] TEMPERATURE_BANK = new byte[] { 1, 2, 0 };
    75 
    76     private byte[] FAN_TACHO_REG = new byte[] { 0x28, 0x29, 0x2A, 0x3F, 0x53 };
    77     private byte[] FAN_TACHO_BANK = new byte[] { 0, 0, 0, 0, 5 };       
    78     private byte[] FAN_BIT_REG = new byte[] { 0x47, 0x4B, 0x4C, 0x59, 0x5D };
    79     private byte[] FAN_DIV_BIT0 = new byte[] { 36, 38, 30, 8, 10 };
    80     private byte[] FAN_DIV_BIT1 = new byte[] { 37, 39, 31, 9, 11 };
    81     private byte[] FAN_DIV_BIT2 = new byte[] { 5, 6, 7, 23, 15 };
    82 
    83     private byte ReadByte(byte bank, byte register) {
    84       WinRing0.WriteIoPortByte(
    85          (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
    86       WinRing0.WriteIoPortByte(
    87          (ushort)(address + DATA_REGISTER_OFFSET), bank);
    88       WinRing0.WriteIoPortByte(
    89          (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
    90       return WinRing0.ReadIoPortByte(
    91         (ushort)(address + DATA_REGISTER_OFFSET));
    92     } 
    93 
    94     private void WriteByte(byte bank, byte register, byte value) {
    95       WinRing0.WriteIoPortByte(
    96          (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
    97       WinRing0.WriteIoPortByte(
    98          (ushort)(address + DATA_REGISTER_OFFSET), bank);
    99       WinRing0.WriteIoPortByte(
   100          (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
   101       WinRing0.WriteIoPortByte(
   102          (ushort)(address + DATA_REGISTER_OFFSET), value); 
   103     }
   104    
   105     public W836XX(Chip chip, byte revision, ushort address) {
   106       this.address = address;
   107       this.revision = revision;
   108       this.chip = chip;
   109 
   110       if (!IsWinbondVendor())
   111         return;
   112       
   113       temperatures = new float?[3];
   114       peciTemperature = new bool[3];
   115       switch (chip) {
   116         case Chip.W83667HG:
   117         case Chip.W83667HGB:
   118           // note temperature sensor registers that read PECI
   119           byte flag = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
   120           peciTemperature[0] = (flag & 0x04) != 0;
   121           peciTemperature[1] = (flag & 0x40) != 0;
   122           peciTemperature[2] = false;
   123           break;
   124         case Chip.W83627DHG:        
   125         case Chip.W83627DHGP:
   126           // note temperature sensor registers that read PECI
   127           byte sel = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
   128           peciTemperature[0] = (sel & 0x07) != 0;
   129           peciTemperature[1] = (sel & 0x70) != 0;
   130           peciTemperature[2] = false;
   131           break;
   132         default:
   133           // no PECI support
   134           peciTemperature[0] = false;
   135           peciTemperature[1] = false;
   136           peciTemperature[2] = false;
   137           break;
   138       }
   139 
   140       switch (chip) {
   141         case Chip.W83627EHF:
   142           voltages = new float?[10];
   143           voltageRegister = new byte[] { 
   144             0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51, 0x52 };
   145           voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5, 5 };
   146           voltageGain = 0.008f;
   147           fans = new float?[5];
   148           break;
   149         case Chip.W83627DHG:
   150         case Chip.W83627DHGP:        
   151         case Chip.W83667HG:
   152         case Chip.W83667HGB:
   153           voltages = new float?[9];
   154           voltageRegister = new byte[] { 
   155             0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51 };
   156           voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5 };
   157           voltageGain = 0.008f;
   158           fans = new float?[5];
   159           break;
   160         case Chip.W83627HF:
   161         case Chip.W83627THF:
   162         case Chip.W83687THF:
   163           voltages = new float?[7];
   164           voltageRegister = new byte[] { 
   165             0x20, 0x21, 0x22, 0x23, 0x24, 0x50, 0x51 };
   166           voltageBank = new byte[] { 0, 0, 0, 0, 0, 5, 5 };
   167           voltageGain = 0.016f;
   168           fans = new float?[3];         
   169           break;
   170       }
   171     }    
   172 
   173     private bool IsWinbondVendor() {
   174       ushort vendorId =
   175         (ushort)((ReadByte(HIGH_BYTE, VENDOR_ID_REGISTER) << 8) |
   176            ReadByte(0, VENDOR_ID_REGISTER));
   177       return vendorId == WINBOND_VENDOR_ID;
   178     }
   179 
   180     private ulong SetBit(ulong target, int bit, int value) {
   181       if ((value & 1) != value)
   182         throw new ArgumentException("Value must be one bit only.");
   183 
   184       if (bit < 0 || bit > 63)
   185         throw new ArgumentException("Bit out of range.");
   186 
   187       ulong mask = (((ulong)1) << bit);
   188       return value > 0 ? target | mask : target & ~mask;
   189     }
   190 
   191     public Chip Chip { get { return chip; } }
   192     public float?[] Voltages { get { return voltages; } }
   193     public float?[] Temperatures { get { return temperatures; } }
   194     public float?[] Fans { get { return fans; } }
   195 
   196     public void Update() {
   197       if (!WinRing0.WaitIsaBusMutex(10))
   198         return;
   199 
   200       for (int i = 0; i < voltages.Length; i++) {
   201         if (voltageRegister[i] != VOLTAGE_VBAT_REG) {
   202           // two special VCore measurement modes for W83627THF
   203           float fvalue;
   204           if ((chip == Chip.W83627HF || chip == Chip.W83627THF || 
   205             chip == Chip.W83687THF) && i == 0) 
   206           {
   207             byte vrmConfiguration = ReadByte(0, 0x18);
   208             int value = ReadByte(voltageBank[i], voltageRegister[i]);
   209             if ((vrmConfiguration & 0x01) == 0)
   210               fvalue = 0.016f * value; // VRM8 formula
   211             else
   212               fvalue = 0.00488f * value + 0.69f; // VRM9 formula
   213           } else {
   214             int value = ReadByte(voltageBank[i], voltageRegister[i]);
   215             fvalue = voltageGain * value;
   216           }
   217           if (fvalue > 0)
   218             voltages[i] = fvalue;
   219           else
   220             voltages[i] = null;
   221         } else {
   222           // Battery voltage
   223           bool valid = (ReadByte(0, 0x5D) & 0x01) > 0;
   224           if (valid) {
   225             voltages[i] = voltageGain * ReadByte(5, VOLTAGE_VBAT_REG);
   226           } else {
   227             voltages[i] = null;
   228           }
   229         }
   230       }
   231 
   232       for (int i = 0; i < temperatures.Length; i++) {
   233         int value = ((sbyte)ReadByte(TEMPERATURE_BANK[i], 
   234           TEMPERATURE_REG[i])) << 1;
   235         if (TEMPERATURE_BANK[i] > 0) 
   236           value |= ReadByte(TEMPERATURE_BANK[i],
   237             (byte)(TEMPERATURE_REG[i] + 1)) >> 7;
   238 
   239         float temperature = value / 2.0f;
   240         if (temperature <= 125 && temperature >= -55 && !peciTemperature[i]) {
   241           temperatures[i] = temperature;
   242         } else {
   243           temperatures[i] = null;
   244         }
   245       }
   246 
   247       ulong bits = 0;
   248       for (int i = 0; i < FAN_BIT_REG.Length; i++)
   249         bits = (bits << 8) | ReadByte(0, FAN_BIT_REG[i]);
   250       ulong newBits = bits;
   251       for (int i = 0; i < fans.Length; i++) {
   252         int count = ReadByte(FAN_TACHO_BANK[i], FAN_TACHO_REG[i]);
   253         
   254         // assemble fan divisor
   255         int divisorBits = (int)(
   256           (((bits >> FAN_DIV_BIT2[i]) & 1) << 2) |
   257           (((bits >> FAN_DIV_BIT1[i]) & 1) << 1) |
   258            ((bits >> FAN_DIV_BIT0[i]) & 1));
   259         int divisor = 1 << divisorBits;
   260        
   261         float value = (count < 0xff) ? 1.35e6f / (count * divisor) : 0;
   262         fans[i] = value;
   263 
   264         // update fan divisor
   265         if (count > 192 && divisorBits < 7) 
   266           divisorBits++;
   267         if (count < 96 && divisorBits > 0)
   268           divisorBits--;
   269 
   270         newBits = SetBit(newBits, FAN_DIV_BIT2[i], (divisorBits >> 2) & 1);
   271         newBits = SetBit(newBits, FAN_DIV_BIT1[i], (divisorBits >> 1) & 1);
   272         newBits = SetBit(newBits, FAN_DIV_BIT0[i], divisorBits & 1);
   273       }
   274      
   275       // write new fan divisors 
   276       for (int i = FAN_BIT_REG.Length - 1; i >= 0; i--) {
   277         byte oldByte = (byte)(bits & 0xFF);
   278         byte newByte = (byte)(newBits & 0xFF);
   279         bits = bits >> 8;
   280         newBits = newBits >> 8;
   281         if (oldByte != newByte) 
   282           WriteByte(0, FAN_BIT_REG[i], newByte);        
   283       }
   284 
   285       WinRing0.ReleaseIsaBusMutex();
   286     }
   287 
   288     public string GetReport() {
   289       StringBuilder r = new StringBuilder();
   290 
   291       r.AppendLine("LPC " + this.GetType().Name);
   292       r.AppendLine();
   293       r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
   294       r.Append("Chip revision: 0x"); r.AppendLine(revision.ToString("X"));
   295       r.Append("Base Adress: 0x"); r.AppendLine(address.ToString("X4"));
   296       r.AppendLine();
   297 
   298       if (!WinRing0.WaitIsaBusMutex(100))
   299         return r.ToString();
   300 
   301       r.AppendLine("Hardware Monitor Registers");
   302       r.AppendLine();
   303       r.AppendLine("      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   304       r.AppendLine();
   305       for (int i = 0; i <= 0x7; i++) {
   306         r.Append(" "); r.Append((i << 4).ToString("X2")); r.Append("  ");
   307         for (int j = 0; j <= 0xF; j++) {
   308           r.Append(" ");
   309           r.Append(ReadByte(0, (byte)((i << 4) | j)).ToString("X2"));
   310         }
   311         r.AppendLine();
   312       }
   313       for (int k = 1; k <= 15; k++) {
   314         r.AppendLine("Bank " + k);
   315         for (int i = 0x5; i < 0x6; i++) {
   316           r.Append(" "); r.Append((i << 4).ToString("X2")); r.Append("  ");
   317           for (int j = 0; j <= 0xF; j++) {
   318             r.Append(" ");
   319             r.Append(ReadByte((byte)(k),
   320               (byte)((i << 4) | j)).ToString("X2"));
   321           }
   322           r.AppendLine();
   323         }
   324       }
   325       r.AppendLine();
   326 
   327       WinRing0.ReleaseIsaBusMutex();
   328 
   329       return r.ToString();
   330     }
   331   } 
   332 }