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