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