Hardware/LPC/W836XX.cs
author moel.mich
Sun, 06 Jun 2010 14:44:53 +0000
changeset 135 51a15cf90d22
parent 122 3ef997c53b50
child 152 98187e7a08b2
permissions -rw-r--r--
Added experimental lm-sensors super I/O support for Linux.
     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   public 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           // do not add 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 
   198       for (int i = 0; i < voltages.Length; i++) {
   199         if (voltageRegister[i] != VOLTAGE_VBAT_REG) {
   200           // two special VCore measurement modes for W83627THF
   201           float fvalue;
   202           if ((chip == Chip.W83627HF || chip == Chip.W83627THF || 
   203             chip == Chip.W83687THF) && i == 0) 
   204           {
   205             byte vrmConfiguration = ReadByte(0, 0x18);
   206             int value = ReadByte(voltageBank[i], voltageRegister[i]);
   207             if ((vrmConfiguration & 0x01) == 0)
   208               fvalue = 0.016f * value; // VRM8 formula
   209             else
   210               fvalue = 0.00488f * value + 0.69f; // VRM9 formula
   211           } else {
   212             int value = ReadByte(voltageBank[i], voltageRegister[i]);
   213             fvalue = voltageGain * value;
   214           }
   215           if (fvalue > 0)
   216             voltages[i] = fvalue;
   217           else
   218             voltages[i] = null;
   219         } else {
   220           // Battery voltage
   221           bool valid = (ReadByte(0, 0x5D) & 0x01) > 0;
   222           if (valid) {
   223             voltages[i] = voltageGain * ReadByte(5, VOLTAGE_VBAT_REG);
   224           } else {
   225             voltages[i] = null;
   226           }
   227         }
   228       }
   229 
   230       for (int i = 0; i < temperatures.Length; i++) {
   231         int value = ((sbyte)ReadByte(TEMPERATURE_BANK[i], 
   232           TEMPERATURE_REG[i])) << 1;
   233         if (TEMPERATURE_BANK[i] > 0) 
   234           value |= ReadByte(TEMPERATURE_BANK[i],
   235             (byte)(TEMPERATURE_REG[i] + 1)) >> 7;
   236 
   237         float temperature = value / 2.0f;
   238         if (temperature <= 125 && temperature >= -55 && !peciTemperature[i]) {
   239           temperatures[i] = temperature;
   240         } else {
   241           temperatures[i] = null;
   242         }
   243       }
   244 
   245       ulong bits = 0;
   246       for (int i = 0; i < FAN_BIT_REG.Length; i++)
   247         bits = (bits << 8) | ReadByte(0, FAN_BIT_REG[i]);
   248       ulong newBits = bits;
   249       for (int i = 0; i < fans.Length; i++) {
   250         int count = ReadByte(FAN_TACHO_BANK[i], FAN_TACHO_REG[i]);
   251         
   252         // assemble fan divisor
   253         int divisorBits = (int)(
   254           (((bits >> FAN_DIV_BIT2[i]) & 1) << 2) |
   255           (((bits >> FAN_DIV_BIT1[i]) & 1) << 1) |
   256            ((bits >> FAN_DIV_BIT0[i]) & 1));
   257         int divisor = 1 << divisorBits;
   258        
   259         float value = (count < 0xff) ? 1.35e6f / (count * divisor) : 0;
   260         fans[i] = value;
   261 
   262         // update fan divisor
   263         if (count > 192 && divisorBits < 7) 
   264           divisorBits++;
   265         if (count < 96 && divisorBits > 0)
   266           divisorBits--;
   267 
   268         newBits = SetBit(newBits, FAN_DIV_BIT2[i], (divisorBits >> 2) & 1);
   269         newBits = SetBit(newBits, FAN_DIV_BIT1[i], (divisorBits >> 1) & 1);
   270         newBits = SetBit(newBits, FAN_DIV_BIT0[i], divisorBits & 1);
   271       }
   272      
   273       // write new fan divisors 
   274       for (int i = FAN_BIT_REG.Length - 1; i >= 0; i--) {
   275         byte oldByte = (byte)(bits & 0xFF);
   276         byte newByte = (byte)(newBits & 0xFF);
   277         bits = bits >> 8;
   278         newBits = newBits >> 8;
   279         if (oldByte != newByte) 
   280           WriteByte(0, FAN_BIT_REG[i], newByte);        
   281       }
   282     }
   283 
   284     public string GetReport() {
   285       StringBuilder r = new StringBuilder();
   286 
   287       r.AppendLine("LPC " + this.GetType().Name);
   288       r.AppendLine();
   289       r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
   290       r.Append("Chip revision: 0x"); r.AppendLine(revision.ToString("X"));
   291       r.Append("Base Adress: 0x"); r.AppendLine(address.ToString("X4"));
   292       r.AppendLine();
   293       r.AppendLine("Hardware Monitor Registers");
   294       r.AppendLine();
   295       r.AppendLine("      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   296       r.AppendLine();
   297       for (int i = 0; i <= 0x7; i++) {
   298         r.Append(" "); r.Append((i << 4).ToString("X2")); r.Append("  ");
   299         for (int j = 0; j <= 0xF; j++) {
   300           r.Append(" ");
   301           r.Append(ReadByte(0, (byte)((i << 4) | j)).ToString("X2"));
   302         }
   303         r.AppendLine();
   304       }
   305       for (int k = 1; k <= 15; k++) {
   306         r.AppendLine("Bank " + k);
   307         for (int i = 0x5; i < 0x6; i++) {
   308           r.Append(" "); r.Append((i << 4).ToString("X2")); r.Append("  ");
   309           for (int j = 0; j <= 0xF; j++) {
   310             r.Append(" ");
   311             r.Append(ReadByte((byte)(k),
   312               (byte)((i << 4) | j)).ToString("X2"));
   313           }
   314           r.AppendLine();
   315         }
   316       }
   317       r.AppendLine();
   318 
   319       return r.ToString();
   320     }
   321   } 
   322 }